The Joy of Signalling NaNs! (A compiler patch)
Fawzi Mohamed
fmohamed at mac.com
Fri Mar 20 05:21:44 PDT 2009
Great work Don!
Fawzi
On 2009-03-19 05:58:40 +0100, Don <nospam at nospam.com> said:
> It's great that D initializes floating-point variables to NaN, instead
> of whatever random garbage happened to be in RAM.
> But, if your calculation ends up with a NaN, you have to work out where
> it came from. Worse, the NaN might not necessarily
> be visible in your final results, but you results may nonetheless be wrong.
>
> The hardware has excellent support for debugging these problems -- all
> you need to do is activitating the floating-point 'invalid' trap,
> and you'll get a hardware exception whenever you _create_ a NaN. What
> about uninitialized variables?
> Signalling NaNs are designed for exactly this situation. The instant
> you access a signalling NaN, a hardware exception occurs,
> and you drop straight into your debugger (just like accessing a null pointer).
> But this could only work if the compiler initialized every
> uninitialised floating-point variable to a signalling NaN.
>
> Now that we have access to the backend (thanks Walter!), we can do
> exactly that. My patch(below) is enabled only when compiled with DMC.
> real.nan is unchanged, and won't cause exceptions if you use it, but
> real.init is now a signalling nan.
> This doesn't make any difference to anything, until you enable FP exceptions.
> And when you do, if no exceptions occur, you can use the code coverage
> feature to give very high confidence that you're not using any
> uninitialised floating-point variables.
>
> I propose that this should become part of DMD. It doesn't need to be in
> the spec, it's primarily for debugging.
>
> Don.
>
> ==============
> Example of usage:
> ==============
>
> void main()
> {
> double a, b, c;
> a*=7; // Exactly the same as it is now, a is nan.
>
> enableExceptions();
>
> c = 6; // ok, c is initialized now
> c *= 10;
> b *= 10; // BANG ! Straight into the debugger
> b *= 5;
>
> disableExceptions();
> }
>
> -------
>
> void enableExceptions() {
> version(D_InlineAsm_X86) {
> short cont;
> asm {
> fclex;
> fstcw cont;
> mov AX, cont;
> and AX, 0xFFFE; // enable invalid exception
> mov cont, AX;
> fldcw cont;
> }
> }
> }
>
> void disableExceptions() {
> version(D_InlineAsm_X86) {
> short cont;
> asm {
> fclex;
> fstcw cont;
> mov AX, cont;
> or AX, 0x1; // disable invalid exception
> mov cont, AX;
> fldcw cont;
> }
> }
> }
>
>
> =================================
> Patches to DMD to turn all unitialized floats into SNANs.
>
> Changes are in mytype.c and e2ir.c
> =================================
> mytype.c:
> =================================
> line 2150:
>
> Expression *TypeBasic::defaultInit(Loc loc)
> { integer_t value = 0;
> #if __DMC__
> // Note: could be up to 16 bytes long.
> unsigned short snan[8] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xBFFF, 0x7FFF, 0, 0, 0 };
> d_float80 fvalue = *(long double*)snan;
> #endif
>
> line 2177:
>
> case Tfloat80:
> #if __DMC__
> return new RealExp(loc, fvalue, this);
> #else
> return getProperty(loc, Id::nan);
> #endif
>
> line 2186:
>
> case Tcomplex80:
> #if __DMC__
> { // Can't use fvalue + I*fvalue (the im part becomes a quiet NaN).
> complex_t cvalue;
> ((real_t *)&cvalue)[0] = fvalue;
> ((real_t *)&cvalue)[1] = fvalue;
> return new ComplexExp(loc, cvalue, this);
> }
> #else
> return getProperty(loc, Id::nan);
> #endif
> =================================
> e2ir.c line 1182.
> =================================
>
> bool isSignallingNaN(real_t x)
> {
> #if __DMC__
> if (x>=0 || x<0) return false;
> return !((((unsigned short*)&x)[3])&0x4000);
> #else
> return false;
> #endif
> }
>
>
> elem *RealExp::toElem(IRState *irs)
> { union eve c;
> tym_t ty;
>
> //printf("RealExp::toElem(%p)\n", this);
> memset(&c, 0, sizeof(c));
> ty = type->toBasetype()->totym();
> switch (tybasic(ty))
> {
> case TYfloat:
> case TYifloat:
> c.Vfloat = value;
> if (isSignallingNaN(value) ) {
> ((unsigned int*)&c.Vfloat)[0] &= 0xFFBFFFFFL;
> }
> break;
>
> case TYdouble:
> case TYidouble:
> c.Vdouble = value; // this unfortunately converts SNAN to QNAN.
> if ( isSignallingNaN(value) ) {
> ((unsigned int*)&c.Vdouble)[1] &= 0xFFF7FFFFL;
> }
> break;
>
> case TYldouble:
> case TYildouble:
> c.Vldouble = value;
> break;
>
> default:
> print();
> type->print();
> type->toBasetype()->print();
> printf("ty = %d, tym = %x\n", type->ty, ty);
> assert(0);
> }
> return el_const(ty, &c);
> }
>
> elem *ComplexExp::toElem(IRState *irs)
> { union eve c;
> tym_t ty;
> real_t re;
> real_t im;
>
> re = creall(value);
> im = cimagl(value);
>
> memset(&c, 0, sizeof(c));
> ty = type->totym();
> switch (tybasic(ty))
> {
> case TYcfloat:
> c.Vcfloat.re = (float) re;
> c.Vcfloat.im = (float) im;
> if ( isSignallingNaN(re) && isSignallingNaN(im)) {
> ((unsigned int*)&c.Vcfloat.re)[0] &= 0xFFBFFFFFL;
> ((unsigned int*)&c.Vcfloat.im)[0] &= 0xFFBFFFFFL;
> }
> break;
>
> case TYcdouble:
> c.Vcdouble.re = (double) re;
> c.Vcdouble.im = (double) im;
> if ( isSignallingNaN(re) && isSignallingNaN(im)) {
> ((unsigned int*)&c.Vcdouble.re)[1] &= 0xFFF7FFFFL;
> ((unsigned int*)&c.Vcdouble.im)[1] &= 0xFFF7FFFFL;
> }
> break;
>
> case TYcldouble:
> c.Vcldouble.re = re;
> c.Vcldouble.im = im;
> break;
>
> default:
> assert(0);
> }
> return el_const(ty, &c);
> }
More information about the Digitalmars-d
mailing list