Skip to content

Commit a0370d0

Browse files
author
guido
committed
Some patches to Lee Busby's fpectl mods that accidentally didn't make it
into 1.5a4. git-svn-id: http://svn.python.org/projects/python/trunk@8985 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent abb3998 commit a0370d0

3 files changed

Lines changed: 40 additions & 30 deletions

File tree

Include/pyfpe.h

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,22 @@ extern "C" {
6666
* 1) Add the *_PROTECT macros to your C code as required to protect
6767
* dangerous floating point sections.
6868
*
69-
* 2) Turn on the inclusion of the code by #defining WANT_SIGFPE_HANDLER in
70-
* config.h.in before you configure, compile, and install Python, and the
71-
* fpectl module, and any other modules which may have conditional code.
69+
* 2) Turn on the inclusion of the code by adding the ``--with-fpectl''
70+
* flag at the time you run configure. If the fpectl or other modules
71+
* which use the *_PROTECT macros are to be dynamically loaded, be
72+
* sure they are compiled with WANT_SIGFPE_HANDLER defined.
7273
*
7374
* 3) When python is built and running, import fpectl, and execute
7475
* fpectl.turnon_sigfpe(). This sets up the signal handler and enables
7576
* generation of SIGFPE whenever an exception occurs. From this point
7677
* on, any properly trapped SIGFPE should result in the Python
7778
* FloatingPointError exception.
7879
*
79-
* Step 1 has been done already for the Python kernel code, and will be
80-
* done soon for Hugunin's NumPy array package and my Gist graphics module.
81-
* Step 2 is usually done once at python install time. Python's behavior
82-
* with respect to SIGFPE is not changed unless you also do step 3. Thus
83-
* you can control this new facility at compile time, or run time, or both.
80+
* Step 1 has been done already for the Python kernel code, and should be
81+
* done soon for the NumPy array package. Step 2 is usually done once at
82+
* python install time. Python's behavior with respect to SIGFPE is not
83+
* changed unless you also do step 3. Thus you can control this new
84+
* facility at compile time, or run time, or both.
8485
*
8586
********************************
8687
* Using the macros in your code:
@@ -89,17 +90,16 @@ extern "C" {
8990
* {
9091
* ....
9192
* PyFPE_START_PROTECT("Error in foobar", return 0)
92-
* dangerous_op(somearg1, somearg2, ...);
93-
* PyFPE_END_PROTECT
93+
* result = dangerous_op(somearg1, somearg2, ...);
94+
* PyFPE_END_PROTECT(result)
9495
* ....
9596
* }
9697
*
97-
* If a floating point error occurs in dangerous_op, foobar returns 0
98-
* (NULL), after setting the associated value of the FloatingPointError
99-
* exception to "Error in foobar". ``Dangerous_op'' can be a single
100-
* operation, or a block, or function calls, or any combination, so long as
101-
* no alternate return is possible before the PyFPE_END_PROTECT macro is
102-
* reached.
98+
* If a floating point error occurs in dangerous_op, foobar returns 0 (NULL),
99+
* after setting the associated value of the FloatingPointError exception to
100+
* "Error in foobar". ``Dangerous_op'' can be a single operation, or a block
101+
* of code, function calls, or any combination, so long as no alternate
102+
* return is possible before the PyFPE_END_PROTECT macro is reached.
103103
*
104104
* The macros can only be used in a function context where an error return
105105
* can be recognized as signaling a Python exception. (Generally, most
@@ -121,7 +121,7 @@ extern "C" {
121121
* I therefore decided on a more limited form of nesting, using a counter
122122
* variable (PyFPE_counter) to keep track of any recursion. If an exception
123123
* occurs in an ``inner'' pair of macros, the return will apparently
124-
* come from the top level.
124+
* come from the outermost level.
125125
*
126126
*/
127127

@@ -131,23 +131,35 @@ extern "C" {
131131
#include <math.h>
132132
extern jmp_buf PyFPE_jbuf;
133133
extern int PyFPE_counter;
134-
extern double PyFPE_dummy();
134+
extern double PyFPE_dummy(void *);
135135

136136
#define PyFPE_START_PROTECT(err_string, leave_stmt) \
137137
if (!PyFPE_counter++ && setjmp(PyFPE_jbuf)) { \
138-
PyFPE_counter = 0; \
139138
PyErr_SetString(PyExc_FloatingPointError, err_string); \
139+
PyFPE_counter = 0; \
140140
leave_stmt; \
141141
}
142142

143143
/*
144144
* This (following) is a heck of a way to decrement a counter. However,
145-
* code optimizers will sometimes move this statement so that it gets
146-
* executed *before* the unsafe expression which we're trying to protect.
147-
* This pretty well messes things up, of course. So the best I've been able
148-
* to do is to put a (hopefully fast) function call into the expression
149-
* which counts down PyFPE_counter, and thereby monkey wrench the overeager
150-
* optimizer. Better solutions are welcomed....
145+
* unless the macro argument is provided, code optimizers will sometimes move
146+
* this statement so that it gets executed *before* the unsafe expression
147+
* which we're trying to protect. That pretty well messes things up,
148+
* of course.
149+
*
150+
* If the expression(s) you're trying to protect don't happen to return a
151+
* value, you will need to manufacture a dummy result just to preserve the
152+
* correct ordering of statements. Note that the macro passes the address
153+
* of its argument (so you need to give it something which is addressable).
154+
* If your expression returns multiple results, pass the last such result
155+
* to PyFPE_END_PROTECT.
156+
*
157+
* Note that PyFPE_dummy returns a double, which is cast to int.
158+
* This seeming insanity is to tickle the Floating Point Unit (FPU).
159+
* If an exception has occurred in a preceding floating point operation,
160+
* some architectures (notably Intel 80x86) will not deliver the interrupt
161+
* until the *next* floating point operation. This is painful if you've
162+
* already decremented PyFPE_counter.
151163
*/
152164
#define PyFPE_END_PROTECT(v) PyFPE_counter -= (int)PyFPE_dummy(&(v));
153165

Modules/fpectlmodule.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,9 +180,8 @@ static void fpe_reset(Sigfunc *handler)
180180

181181
/*-- Linux ----------------------------------------------------------------*/
182182
#elif defined(linux)
183-
/* Linux delivers SIGFPE by default,
184-
except for log(0), atanh(-1), 0.^0.
185-
*/
183+
#include <i386/fpu_control.h>
184+
__setfpucw(0x1372);
186185
signal(SIGFPE, handler);
187186

188187
/*-- NeXT -----------------------------------------------------------------*/

Python/pyfpe.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include "config.h"
22
#include "pyfpe.h"
3-
43
/*
54
* The signal handler for SIGFPE is actually declared in an external
65
* module fpectl, or as preferred by the user. These variable
@@ -12,5 +11,5 @@
1211
#ifdef WANT_SIGFPE_HANDLER
1312
jmp_buf PyFPE_jbuf;
1413
int PyFPE_counter = 0;
15-
double PyFPE_dummy(){return(1.0);}
14+
double PyFPE_dummy(void *dummy){ return 1.0; }
1615
#endif

0 commit comments

Comments
 (0)