[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

D. Overflow Check Handling in GNAT

D.1 Background  
D.2 Overflow Checking Modes in GNAT  
D.3 Specifying the Desired Mode  
D.4 Default Settings  
D.5 Implementation Notes  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

D.1 Background

Overflow checks are checks that the compiler may make to ensure that intermediate results are not out of range. For example:

 
   A : Integer;
   ...
   A := A + 1;

if A has the value Integer'Last, then the addition may cause overflow since the result is out of range of the type Integer. In this case Constraint_Error will be raised if checks are enabled.

A trickier situation arises in examples like the following:

 
  A, C : Integer;
  ...
  A := (A + 1) + C;

where A is Integer'Last and C is -1. Now the final result of the expression on the right hand side is Integer'Last which is in range, but the question arises whether the intermediate addition of (A + 1) raises an overflow error.

The (perhaps surprising) answer is that the Ada language definition does not answer this question. Instead it leaves it up to the implementation to do one of two things if overflow checks are enabled.

If the compiler chooses the first approach, then the assignment of this example will indeed raise Constraint_Error if overflow checking is enabled, or result in erroneous execution if overflow checks are suppressed.

But if the compiler chooses the second approach, then it can perform both additions yielding the correct mathematical result, which is in range, so no exception will be raised, and the right result is obtained, regardless of whether overflow checks are suppressed.

Note that in the first example an exception will be raised in either case, since if the compiler gives the correct mathematical result for the addition, it will be out of range of the target type of the assignment, and thus fails the range check.

This lack of specified behavior in the handling of overflow for intermediate results is a source of non-portability, and can thus be problematic when programs are ported. Most typically this arises in a situation where the original compiler did not raise an exception, and then the application is moved to a compiler where the check is performed on the intermediate result and an unexpected exception is raised.

Furthermore, when using Ada 2012's preconditions and other assertion forms, another issue arises. Consider:

 
     procedure P (A, B : Integer) with
       Pre => A + B <= Integer'Last;

One often wants to regard arithmetic in a context like this from a mathematical point of view. So for example, if the two actual parameters for a call to P are both Integer'Last, then the precondition should be regarded as False. If we are executing in a mode with run-time checks enabled for preconditions, then we would like this precondition to fail, rather than raising an exception because of the intermediate overflow.

However, the language definition leaves the specification of whether the above condition fails (raising Assert_Error) or causes an intermediate overflow (raising Constraint_Error) up to the implementation.

The situation is worse in a case such as the following:

 
     procedure Q (A, B, C : Integer) with
       Pre => A + B + C <= Integer'Last;

Consider the call

 
     Q (A => Integer'Last, B => 1, C => -1);

From a mathematical point of view the precondition is True, but at run time we may (but are not guaranteed to) get an exception raised because of the intermediate overflow (and we really would prefer this precondition to be considered True at run time).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

D.2 Overflow Checking Modes in GNAT

To deal with the portability issue, and with the problem of mathematical versus run-time interpretation of the expressions in assertions, GNAT provides comprehensive control over the handling of intermediate overflow. GNAT can operate in three modes, and furthemore, permits separate selection of operating modes for the expressions within assertions (here the term "assertions" is used in the technical sense, which includes preconditions and so forth) and for expressions appearing outside assertions.

The three modes are:

Note that these modes apply only to the evaluation of predefined arithmetic, membership, and comparison operators for signed integer aritmetic.

For fixed-point arithmetic, checks can be suppressed. But if checks are enabled then fixed-point values are always checked for overflow against the base type for intermediate expressions (that is such checks always operate in the equivalent of STRICT mode).

For floating-point, on nearly all architectures, Machine_Overflows is False, and IEEE infinities are generated, so overflow exceptions are never raised. If you want to avoid infinities, and check that final results of expressions are in range, then you can declare a constrained floating-point type, and range checks will be carried out in the normal manner (with infinite values always failing all range checks).


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

D.3 Specifying the Desired Mode

The desired mode of for handling intermediate overflow can be specified using either the Overflow_Mode pragma or an equivalent compiler switch. The pragma has the form

 
    pragma Overflow_Mode ([General =>] MODE [, [Assertions =>] MODE]);

where MODE is one of

The case is ignored, so MINIMIZED, Minimized and minimized all have the same effect.

If only the General parameter is present, then the given MODE applies to expressions both within and outside assertions. If both arguments are present, then General applies to expressions outside assertions, and Assertions applies to expressions within assertions. For example:

 
   pragma Overflow_Mode
     (General => Minimized, Assertions => Eliminated);

specifies that general expressions outside assertions be evaluated in "minimize intermediate overflows" mode, and expressions within assertions be evaluated in "eliminate intermediate overflows" mode. This is often a reasonable choice, avoiding excessive overhead outside assertions, but assuring a high degree of portability when importing code from another compiler, while incurring the extra overhead for assertion expressions to ensure that the behavior at run time matches the expected mathematical behavior.

The Overflow_Mode pragma has the same scoping and placement rules as pragma Suppress, so it can occur either as a configuration pragma, specifying a default for the whole program, or in a declarative scope, where it applies to the remaining declarations and statements in that scope.

Note that pragma Overflow_Mode does not affect whether overflow checks are enabled or suppressed. It only controls the method used to compute intermediate values. To control whether overflow checking is enabled or suppressed, use pragma Suppress or Unsuppress in the usual manner

Additionally, a compiler switch `-gnato?' or `-gnato??' can be used to control the checking mode default (which can be subsequently overridden using pragmas).

Here `?' is one of the digits `1' through `3':

As with the pragma, if only one digit appears then it applies to all cases; if two digits are given, then the first applies outside assertions, and the second within assertions. Thus the equivalent of the example pragma above would be `-gnato23'.

If no digits follow the `-gnato', then it is equivalent to `-gnato11', causing all intermediate operations to be computed using the base type (STRICT mode).

In addition to setting the mode used for computation of intermediate results, the -gnato switch also enables overflow checking (which is suppressed by default). It thus combines the effect of using a pragma Overflow_Mode and pragma Unsuppress.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

D.4 Default Settings

The default mode for overflow checks is

 
   General => Strict

which causes all computations both inside and outside assertions to use the base type. In addition overflow checks are suppressed.

This retains compatibility with previous versions of GNAT which suppressed overflow checks by default and always used the base type for computation of intermediate results.

The switch `-gnato' (with no digits following) is equivalent to

 
   General => Strict

which causes overflow checking of all intermediate overflows both inside and outside assertions against the base type. This provides compatibility with this switch as implemented in previous versions of GNAT.

The pragma Suppress (Overflow_Check) disables overflow checking, but it has no effect on the method used for computing intermediate results.

The pragma Unsuppress (Overflow_Check) enables overflow checking, but it has no effect on the method used for computing intermediate results.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

D.5 Implementation Notes

In practice on typical 64-bit machines, the MINIMIZED mode is reasonably efficient, and can be generally used. It also helps to ensure compatibility with code imported from some other compiler to GNAT.

Setting all intermediate overflows checking (CHECKED mode) makes sense if you want to make sure that your code is compatible with any other possible Ada implementation. This may be useful in ensuring portability for code that is to be exported to some other compiler than GNAT.

The Ada standard allows the reassociation of expressions at the same precedence level if no parentheses are present. For example, A+B+C parses as though it were (A+B)+C, but the compiler can reintepret this as A+(B+C), possibly introducing or eliminating an overflow exception. The GNAT compiler never takes advantage of this freedom, and the expression A+B+C will be evaluated as (A+B)+C. If you need the other order, you can write the parentheses explicitly A+(B+C) and GNAT will respect this order.

The use of ELIMINATED mode will cause the compiler to automatically include an appropriate arbitrary precision integer arithmetic package. The compiler will make calls to this package, though only in cases where it cannot be sure that Long_Long_Integer is sufficient to guard against intermediate overflows. This package does not use dynamic alllocation, but it does use the secondary stack, so an appropriate secondary stack package must be present (this is always true for standard full Ada, but may require specific steps for restricted run times such as ZFP).

Although ELIMINATED mode causes expressions to use arbitrary precision arithmetic, avoiding overflow, the final result must be in an appropriate range. This is true even if the final result is of type [Long_[Long_]]Integer'Base, which still has the same bounds as its associated constrained type at run-time.

Currently, the ELIMINATED mode is only available on target platforms for which Long_Long_Integer is 64-bits (nearly all GNAT platforms).


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by GNAT Mailserver on April, 17 2014 using texi2html