This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: As-if Infinitely Ranged Integer Model


Dave Korn wrote:

"  AIR Integers do not require Ada-style precise traps, which require that an
exception is raised every time there is an integer overflow.  In the AIR
integer model, it is acceptable to delay catching an incorrectly represented
value until an observation point is reached just before it either affects the
output or causes a critical undefined behavior  "

Important correction! Ada does not require precise traps, and it is
worth clearing up this significant misconception (which I find a bit surprising in this context), because in fact Ada has a well thought out
model with some of the characteristics that are proposed in this model.
Indeed that's why I find the misconception surprising. I would have
thought that anyone devising an approach of this kind would have had
a look at what Ada has done to solve exactly the same behavior (and
we have now had nearly three decades of experience in understanding
this approach and refining it in Ada).


Indeed an alternative approach to handling this problem in GCC would
be to adapt the Ada model for C and C++ which would not be too hard
to do I suspect. Then gcc could be improved to handle this model
better and more effectively with respect to optimization, and both
C/C++ and Ada would benefit.

Two important aspects of Ada behavior:

It is generally permissible to avoid intermediate overflow exceptions
if the right result is given, this is because the base type of types
like integer is NOT constrained by the range (In Ada, Integer and
Integer'Base are different types, Integer is constrained with a
range, and Integer'Base is not. All intermediate calculations are
done using Integer'Base (or other appropriate signed integer base type).
The rule is that either an overflow exception must be raised, or the
proper result must be given. A typical example is

X := Y * Z / M;

where with typical hardware you get an intermediate double length
result, and it is perfectly efficient to get the right result.

The second important point is that Constraint_Error exceptions
in Ada do NOT have to be raised in the manner described in the
canonical semantics. This is a very important point, and in fact
the Ada decision is more liberal than that proposed in this model.
Let's look at the Ada definition. First the main body of the RM
does indeed define canonical semantics that require the CE
exception to be raised at a very specific point. BUT, you also
have to read the section "Exceptions and Optimization", this is
a well known pargraph in the RM with the number 11.6 (this para
number has been carefully maintained in successive versions of
the Ada standard, since people are so-used to referring to
11.6!) First the general aim:

1     This clause gives permission to the implementation to perform certain
"optimizations" that do not necessarily preserve the canonical semantics.

And then we have the important paragraphs:


4 The following additional permissions are granted to the implementation:

5     An implementation need not always raise an exception when a
      language-defined check fails. Instead, the operation that failed the
      check can simply yield an undefined result. The exception need be raised
      by the implementation only if, in the absence of raising it, the value
      of this undefined result would have some effect on the external
      interactions of the program. In determining this, the implementation
      shall not presume that an undefined result has a value that belongs to
      its subtype, nor even to the base range of its type, if scalar. Having
      removed the raise of the exception, the canonical semantics will in
      general allow the implementation to omit the code for the check, and
      some or all of the operation itself.

6     If an exception is raised due to the failure of a language-defined
      check, then upon reaching the corresponding exception_handler (or the
      termination of the task, if none), the external interactions that have
      occurred need reflect only that the exception was raised somewhere
      within the execution of the sequence_of_statements with the handler (or
      the task_body), possibly earlier (or later if the interactions are
      independent of the result of the checked operation) than that defined by
      the canonical semantics, but not within the execution of some
      abort-deferred operation or independent subprogram that does not
      dynamically enclose the execution of the construct whose check failed.
      An independent subprogram is one that is defined outside the library
      unit containing the construct whose check failed, and has no Inline
      pragma applied to it. Any assignment that occurred outside of such
      abort-deferred operations or independent subprograms can be disrupted by
      the raising of the exception, causing the object or its parts to become
      abnormal, and certain subsequent uses of the object to be erroneous, as
      explained in 13.9.1.

Basically the idea is that if a CE happens, then you don't know where it occured, and any assignments in the region covered by the exception handler may have happened, not happened, or been disrupted yielding abnormal results.

That's pretty generally permissive, much more permissive than the
AIR sketch here. It may seem too permissive, but the general idea
is that if a Constraint_Error occurs, something is seriously wrong
and the only required action is to signal that something is wrong.

If you need more precise control, remember that in Ada you can always
specify as small a scope as you like for an exception handler, so you
can write

     begin
        X := Y + Z;
     exception
        when Constraint_Error =>
          -- addition overflowed
           ...
     end;

Or you can make the addition be its own separate little
subprogram (function in C speak) and the scope can be
narrowed down that way.

I suspect that in C, the use of separate functions would
be good enough, given this is not a language in which people
are used to writing programs that recover from integer
overflow.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]