3

I'm working on an application using both GLib and CUDA in C. It seems that there's a conflict when importing both glib.h and cuda_runtime.h for a .cu file.

7 months ago GLib made a change to avoid a conflict with pixman's macro. They added __ before and after the token noinline in gmacros.h: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2059

That should have worked, given that gcc claims:

You may optionally specify attribute names with __ preceding and following the name. This allows you to use them in header files without being concerned about a possible macro of the same name. For example, you may use the attribute name __noreturn__ instead of noreturn.

However, CUDA does use __ in its macros, and __noinline__ is one of them. They acknowledge the possible conflict, and add some compiler checks to ensure it won't conflict in regular c files, but it seems that in .cu files it still applies:

#if defined(__CUDACC__) || defined(__CUDA_ARCH__) || defined(__CUDA_LIBDEVICE__)
/* gcc allows users to define attributes with underscores,
   e.g., __attribute__((__noinline__)).
   Consider a non-CUDA source file (e.g. .cpp) that has the
   above attribute specification, and includes this header file. In that case,
   defining __noinline__ as below  would cause a gcc compilation error.
   Hence, only define __noinline__ when the code is being processed
   by a  CUDA compiler component.
*/
#define __noinline__ \
        __attribute__((noinline))

I'm pretty new to CUDA development, and this is clearly a possible issue that they and gcc are aware of, so am I just missing a compiler flag or something? Or is this a genuine conflict that GLib would be left to solve?

Environment: glib 2.70.2, cuda 10.2.89, gcc 9.4.0

Edit: I've raised a GLib issue here

It might not be GLib's fault, but given the difference of opinion in the answers so far, I'll leave it to the devs there to decide whether to raise it with NVidia or not.

I've used nemequ's workaround for now and it compiles without complaint.

Tim
  • 31
  • 3
  • 1
    The CUDA tool chain uses gcc to compile most of its code, and the NVIDIA integration is following the exact practice which gcc recommends for user side macro naming, which you cited in your question. And it has worked this way since the first beta release. The fact that the glib developers have integrated a conflict into their headers is unfortunate, but nothing that you should expect that can fixed from the CUDA side – talonmies Dec 10 '21 at 08:10
  • 1
    The GLib developers have not integrated a conflict into their headers. GLib is using the `__noinline__` attribute name correctly, expecting it to be provided by the compiler. CUDA is at fault here for defining a macro in the underscore-prefixed namespace, which is reserved for the compiler (see, for example, https://stackoverflow.com/a/35243083/2931197). – Philip Withnall Dec 10 '21 at 10:58
  • @Philip: You could argue that Nvidia's macro definition is part of the compiler. The GLib library definitely isn't part of the compiler and I would assume that gcc's comment about user attributes with double underscore may violate the C++ standard regarding reserved identifiers, or is at least only compatible with gcc and no other compilers. – Sebastian Dec 11 '21 at 07:57
  • Another question would be: How to use glib with Cuda device code nevertheless? – Sebastian Dec 11 '21 at 07:58

2 Answers2

2

GCC's documentation states:

You may optionally specify attribute names with __ preceding and following the name. This allows you to use them in header files without being concerned about a possible macro of the same name. For example, you may use the attribute name __noreturn__ instead of noreturn.

Now, that's only assuming you avoid double-underscored names the compiler and library use; and they may use such names. So, if you're using NVCC - NVIDIA could declare "we use noinline and you can't use it".

... and indeed, this is basically the case: The macro is protected as follows:

#if defined(__CUDACC__) || defined(__CUDA_ARCH__) || defined(__CUDA_LIBDEVICE__)
#define __noinline__  __attribute__((noinline))
#endif /* __CUDACC__  || __CUDA_ARCH__ || __CUDA_LIBDEVICE__ */
  • __CUDA_ARCH__ - only defined for device-side code, where NVCC is the compiler (ignoring clang CUDA support here).
  • __CUDA_LIBDEVICE__ - Don't know where this is used, but you're certainly not building it, so you don't care about that.
  • __CUDACC__ defined when NVCC is compiling the code.

So in regular host-side code, including this header will not conflict with Glib's definitions.

Bottom line: NVIDIA is (basically) doing the right thing here and it shouldn't be a real problem.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
2

GLib is clearly in the right here. They check for __GNUC__ (which is what compilers use to indicate compatibility with GNU C, AKA the GNU extensions to C and C++) prior to using __noinline__ exactly as the GNU documentation indicates it should be used: __attribute__((__noinline__)).

GNU C is clearly doing the right thing here, too. Compilers offering the GNU extensions (including GCC, clang, and many many others) are, well, compilers, so they are allowed to use the double-underscore prefixed identifiers. In fact, that's the whole idea behind them; it's a way for compilers to provide extensions without having to worry about conflicts to user code (which is not allowed to declare double-underscore prefixed identifiers).

At first glance, NVidia seems to be doing the right thing, too, but they're not. Assuming you consider them to be the compiler (which I think is correct), they are allowed to define double-underscore prefixed macros such as __noinline__. However, the problem is that NVidia also defines __GNUC__ (quite intentionally since they want to advertise support for GNU extensions), then proceeds to define __noinline__ in an incompatible way, breaking an API provided by GNU C.

Bottom line: NVidia is in the wrong here.

As for what to do about it, well that's a less interesting question but there are a few options. You could (and should) file an issue with NVidia to fix their compiler. In my experience they're pretty good about responding quickly but unlikely to get around to fixing the problem in a reasonable amount of time.

You could also send a patch to GLib to work around the problem by doing something like

#if defined(__CUDACC__)
__attribute__((noinline))
#elif defined(__GNUC__)
__attribute__((__noinline__))
#else
...
#endif

If you're in control of the code which includes glib, another option would be to do something like

#undef __noinline__
#include glib_or_file_which_includes_glib
#define __noinline__ __attribute__((noinline))

My advice would be to do all three, but especially the first one (file an issue with NVidia) and find a way to work around it in your code until NVidia fixes the problem.

nemequ
  • 16,623
  • 1
  • 43
  • 62