Question

When compiling the following with the strict overflow flag, it tells me, on the 2nd test that r may not be what I think it could be:

    int32_t r(my_rand());
    if(r < 0) {
        r = -r;
        if(r < 0) {   // <-- error on this line
            r = 0;
        }
    }

The error is:

/build/buildd/libqtcassandra-0.5.5/tests/cassandra_value.cpp:
     In function 'int main(int, char**)':
/build/buildd/libqtcassandra-0.5.5/tests/cassandra_value.cpp:2341:13:
     error: assuming signed overflow does not occur when simplifying
     conditional to constant [-Werror=strict-overflow]
         if(r < 0) {
         ^

What I do not understand is: why wouldn't the error be generated on the line before that? Because really the overflow happens when I do this, right?

    r = -r;
Was it helpful?

Solution

EDIT: I removed my first answer, because it was invalid. Here is completely new version. Thanks to @Neil Kirk for pointing out my errors.

Answer for the question is here: https://stackoverflow.com/a/18521660/2468549

GCC always assumes, that signed overflow does never occur, and, on that assumption, it (always) optimizes out the inner if (r < 0) block.

If you turn -Wstrict-overflow on, then compiler finds out, that after r = -r r < 0 may still be true (if r == -2^31 initially), which causes an error (error is caused by optimization based on assumption of overflow never occurring, not by overflow possibility itself - that's how -Wstrict-overflow works).

OTHER TIPS

A couple of additions in regard to having such overflows:

  1. You can turn off these warnings with a #pragma GCC ...

    If you are sure that your code will always work (wrapping of integers is fine in your function) then you can use a pragma around the offensive block or function:

     #pragma GCC diagnostic push
     #pragma GCC diagnostic ignored "-Wstrict-overflow"
     ...offensive code here...
     #pragma GCC diagnostic pop
    

    Of course, this means you are completely ignoring the errors and let the compiler do its optimizations. Better have a test to make 100% sure that edge cases work as expected!

  2. Use unsigned integers

    The documentation says:

    For C (and C++) this means that overflow when doing arithmetic with signed numbers is undefined, which means that the compiler may assume that it will not happen.

    In other words, if the math uses unsigned integers, this optimization doesn't apply. So if you need to do something such as r = -r, you can first copy r in an unsigned integer of the same size and then do the sign change:

     std::int32_t r = ...;
    
     std::uint32_t q = r;
     q = -q;
    
     if(static_cast<std::int32_t>(q) < 0) ...
    

    That should work as expected: the if() statement will not be optimized out.

  3. Turn off the actual optimization

    Note: This was seemingly working in older compilers, but that is not currently possible on a per function basis. You have to use that command line option on the command line and not as an attribute. I leave this here for now as it may work for you.

    I like this one better. I bumped in a test that would fail in Release mode today. Nothing that I use on a regular basis and only for a very specific edge case, but it worked just fine in Debug, therefore, not having the compiler optimize that function is a better option. You can actually do so as follow:

     T __attribute__((optimize("-fno-strict-overflow"))) func(...)
     {
         ...
     }
    

    That attribute cancels the -fstrict-overflow¹ error by actually emitted code as expected. My test now passes in Debug and Release.

Note: this is g++ specific. See your compiler documentation for equivalents, if available.

Either way, as mentioned by Frax, in -O3 mode, the compiler wants to optimize the test by removing it and the whole block of code. The whole block can be removed because if it were negative, after the r = -r;, then r is expected to be positive. So testing for a negative number again is optimized out and that's what the compiler is warning us about. However, with the -fstrict-overflow attribute, you instead ask the compiler to do that optimization in that one function. As a result you get the expected behavior in all cases, including overflows.

¹ I find that option name confusing. In this case, if you use -fstrict-overflow, you ask for the optimizer to do optimization even if strict overflows are not respected as a result.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top