Bug 2746 – Make float.init signalling NaN by default
Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
x86
OS
Windows
Creation time
2009-03-19T07:01:00Z
Last change time
2015-06-09T01:21:08Z
Keywords
patch
Assigned to
bugzilla
Creator
clugdbug
Comments
Comment #0 by clugdbug — 2009-03-19T07:01:03Z
The patch below changes the init values for
float, double, real, ifloat, idouble, ireal, cfloat, cdouble, and creal
from a quiet NaN into a signalling NaN.
Thus, use of uninitialized variables can be detected simply by enabling the "invalid" floating-point exception.
------
It involves adding one short function (isSignallingNaN), and modifying 3 others.
(Note: compared to the version I posted on the newgroup, this uses a payload which is different to the machine NaN, so that uninitialised variables can be detected even if exceptions are disabled).
=================================
mtype.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] = { 0, 0, 0, 0xA000, 0x7FFF, 0 };
d_float80 fvalue = *(long double*)snan;
#endif
#if LOGDEFAULTINIT
printf("TypeBasic::defaultInit() '%s'\n", toChars());
#endif
switch (ty)
{
case Tchar:
value = 0xFF;
break;
case Twchar:
case Tdchar:
value = 0xFFFF;
break;
case Timaginary32:
case Timaginary64:
case Timaginary80:
case Tfloat32:
case Tfloat64:
case Tfloat80:
#if __DMC__
return new RealExp(loc, fvalue, this);
#else
return getProperty(loc, Id::nan);
#endif
case Tcomplex32:
case Tcomplex64:
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
case Tvoid:
error(loc, "void does not have a default initializer");
}
return new IntegerExp(loc, value, this);
}
=================================
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);
}
Comment #1 by bugzilla — 2009-03-20T01:29:25Z
I'll put it in, but there's a problem if the compiler does any constant folding on the values - they'll get converted to quiet NaNs.
Comment #2 by clugdbug — 2009-03-20T09:14:17Z
(In reply to comment #1)
> I'll put it in, but there's a problem if the compiler does any constant folding
> on the values - they'll get converted to quiet NaNs.
I know. I don't think it's too unreasonable: once you've done a calculation on it, it's no longer uninitialized. The only way it could get constant-folded is if you've explicitly entered 'real.init'. So you'd have to be doing something peculiar.
(Negation still preserves signallingness, through the ancient code in DMD; and there, you're just setting the sign bit).
Since it only SNANs automatically get converted to QNANs when no traps are enabled, and since no-one expects traps to be enabled at compile time, it's quite defensible, I think.