Paul Eggert <[EMAIL PROTECTED]> writes: > Ian Lance Taylor <[EMAIL PROTECTED]> writes: > > > Also, it does not make sense to me to lump together all potentially > > troublesome optimizations under a single name. > > As a compiler developer, you see the trees. But most users just see a > forest and want things to be simple. Even adding a single binary > switch (-fno-risky/-frisky) will be an extra level of complexity that > most users don't particularly want to know about. Requiring users to > worry about lots of little switches (at least -fwrapv/-fundefinedv/-ftrapv, > -fstrict-signed-overflow/-fno-strict-signed-overflow, and > -fstrict-aliasiang/-fno-strict-aliasing, and probably more) makes GCC > harder to use conveniently, and will make things more likely to go > wrong in practical use.
You're right. I should have said: it does not make sense to me to lump together all potentially troublesome optimizations under a single option. But it would be reasonable to introduce a single option which groups a collection of other options. I don't think -frisky is a good name for that option. A better name would be -fstrict. > > 1) Add an option like -Warnv to issue warnings about cases where gcc > > implements an optimization which relies on the fact that signed > > overflow is undefined. > > > > 2) Add an option like -fstrict-signed-overflow which controls those > > cases which appear to be risky. Turn on that option at -O2. > > This sounds like a good approach overall, but the devil is in the > details. As you mentioned, (1) might have too many false positives. > > More important, we don't yet have an easy way to characterize the > cases where (2) would apply. For (2), we need a simple, documented > rule that programmers can easily understand, so that they can easily > verify that C code is safe: for most applications this is more > important than squeezing out the last ounce of performance. Having > the rule merely be "does your version of GCC warn about it on your > platform?" doesn't really cut it. > > So far, the only simple rule that has been proposed is -fwrapv, which > many have said is too conservative as it inhibits too many useful > optimizations for some numeric applications. I'm not yet convinced > that -fwrapv harms performance that much for most real-world > applications, but if we can come up with a less-conservative (but > still simple) rule, that would be fine. My worry, though, is that the > less-conservative rule will be too complicated. I hope I'm not beating a dead horse, but I think it's important to understand that there is a big difference between -fwrapv and full-powered optimizations based on signed overflow being undefined. -fwrapv specifies precisely how signed overflow should behave, and thus requires the compiler to handle numbers in a fully specified manner. But the vast majority of C/C++ code never involves signed overflow, and thus does not require any special handling. For example, my earlier code involving optimizing ((X * 10) / 5) to (X / 2). This transformation is invalid under -fwrapv because it will mishandle certain values of X. But it is valid to make this transformation if the actual values of X are such that overflow never actually occurs. Therefore, we have these more-or-less easily characterized aspects of signed overflow handling: 1) Signed numbers always wrap on overflow using standard twos-complement arithmetic (-fwrapv, Java). 2) We assume that in loops using signed indexes, the index value never wraps around. That is, if the variable i is always incremented (decremented) in a loop, we assume that i <= INT_MAX (>= INT_MIN) is always true. 2a) We use that assumption only when considering the number of times the loop will be executed, or when considering whether the loop terminates, or when determining the final value the variable will hold. In particular we do not eliminate the loop termination test unless we can determine precisely how many times the loop will be executed. 2b) We apply that assumption to all uses of the variable i. 3) We perform algebraic simplifications based on the assumption that signed arithmetic in the program never overflows. We can safely translate ((X * 10) / 5) to (X / 2) because we assume that X * 10 will not overflow. If this arithmetic does overflow, we guarantee that you will always get some valid number, though we don't try to specify which number it will be. 4) We permit an exception to occur if there is a signed overflow. If we can prove that some expression causes signed overflow, we are permitted to assume that that case will never arise. 5) We require an exception to occur if there is a signed overflow (-ftrapv). Case 1 may not be used with any of the other cases. The same is true of case 5. The other cases may be used in any combination. So this suggests some options: -fwrapv-loop-iterations (controls 2a) -fwrapv-loop-indexes (controls 2b) -fwrapv-loop (controls 2a and 2b) -fwrapv-math (controls 3) Case 4 actually never arises at present, and I can't imagine any reason that it would be desirable. Ian