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

Reply via email to