[Cfp-interest] more on try/catch

Ian McIntosh ianm at ca.ibm.com
Thu May 29 20:12:18 PDT 2014


I thought I'd try approaching this from the opposite direction:  How would
you change C++ to handle floating-point exceptions?  Then could / how could
that be handled in C, and would that also work in C++?


So far C++ exceptions only come from throw statements, never from
floating-point exceptions (also never from traps, other hardware detected
events, or operating system detected events).  Let's assume that is changed
so they can come from floating-point exceptions.

In C++, a catch clause catches an exception object according to its type,
not its value, then is optionally told the value.
 - One way to use that is to define a "floating point exception" struct
type that contains something identifying which exception(s) happened in the
try block, and possibly information about the kind of operation and the
input value(s) triggering it, and catch would catch an object.
 - Another way is to have a separate type for each possible exception.
 - In C++ those could be merged using base and derived types.
For any of those, we should also define how to handle
implementation-defined exceptions; for example, PowerPC has half a dozen
specific kinds of invalid operation exceptions plus a general flag
combining them.

There are of course many variations but syntactically, so far it could look
like

	try {
		. . . block of code . . .
	}
	catch (FPException::Overflow fpexception)  { /* overflow handler
*/ . . . }
#ifdef PPC
	catch (FPException::PowerPC_SignalingNaN fpexception)  { /* SNaN
handler */ . . . }
	catch (FPException::PowerPC_InvalidConversion fpexception)  { /*
Invalid Conversion handler */ . . . }
	catch (FPException::PowerPC_SqrtOfNegative fpexception)  { /* Sqrt of
Negative handler */ . . . }
#endif
	catch (FPException::InvalidOperation fpexception)  { /* General
Invalid Operation handler */ . . . }
	catch (FPException::ZeroDivide fpexception)  { /* Divide Finite by
Zero handler */ . . . }
	catch (int m)  { /* integer handler */ }
	catch (...) { /* handler for anything else */ }

In each of the FPException catch clauses, the "fpexception" is like a
function parameter containing the exception information, is optional, and
can be any name you choose.

A catch clause can have a list of exception types if they all need the same
handling.

This is only part of the solution.  One issue is how the exceptions to
check are to be specified.

The rule could be that the floating-point exceptions to be checked for
would be those which have a catch clause naming them, but that's not true
for general C++ exceptions so would be an inconsistency.  A portable way to
specify which ones to enable would be useful; for example it could be a
function like
	FPEnable  oldcontrols = enableFPExceptions (FPEnable::Overflow |
FPEnable::InvalidOperation | FPEnable::ZeroDivide);
which sets up exception checking controls and returns the old exception
controls, along with another function to restore the saved controls
afterwards
	restoreFPExceptions (oldcontrols);
Since a catch handler doesn't have access to the try block's local
variables, those would have to be outside the try block.  They could be
useful elsewhere too.

In C++ an alternative would be that the control setting would construct an
object whose destructor would automatically do the restore (although that
wouldn't work for C which doesn't have constructors and destructors), eg,
	{
		FPEnableExceptions oldcontrols (FPEnable::Overflow |
FPEnable::InvalidOperation | FPEnable::ZeroDivide);
		try . . .
		// At the end of this block the destructor
FPEnableExceptions::~FPEnableExceptions (oldcontrols) would be run,
		// restoring the old controls.
	}

Assuming the first way (because it's more compatible with C), the
combination (with the int and ... deleted) is

	FPExceptionControls  oldcontrols = enableFPExceptions
(FPEnable::Overflow | FPEnable::InvalidOperation | FPEnable::ZeroDivide);
	try {
		. . . block of code . . .
	}
	catch (FPException::Overflow fpexception)  { /* overflow handler
*/ . . . }
#ifdef PPC
	catch (FPException::PowerPC_SignalingNaN fpexception)  { /* SNaN
handler */ . . . }
	catch (FPException::PowerPC_InvalidConversion fpexception)  { /*
Invalid Conversion handler */ . . . }
	catch (FPException::PowerPC_SqrtOfNegative fpexception)  { /* Sqrt of
Negative handler */ . . . }
#endif
	catch (FPException::InvalidOperation fpexception)  { /* General
Invalid Operation handler */ . . . }
	catch (FPException::ZeroDivide fpexception)  { /* Divide Finite by
Zero handler */ . . . }
	restoreFPExceptions (oldcontrols);


What about C?  C doesn't have try, throw or catch, and doesn't have classes
so the "::" syntax wouldn't work, but possibly could have something like:

	fpexceptioncontrols  oldcontrols = enablefpexceptions
(fpexception_overflow | fpexception_invalidoperation |
fpexception_zerodivide);
	try {
		. . . block of code . . .
	}
	catch (fpexception_overflow  fpexception)  { /* overflow handler
*/ . . . }
#ifdef PPC
	catch (fpexception_powerpc_signalingnan  fpexception)  { /* SNaN
handler */ . . . }
	catch (fpexception_powerpc_invalidconversion  fpexception)  { /*
Invalid Conversion handler */ . . . }
	catch (fpexception_powerpc_sqrtofnegative  fpexception)  { /* Sqrt of
Negative handler */ . . . }
#endif
	catch (fpexception_invalidoperation  fpexception)  { /* General
Invalid Operation handler */ . . . }
	catch (fpexception_zerodivide  fpexception)  { /* Divide Finite by
Zero handler */ . . . }
	restorefpexceptions (oldcontrols);

A simpler example to just handle overflow would be

	fpcontrols  oldcontrols = enablefpexceptions (fpexception_overflow);
	try {
		. . . block of code . . .
	}
	catch (fpexception_overflow  fpexception)  { /* overflow handler
*/ . . . }
	restorefpexceptions (oldcontrols);

It's more C than C++ style, but should work in C++ too.  It doesn't look
much different from the proposal below.

>From an implementation point of view, the same approaches and the same
performance issues apply as in my earlier email.  Optimizations like saving
and setting then later restoring exception controls can be moved out of
loops to reduce overhead.

This doesn't handle automatic value substitution, and like some of our
other approaches is a heavy approach where block of code is a simple
expression, but to me it seems fairly readable.

If C ever wanted to extend exception handling to allow throws, this is
compatible with the C++ syntax and semantics.  If C and/or C++ ever wanted
to extend exception handling to handle traps, other hardware detected
events, or operating system detected events, those could be extensions of
this.

- Ian McIntosh          IBM Canada Lab         Compiler Back End Support
and Development



|------------>
| From:      |
|------------>
  >-----------------------------------------------------------------------------------------------------------------------------------------|
  |David Hough CFP <pcfp at oakapple.net>                                                                                                      |
  >-----------------------------------------------------------------------------------------------------------------------------------------|
|------------>
| To:        |
|------------>
  >-----------------------------------------------------------------------------------------------------------------------------------------|
  |cfp-interest at ucbtest.org,                                                                                                                |
  >-----------------------------------------------------------------------------------------------------------------------------------------|
|------------>
| Date:      |
|------------>
  >-----------------------------------------------------------------------------------------------------------------------------------------|
  |2014-05-29 04:37 PM                                                                                                                      |
  >-----------------------------------------------------------------------------------------------------------------------------------------|
|------------>
| Subject:   |
|------------>
  >-----------------------------------------------------------------------------------------------------------------------------------------|
  |[Cfp-interest] more on try/catch                                                                                                         |
  >-----------------------------------------------------------------------------------------------------------------------------------------|
|------------>
| Sent by:   |
|------------>
  >-----------------------------------------------------------------------------------------------------------------------------------------|
  |cfp-interest-bounces at oakapple.net                                                                                                        |
  >-----------------------------------------------------------------------------------------------------------------------------------------|






Unlike normal C++ try blocks which can be compiled without reference to
whatever exceptions might be caught, floating-point try blocks have to
be compiled knowing what exceptions might be singled out for special
treatment.

So maybe fp try/catch
should just be separated syntactically as
well as semantically from C++:


fe_try {
}
fe_catch_sub( e, elist) {
}
fe_catch_exceps( e, elist) {
}
fe_catch_flags( f, flist) {
}

I've added a new wrinkle - fe_catch_sub which computes a substituted
value and continues execution.     How is the substituted value evaluated?
It's the value of a compound statement - but it's undefined (and compilers
ought to warn) if it uses any variables whose values change within the
try block.      Thus the try block to use with the substituted value
is probably pretty small.     The substituted value can be presubstituted
if there's system support for that, but in the much more likely event that
there isn't, then the compiler has to generate appropriate tests and
branches.
The elist usually better be a list of one subexception of invalid
rather than invalid itself;
otherwise the compiler has to generate tests wherever a signaling NaN might
cause an invalid exception.     Do we need a nonstandard pragma (meaning
OK to ignore) that says compiler need not worry about signaling NaNs in a
given scope?     Life would have been a lot simpler if signaling NaNs had
been given their own exception in 1977.

The substitute-exor feature of 754-2008 can be provided by the above plus
a standard function fe_exor_result(x,y,z) which returns abs(x) with the
exor signs of y and z.

_______________________________________________
Cfp-interest mailing list
Cfp-interest at oakapple.net
http://mailman.oakapple.net/mailman/listinfo/cfp-interest


-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mailman.oakapple.net/pipermail/cfp-interest/attachments/20140529/25f66402/attachment-0001.html 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: graycol.gif
Type: image/gif
Size: 105 bytes
Desc: not available
Url : http://mailman.oakapple.net/pipermail/cfp-interest/attachments/20140529/25f66402/attachment-0002.gif 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: ecblank.gif
Type: image/gif
Size: 45 bytes
Desc: not available
Url : http://mailman.oakapple.net/pipermail/cfp-interest/attachments/20140529/25f66402/attachment-0003.gif 


More information about the Cfp-interest mailing list