Can the use of `__builtin_expect` affect the semantics of the program?
GCC (and Clang as well), specify this __builtin_expect
for human-assisted branch prediction, as described here here . Informally, people explain their semantics as follows: "the compiler simply processes the specified branch unconditionally, and if the condition should be different than specified, an expensive rollback occurs."
But if I have a piece of code:
if (__builtin_expect(p != 0, 1)) // line 1
p->access_object(); // line 2
If I were to take the informal explanation above literally, the compiler could simply execute line 2 without waiting for the condition on line 1 to be evaluated, and therefore cause undefined behavior (null pointer reversal) if the pointer happens to be null.
My question is, if I use __builtin_expect
, do I still get a guarantee that my security checks are working? And if so, can I get any runtime advantage if I use __builtin_expect
defensive checks as above?
(Note: my goal of using __builtin_expect
it like this is to get the best performance for p
non-null cases, at the cost of slowing down (even in an order of magnitude) cases where p
null, even if the latter case appears quite frequently.)
source to share
No, builtin_expect will not affect the semantics of the free program.
In particular, the compiler should not emit code that will execute the body of a block if
if that code has side effects that cannot be undone. The code should be "as if" builtin_expect
not used, other than performance.
In your specific example:
if (__builtin_expect(p != 0, 1)) // line 1
p->access_object(); // line 2
p
cannot be dereferenced if it is zero. So what's the point builtin_expect
in this case? The most it can do is say that the compiler is " p
probably not null, so it access_object()
will probably be called." If the definition access_object()
is equal inline
, the compiler will probably try to inline it, whereas if you said " p
probably null", the compiler might decide that it is best not to inline the code for access_object()
the invocation at that site, as it is unlikely to be used ...
This actually leads to unintuitive usage builtin_expect
in practice: you can use to mean "this code is a slow way", no matter how "likely" it is. As a trivial example, a server program can do this:
if (__builtin_expect(is_allowed(user, request), 1))
process(request);
else
reject(request);
Even if we find that 50% of the requests are illegitimate and get rejected, we can still mark "happy path" as soon as possible, because we don't care about slowing down the bounce rate.
source to share