NCEG IEEE Exceptions Proposal Outline
David Hough
sun!Eng!David.Hough
Thu Jun 27 21:56:20 PDT 1991
The following is a proposal for handling exceptions in NCEG intended for
IEEE implementations although partially generalizable to others. It's
intended to be expanded into a section of NCEG 91-015, the Floating-Point C
Extensions document that Jim Thomas is working on.
What's proposed is intended to be implementable now, although efficient
implementations await hardware support.
The chief goals are to obtain most of the benefits of continuable traps
and presubstitution without paying most of the price in high performance
implementations. I've indicated the hardware support required for an effi-
cient SPARC-like implementation.
No errno, no matherr
Just in case it needs repeating, NCEG IEEE libm functions do not set
errno and do not call matherr.
Additional Floating-Point Operators
The following floating-point operators need to be added, either by
extending definitions, revising definitions, or adding new operators in func-
tion syntax:
Function Possible Syntax
scale by power of two float expression << integer expression
- -
square root sqrt()
power pow()
I'm not married to the possible syntax above, although it seems natural to me.
I doubt that there is an important body of C code that would be invalidated if
sqrt() and pow() became operators. Additional operators could be added to
this list; the criteria are:
1) is it likely to be inlined frequently;
2) is it likely to generate exceptions that programmers would like to
inquire about with the on-exception syntax described below.
exp() and log() aren't likely to be inlined much as extensive new com-
pilers aren't likely to be forthcoming for 68881/2-based systems. In con-
trast, pow(x,2) will likely be inlined. fmod() or % aren't likely to be used
with on-exception syntax.
On-Exception Syntax and Hardware
The on-exception syntax allows the programmer to specify a branch condi-
tional on the exceptions generated by a specific operator:
p = p * [ FE OVERFLOW goto label1 ] [ FE UNDERFLOW goto label2] z[i]
- -
I'm not married to this syntax either. It means for the specific operation *
in this statement, if an overflow occurs go to label1, if an underflow occurs
go to label2. FE INEXACT, FE UNDERFLOW, FE OVERFLOW, FE DIVBYZERO, and
- - - -
FE INVALID are defined in NCEG 91-015.
-
The general syntax is that an operator is followed immediately by one or
more on-exception clauses specifying an IEEE exception and a label to branch
to if it arises:
operator [ integer constant expression goto label ]
- -
The integer constant expression has to evaluate at compile time to one of the
- -
five FE ... macro values above.
-
Note that in the case of compound operators op=, the on-exception applies
to the op rather than the =, since that's most likely to correspond to pro-
grammer intent. Note that programs intended to run both on systems with trad-
itional Fortran and with extended-precision expression evaluation need to be
coded defensively in this way:
p = [ FE OVERFLOW goto label1 ] p * [FE OVERFLOW goto label1] z[i]
- -
because the overflow could occur on the fmuld (SPARC) or on the fmoved
(68881). For this reason one would not write code in the form
p *= [ FE OVERFLOW goto label1 ] z[i]
-
if it were to run robustly in both environments.
The traditional way to encode this with accrued exception flags would
look something like
fesetexcept(OFF,FE OVERFLOW);
-
t = p * z[i] ;
fegetexcept(&e,FE OVERFLOW);
-
if (e == ON) goto label1;
p = t;
which might not appeal to everybody from an efficiency point of view. You
could define another function fegetcurrentexcept but the compiler would have
to know all about it. Might as well say exactly what you mean. The semantics
of on-exception clauses is that the operator may or may not complete and
assign a value to its "destination", whatever that is. In the foregoing exam-
ple, you wouldn't know whether p = t occurs before or after the "if (e ==
ON)".
Current SPARC implementations would be based upon storing the %fsr and
examining it in the integer unit after operations with on-exception clauses.
The future efficient SPARC-like hardware implementation requires communi-
cating the five current exception flags to the branch unit and five new condi-
-
tional branch instructions
fbnxc
fbufc
fbofc
fbdzc
fbnvc
that branch on bits in the "cexc" field of the %fsr. There would need to be
hardware interlocks between the last fpop and a following fb??c. Instruction
prefetching would assume the branch rarely is taken.
On-exception clauses are applicable to logical operators on floating-
point data < <= > >=.
Operators on integer operands could use FE OVERFLOW and FE DIVBYZERO in
- -
the same syntax to detect integer overflow and division by zero. I think that
would be handy but it's a minor point to me.
Expressions with these on-exception clauses can never be eliminated as
dead code. Compilers are allowed to ignore any on-exception clauses that they
determine will never be activated, such as on-exception clauses applied to =
on systems in which that doesn't cause any type coercion. Compilers should
generate errors about on-exception clauses that they can't implement for what-
ever reason.
What about systems that implement sqrt or division with a subroutine?
Expressions involving on-exceptions clauses with these operators will have to
call somewhat more elaborate run-time routines that return the current excep-
tions in a place where subsequent compiled code can test them.
IEEE Default, SIGFPE, and Alternate Exception Handling
Most IEEE systems allow exceptions to be handled either by default or by
causing a trap, which on Unix systems typically gets converted into a SIGFPE,
which can be caught by a user-written SIGFPE handler and sometimes continued
on some systems, if the user can figure out how to decode what happened and
where to put what he wanted to happen instead.
Presubstitution is Kahan's idea to avoid requiring continuability.
Before the the operation that might cause the bad exception, you load a spe-
cial register with a value to be substituted for the IEEE default value if
that exception occurs. So instead of a lot of mechanism to support infrequent
traps, there is a lot of mechanism to support infrequent presubstitutions - at
least one register for each type of exception, op codes for loading and stor-
ing them, etc. Once implemented, either mechanism doesn't slow down loops
that don't generate the bad exception, unlike the on-exception clauses above,
although on-exception tests would probably be cheaper than testing one or both
floating-point operands prior to the operation.
"Alternate" (or "Moderate" or "Avoidance" or "VAX-centric" - I'd be happy
to accept any better alternate name) exception handling is intended to provide
an efficient way to get the most common cases of presubstitution for under-
flow, overflow, and division by zero. It doesn't help much for invalid
although it's defined. Any exception that is in alternate mode behaves like
default non-trapping mode except that the defaults are different:
subnormal default results are replaced by zero results
infinite default results are replaced by largest-normalized results
nan default results are replaced by largest-normalized results
all alternate results have the same sign bit as the IEEE default results they
replace. Subnormal, infinite, and nan operands would be treated as IEEE
requires; only the results would change.
The current SPARC implementation would require intervention in the user-
mode signal handler library to add a layer to interpret trapped SIGFPE's in
alternate mode according to a software-emulated mode bit, and substitute the
alternate result in the fpop destination, then continue. Current SPARC imple-
mentations are supposed to be continuable after SIGFPE's.
In a future efficient SPARC-like hardware implementation, the number of
trap-enable bits increases from five to ten; each IEEE exception has two mode
bits encoding four possible mode states, one of which is currently undefined
although I am happy to consider worthwhile suggestions.
The NCEG 91-015 <fltenv.h> would have these additions:
FT DEFAULT implementation-defined macro corresponding to
-
IEEE default nonstop mode
FT SIGFPE implementation-defined macro corresponding to
-
IEEE trap mode
FT ALTERNATE implementation-defined macro corresponding to
-
alternate nonstop mode
typedef ft trap t
- -
implementation-defined type that can hold trap modes
ftgettrap( fp trap t *flagp, int except)
- -
ftsettrap(const fp trap t *flagp, int except)
- -
The latter two get and set the trap type for a particular exception.
Directed roundings
A similar kind of clause without the goto could specify a static rounding
mode
operator [ integer constant expression ]
- -
where the integer constant expression evaluates to one of FE UPWARD or
-
FE DOWNWARD. Efficient support requires a future SPARC-like enhancement to
-
include fpops with directed static rounding.
More information about the Numeric-interest
mailing list