18

Consider this code:

void foo(int n) {
    assert(n>=0&&n<=3);

    for (int i=0; i<n; i++) {
        doSomething();
    }
}

Here, there is an assert: n is between [0;3]. Asserts generally used for checking programmer mistakes. But here, it could be used as a hint to the compiler that n is between [0;3], so it could optimize better. Maybe it can unroll the loop, and use a jump.

For GCC, we can help the compiler manually:

if (!(n>=0&&n<=3)) __builtin_unreachable();

Here, GCC was actually informed that n is between [0;3], so it could generate better code.

My question is: is it possible to create a (possibly compiler dependent) new_assert macro, which can tell hints to the compiler in release builds? This solution must be transparent, so it can be a full replacement for the assert macro. For example, in "new_assert(func());" func() must not be called in release builds, if it has side effects.

Or, if it is not possible, another useful new_assert could be, if the condition is not allowed to have side effects (it would cause compile-time error), so we can use if (!(cond)) __builtin_unreachable(); in release builds without the fear that cond has a side effect. I.e. is it possible to create a new_assert that is checked whether its condition has side effects?

This is a related question.

This is a very similar question, but this time I ask whether it is possible to create a full replacement for the assert macro (so we can avoid manually annotating code)

geza
  • 28,403
  • 6
  • 61
  • 135
  • 1
    *For example, in "new_assert(func());" func() must not be called in release builds, if it has side effects.* how can your new_assert evaluate the result of func without calling it ? – UmNyobe Sep 11 '17 at 15:44
  • 1
    Your question looks closely like [this one](https://stackoverflow.com/questions/44054078/how-to-guide-gcc-optimizations-based-on-assertions-without-runtime-cost), but unfortunately it wasn't solved. – Quentin Sep 11 '17 at 15:45
  • @UmNyobe: if `func()` has side effects, then there's no hint will be given the compiler, so in this case it is perfectly fine that `new_assert` is a real `nop`. – geza Sep 11 '17 at 15:47
  • 1
    it's impossible for the compiler to prove that a given expression has no size effect. It is at least as hard as being able to inline any function. – UmNyobe Sep 11 '17 at 15:52
  • @UmNyobe: in this case, it is perfectly fine that we consider the function as having a side effect. Here, not a perfect solution is needed. I think, at least 95% of asserts are simple expressions, so we can use them as hint to the compiler. As for the other 5%, it's fine to ignore them. – geza Sep 11 '17 at 15:58
  • @Quentin: yes, indeed, it is very similar, it's about the same subject. But the asked questions are a little bit different, so I'd keep this question open. – geza Sep 11 '17 at 16:00
  • 3
    it seems that clang's `__builtin_assume` does exactly what I want: "The boolean argument to this function is defined to be true. The optimizer may analyze the form of the expression provided as the argument and deduce from that information used to optimize the program. If the condition is violated during execution, the behavior is undefined. The argument itself is never evaluated, so any side effects of the expression will be discarded." – geza Sep 11 '17 at 17:16
  • 1
    MS and Intel (also Linux) compilers provide `__assume()` for that purpose [see this](https://learn.microsoft.com/en-us/cpp/intrinsics/assume). – noma Dec 06 '17 at 13:47
  • @noma: thanks! It seems that the only major compiler without this feature is GCC then. – geza Dec 06 '17 at 14:00
  • The library range-v3 has exactly such a macro called [`RANGES_EXPECT`](https://github.com/ericniebler/range-v3/blob/b4b08157059838d76c2bd7e434b04bc4856a9f16/include/range/v3/detail/config.hpp#L78) – sv90 Aug 19 '19 at 08:36
  • I tested around your ideas with asserts and others also with gcc and clang. What I see is: I get the best output if I use LTO combined with O3. So in fact it might be not needed to "hint" the compiler for something. Compilers are more clever as we expect? – Klaus Aug 19 '19 at 11:44

2 Answers2

1

I reckon that if you just add undefined behavior in the new_assert() macro, the compiler will decide that those preconditions are non-negotiable. and generate faster code.

However, if your code is buggy, and the assert fails, anything could happen.

rep_movsd
  • 6,675
  • 4
  • 30
  • 34
  • I was under the impression that the simple `assert()` should be enough for the compiler to prove that the condition is true after the `assert()`, as the implementation will call `abort()` otherwise, and the compiler knows that this function cannot return. The question remains, whether the compiler is actually clever enough to do this. – cmaster - reinstate monica Mar 16 '18 at 12:25
0

You can use the cross-platform macro assure' from' from GNU's Gnulib.

Marc
  • 4,327
  • 4
  • 30
  • 46