Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is there something like MSVC's __assume with gcc?

34 views
Skip to first unread message

Bonita Montero

unread,
Sep 18, 2019, 4:24:28 AM9/18/19
to
I'm currently using the following macro:

#pragma once
#include <cassert>
#if !defined(xassert)
#if !defined(NDEBUG)
#definde xassert(e) (assert(e))
#else
#if defined(_MSC_VER)
#define xassert(e) (__assume(e))
#else
#define xassert(e) ((void)0)
#endif
#endif
#endif

Is there something simular like MSVC's __assume for the gcc?
__assume works actually pretty well. F.e. when you have a
switch -statement for a range of values the bounds aren'T
checked if the _assume allows this.

David Brown

unread,
Sep 18, 2019, 6:04:42 AM9/18/19
to
I use this:

// Non-existent function called if it is known at compile-time that an
// assume will fail
extern void __attribute__((error("Assume failed"))) assumeFailed(void);

// The compiler can assume that "x" is true, and optimise or warn
// accordingly
// If the compiler can see that the assume will fail, it gives an error
#define assume(x) \
do { \
if (__builtin_constant_p(x)) { \
if (!(x)) { \
assumeFailed(); \
} \
} \
if (!(x)) __builtin_unreachable(); \
} while (0)

Basically, __builtin_unreachable() tells the compiler "you can't get
here". __builtin_constant_p(x) is true if the compiler can figure out
the value of "x" at compile time (it doesn't have to be a C constant),
in which case checking the value can be done for free.


Jorgen Grahn

unread,
Sep 18, 2019, 10:13:58 AM9/18/19
to
On Wed, 2019-09-18, Bonita Montero wrote:
...
> Is there something simular like MSVC's __assume for the gcc?
> __assume works actually pretty well.

Ok, but what does it actually /do/?

> F.e. when you have a switch -statement for a range of values the
> bounds aren't checked if the _assume allows this.

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Bonita Montero

unread,
Sep 21, 2019, 7:08:57 AM9/21/19
to
> // The compiler can assume that "x" is true, and optimise or warn
> // accordingly
> // If the compiler can see that the assume will fail, it gives an error
> #define assume(x) \
> do { \
> if (__builtin_constant_p(x)) { \
> if (!(x)) { \
> assumeFailed(); \
> } \
> } \
> if (!(x)) __builtin_unreachable(); \
> } while (0)

This tells the compiler to verify if an expression has certain
constraints if it is constant. But in this case the compiler
does know the constraints on x itself so there wouldn't be any
need to have an additional assume like in MSVC. But __assume
with MSVC tells the compiler certain constaints it can't figure
out itself.

David Brown

unread,
Sep 21, 2019, 1:05:55 PM9/21/19
to
Look more closely. (I already explained this.) If the expression has a
value known to the compiler, then the compiler should check if it is
false - in which case you have definitely got something wrong. You get
this, for example, if you write "assume(2 == 3);". The point is to
force a compile-time error in such cases, not just inform the compiler
and let it silently optimise the code.

Otherwise, it is the line "if (!(x)) __builtin_unreachable();" that is
critical. "__builtin_unreachable()" tells the compiler that the runtime
will never reach that point in the code. So the compiler knows that
!(x) cannot ever be true - i.e., (x) is always true.

Try it out, and you will see how it works.

Manfred

unread,
Sep 22, 2019, 11:20:22 AM9/22/19
to
On 9/18/2019 12:04 PM, David Brown wrote:
> I use this:
>
> // Non-existent function called if it is known at compile-time that an
> // assume will fail
> extern void __attribute__((error("Assume failed"))) assumeFailed(void);
>
> // The compiler can assume that "x" is true, and optimise or warn
> // accordingly
> // If the compiler can see that the assume will fail, it gives an error
> #define assume(x) \
> do { \
> if (__builtin_constant_p(x)) { \
> if (!(x)) { \
> assumeFailed(); \
> } \
> } \
> if (!(x)) __builtin_unreachable(); \
> } while (0)

I am not an expert in gcc builtins, so I may be wrong, however it looks
like there might be a small risk in that it suffers from the common
problem with macros, i.e. 'x' is evaluated a number of times.
The risk is small because obviously this kind of expression is not meant
to have side effects on the argument, but still..

David Brown

unread,
Sep 22, 2019, 2:32:57 PM9/22/19
to
On 22/09/2019 17:20, Manfred wrote:
> On 9/18/2019 12:04 PM, David Brown wrote:
>> I use this:
>>
>> // Non-existent function called if it is known at compile-time that an
>> // assume will fail
>> extern void __attribute__((error("Assume failed"))) assumeFailed(void);
>>
>> // The compiler can assume that "x" is true, and optimise or warn
>> // accordingly
>> // If the compiler can see that the assume will fail, it gives an error
>> #define assume(x) \
>>     do { \
>>         if (__builtin_constant_p(x)) { \
>>             if (!(x)) { \
>>                 assumeFailed(); \
>>             } \
>>         } \
>>         if (!(x)) __builtin_unreachable(); \
>>     } while (0)
>
> I am not an expert in gcc builtins, so I may be wrong, however it looks
> like there might be a small risk in that it suffers from the common
> problem with macros, i.e. 'x' is evaluated a number of times.

My understanding (through tests and a rough understanding of what the
builtin does, but not through documentation in the manual) is that
__builtin_constant_p() does not evaluate the expression. If it is true,
then it doesn't matter that "x" is evaluated several times - after all,
it is a compile-time constant. So "x" is only evaluated once in cases
where it may matter.

> The risk is small because obviously this kind of expression is not meant
> to have side effects on the argument, but still..
>

Agreed.

It is always important to consider whether there are multiple
evaluations in a macro, and whether it is relevant. Although it does
not matter in this case, in general it is a good idea to write something
like this:

#define assume(x) \
do { \
const auto xx = x; \
if (__builtin_constant_p(xx)) { \
if (!(xx)) { \
assumeFailed(); \
} \
} \
if (!(xx)) __builtin_unreachable(); \
} while (0)

For C, use "const __auto_type xx = x;" - it's a gcc extension, but so
are the builtins.
0 new messages