Identifying GCC in the preprocessor
David Brown
david.brown@hesbynett.no
Mon Nov 10 13:00:54 GMT 2025
Hi,
I would expect a function like this to be marked as "always_inline",
rather than "noinline" - you would want this check to be done with
minimum overhead. Marking a function as "noinline" might not prevent
compiler knowledge of things like nonnull (or other range or constant
propagation information) being passed around, especially in the face of
higher level optimisations like LTO. Perhaps I am overly paranoid, but
I do not like to rely on "making things an independent function" as any
kind of barrier to optimisation information.
As far as I can see, you have the "volatile" in the wrong place.
Writing "volatile const void * vp" means "vp" is a pointer to a volatile
const void - you are promising not to change anything via vp, but
something external may change what vp points to. If, on the other hand,
you write "const void * volatile vp", you are making "vp" itself
volatile. This means that even if the compiler "knows" that the
parameter "p" is null, by the time you are getting to the "if (vp ==
NULL)" check, something external may have changed "vp" and the check
cannot be optimised away. Thus you have something that is safe for the
purpose you have here, regardless of any optimisations a compiler may use.
My suggestion for your code would be:
#if 1 // Checking on godbolt.org
__attribute__((nonnull(1)))
#else // Real code
__attribute__((always_inline)) static inline
#endif
void sht_assert_nonnull(const void *p, const char *msg)
{
#ifdef __GNUC__
const void * vp = p;
__asm__ ("" : "+g" (vp));
#else
const void * volatile vp = p;
#endif
if (__builtin_expect(vp == NULL, 0))
sht_abort(msg);
}
Here, the first "nonnull(1)" attribute is useful for checking the code
on godbolt - putting it in simulates calling the function with a "known"
null pointer that you want to double-check. And when checking on
godbolt you will want to disable the inline qualifier, so that you can
see the code.
If the compiler is not gcc or gcc compatible, there is a fall-back to
the slow but sure use of a volatile. (I guess the "__builtin_expect"
call should be in a macro that is defined only for gcc compatible
compilers.)
With gcc, the inline assembly here says that "vp" needs to be put in a
general-purpose register before running the assembly, and that the
assembly may change it but will leave it in the same place. The
assembly itself is empty. This has the effect of telling gcc that
whatever it thinks it knows about "vp" is no longer valid, but it does
not need to put "vp" onto the stack or move it around as it would for a
"volatile" variable - it can stay in a register. I've used that
technique a few times for doing weird stuff, and found it to be the most
efficient way to tell the compiler to forget what it knows. If it is
not a safe technique here, then hopefully Jonathan will let use know!
David
On 08/11/2025 23:35, Ian Pilcher via Gcc-help wrote:
> On 11/7/25 4:44 AM, Arsen Arsenović wrote:
>> If I may ask, why do you need to check for GCC specifically? I suspect
>> there might be an XY-problem afoot.
> Honestly ... I probably don't. It just seemed like an obvious thing to
> do. (And how hard could it be, right?)
>
> FWIW, here's the reason for my original question (comments included to
> explain *why* I'm doing this, even though I know that may people reading
> this will believe that it's a bad idea to "fight" the compiler this
> way).
>
> /**
> * Check that a `nonnull` pointer really isn't `NULL`.
> *
> * The public API (`sht.h`) declares most pointer arguments to be
> non-`NULL`
> * (using the `nonnull` function attribute). This causes GCC to issue
> a warning
> * when it determines that one of these function arguments is `NULL`,
> which is a
> * very desirable behavior.
> *
> * However, it also has the effect of allowing GCC to assume that the
> value of
> * these arguments will **never** be `NULL`, even though that is not
> actually
> * enforced. Because of this assumption, explicit checks for `NULL`
> values will
> * usually be optimized away.
> *
> * This function endeavors to "force" the compiler to check that a
> pointer value
> * is not `NULL`, even when its been told that it can't be.
> *
> * @param p The pointer to be checked.
> * @param msg Error message if pointer is `NULL`.
> *
> * @see sht_abort()
> */
> #ifdef __clang__
> SHT_FNATTR(optnone, noinline)
> #else
> SHT_FNATTR(optimize(0), noinline)
> #endif
> static void sht_assert_nonnull(const void *p, const char *msg)
> {
> volatile const void *vp = p;
>
> if (vp == NULL)
> sht_abort(msg);
> }
>
> The function attributes obviously may not work on RRC (Riley's Rando
> Compiler), that defines __GNUC__ just because everyone else does. In
> the end, I decided that isn't my problem.
>
More information about the Gcc-help
mailing list