On Sun, 8 Nov 2015, Jeff Law wrote:
On 11/08/2015 12:11 PM, Florian Weimer wrote:
On 11/06/2015 01:32 PM, David Brown wrote:
How about this case:
int foo(int x) {
if (x > 1290) {
printf("X is wrong here %d, but we don't care\n", x);
}
return x*x*x;
Why not simply:
unsigned y = x;
return y*y*y;
? This is an example where defined behavior is so easy to get...
One very useful trick against compiler optimizations is:
asm("":"+g"(x));
(should be "+X" but gcc is too buggy for that. Use "+gx" for floats on x86)
It tells gcc: "I might have modified the value of x here, so you can't
assume anything about that value", but no code is actually generated for
it.
}
The compiler can eliminate the check and the printf.
I don't think the compiler can do that because printf has an externally
visible effect, which is sequenced before the undefined behavior, so
this program transformation would not be permitted under the as-if rule.
I am not sure about this interpretation (not 100% about the opposite
either). If the compiler knew that printf always returns, it would know
that x > 1290 implies undefined behavior, and could thus assume that it
does not happen. As long as there is a chance that printf might not return
(throws an exception, calls longjmp or exit, loops infinitely, etc), the
compiler cannot make that assumption (it could assume that printf does not
return though, and do weird things if that assumption turns out to be
wrong).
Right. This is precisely the discussion we had when looking at this class of
issues in the erroneous-path optimizer. It doesn't currently try to handle
overflows, but if it did, it'd probably do something like first transforming
the code into:
if (x > 1290) {
printf ("...");
return x * x * x;
}
return x * x * x;
Note how the return statement has been duplicated into the THEN clause. That
allows us to transform the undefined behaviour into
if (x > 1290) {
printf ("...");
__builtin_trap ();
}
return x * x * x.
The OP was hoping to get an arbitrary value, not a trap, or he could have
added a call to abort() himself.
Note carefully that we don't use __builtin_unreachable, which has the
undesirable effect of doing absolutely nothing. Whereas __builtin_trap
immediately terminates the program, thus never allowing the undefined
behaviour to actually execute (and thus prevent any bad things from
happening from a security standpoint).
There are 2 irreconcilable viewpoints: security and speed. We already have
an option that turns __builtin_unreachable into an equivalent of
__builtin_trap. If you insist on using __builtin_trap directly in too many
places, we will have to add an option: -fprogram-returns-normally that
turns __builtin_trap (and std::terminate and a few others) into
__builtin_unreachable.
--
Marc Glisse