Bug 2697 – Cast of float function return to ulong or uint gives bogus value

Status
RESOLVED
Resolution
FIXED
Severity
critical
Priority
P2
Component
dmd
Product
D
Version
D1 (retired)
Platform
x86
OS
Windows
Creation time
2009-02-28T17:26:00Z
Last change time
2014-03-01T00:36:03Z
Keywords
patch, wrong-code
Assigned to
nobody
Creator
smjg

Comments

Comment #0 by smjg — 2009-02-28T17:26:54Z
import std.stdio; float func() { return 5; } void main() { writefln("float: %f", func()); writefln("ulong: %d", cast(ulong) func()); writefln("long: %d", cast(long) func()); writefln("uint: %d", cast(uint) func()); writefln("int: %d", cast(int) func()); writefln("ushort: %d", cast(ushort) func()); writefln("short: %d", cast(short) func()); writefln("ubyte: %d", cast(ubyte) func()); writefln("byte: %d", cast(byte) func()); float result = func(); writefln("float: %f", result); writefln("ulong: %d", cast(ulong) result); writefln("long: %d", cast(long) result); writefln("uint: %d", cast(uint) result); writefln("int: %d", cast(int) result); writefln("ushort: %d", cast(ushort) result); writefln("short: %d", cast(short) result); writefln("ubyte: %d", cast(ubyte) result); writefln("byte: %d", cast(byte) result); } ---------- float: 5.000000 ulong: 2048 long: 5 uint: 0 int: 5 ushort: 5 short: 5 ubyte: 5 byte: 5 float: 5.000000 ulong: 5 long: 5 uint: 5 int: 5 ushort: 5 short: 5 ubyte: 5 byte: 5 ---------- If the ulong and long cases are removed, the cast to uint gives 2048. Seems to be only float that has the problem - it disappears if I change it to double or real.
Comment #1 by dfj1esp02 — 2009-03-06T04:39:39Z
In D2: float: 5.000000 ulong: 0 long: 5 uint: 0 int: 5 ushort: 5 short: 5 ubyte: 5 byte: 5 float: 5.000000 ulong: 5 long: 5 uint: 5 int: 5 ushort: 5 short: 5 ubyte: 5 byte: 5
Comment #2 by moritzwarning — 2009-05-03T13:30:57Z
I only see this error on windows, linux (x64) works for me. (dmd 1.043) This is the test case I have made after I ran into this problem: float getFloat() { return 11468.78f; } void main() { uint i = cast(uint) 11468.78f; assert(i == 11468); uint j = cast(uint) getFloat(); assert(j == 11468); //fails on windows: j is 0. }
Comment #3 by clugdbug — 2009-08-01T03:04:42Z
This code below fails on DMC, proving that this is a severe back-end bug. Interesting on DMC, the uint case does NOT fail: it inlines in that case. Raising severity, as I believe this is one of the worse extant compiler bugs. What's happening is that a function _DBLULNG@ is being used to convert from double to ulong. It expects a double, but it's being given a float. So garbage results. #include <assert.h> float getFloat() { return 5.0f; } int main() { unsigned long j = (unsigned long)getFloat(); assert(j==5); return 0; }
Comment #4 by clugdbug — 2009-08-04T00:27:08Z
The evil is done in cdcnvt() in cod4.c, just after this bit... case OPf_d: case OPd_f: /* if won't do us much good to transfer back and */ /* forth between 8088 registers and 8087 registers */ So it elides the conversion. This comment, and the transformation it performs, is false in the case where the OPf_d is followed a conversion from double to uint or ulong (OPd_u32 or OPd_u64). This is because the C functions which perform the conversion expect the double to be in EDX:EAX: ie, it genuinely needs to be converted to double. Conversions from float to uint work on Linux because a different function is called, which expects the double to be passed in ST0. They work on DMC Windows because the conversion gets inlined. Before presenting a patch, I'd like to make sure that there aren't any other failing cases. Could someone please check whether conversions from float to ulong work on Linux? (I'm surprised by the comment that it works on Linux, I would expect it to fail).
Comment #5 by bugzilla — 2009-08-04T01:14:48Z
This is the output I get from running Stewart's program with DMD 2.031 on Linux (64-bit, although since DMD is 32-bit it probably doesn't matter): float: 5.000000 ulong: 9223372036854775808 long: 5 uint: 5 int: 5 ushort: 5 short: 5 ubyte: 5 byte: 5 float: 5.000000 ulong: 5 long: 5 uint: 5 int: 5 ushort: 5 short: 5 ubyte: 5 byte: 5 As you suspected, float->uint works, while float->ulong fails.
Comment #6 by clugdbug — 2009-08-04T01:48:40Z
(In reply to comment #5) > This is the output I get from running Stewart's program with DMD 2.031 on Linux > As you suspected, float->uint works, while float->ulong fails. Awesome! There's several ways to fix this, I'm not sure which is the best.
Comment #7 by clugdbug — 2009-09-08T08:44:56Z
Here's a patch against DMD2.032. It also requires a change to llmath.d in druntime (or in Phobos1). The simplest way is to copy the contents of __LDBLULLNG() and put it into __DBLULLNG(). Because there's no need for a difference between those functions (although there can be a performance benefit, so they are both worth retaining, I think). ================= Index: C:/dmd2best/src/dmd/backend/cod4.c =================================================================== --- C:/dmd2best/src/dmd/backend/cod4.c (revision 49) +++ C:/dmd2best/src/dmd/backend/cod4.c (revision 51) @@ -2313,7 +2313,7 @@ case OPd_s64: return cnvt87(e,pretregs); case OPd_u32: // use subroutine, not 8087 -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if 1//TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS retregs = mST0; #else retregs = DOUBLEREGS; @@ -2321,7 +2321,8 @@ goto L1; case OPd_u64: - retregs = DOUBLEREGS; + retregs = mST0; + //retregs = DOUBLEREGS; goto L1; case OPu64_d: if (*pretregs & mST0) Index: C:/dmd2best/src/dmd/backend/cod1.c =================================================================== --- C:/dmd2best/src/dmd/backend/cod1.c (revision 49) +++ C:/dmd2best/src/dmd/backend/cod1.c (revision 51) @@ -1936,14 +1936,15 @@ Y(DOUBLEREGS_16,"_INTDBL@"), Y(DOUBLEREGS_16,"_DBLUNS@"), Y(DOUBLEREGS_16,"_UNSDBL@"), - Y(DOUBLEREGS_16,"_DBLULNG@"), +// Y(DOUBLEREGS_16,"_DBLULNG@"), + Y(DOUBLEREGS_16,"__DBLULNG"), Y(DOUBLEREGS_16,"_ULNGDBL@"), Y(DOUBLEREGS_16,"_DBLFLT@"), Y(ALLREGS,"_FLTDBL@"), Y(DOUBLEREGS_16,"_DBLLLNG@"), Y(DOUBLEREGS_16,"_LLNGDBL@"), -#if 0 +#if 1 Y(DOUBLEREGS_16,"__DBLULLNG"), #else Y(DOUBLEREGS_16,"_DBLULLNG@"), @@ -2022,7 +2023,7 @@ {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _INTDBL@ intdbl {mAX,mAX,0,INFfloat,1,1}, // _DBLUNS@ dbluns {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _UNSDBL@ unsdbl -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if 1//TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS {mDX|mAX,mAX,0,INF32|INFfloat,0,1}, // _DBLULNG@ dblulng #else {mDX|mAX,mAX,0,INFfloat,1,1}, // _DBLULNG@ dblulng @@ -2035,7 +2036,7 @@ {DOUBLEREGS_16,mDX|mAX,0,INFfloat,1,1}, // _DBLLLNG@ {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _LLNGDBL@ -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if 1//TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS {DOUBLEREGS_16,mDX|mAX,0,INFfloat,2,2}, // _DBLULLNG@ #else {DOUBLEREGS_16,mDX|mAX,0,INFfloat,1,1}, // _DBLULLNG@ @@ -2143,7 +2144,6 @@ 0x66,0xf7,0xe1, // mul ECX 0x66,0x0f,0xa4,0xc2,0x10, // shld EDX,EAX,16 ;DX,AX = EAX }; - c = genasm(c,lmul,sizeof(lmul)); } else
Comment #8 by clugdbug — 2009-09-24T08:43:25Z
There was a problem with the patch (failed the DMD test suite). Here's a revised patch which passes. As before, it also requires a change to llmath.d in druntime (or in Phobos1). The simplest way is to copy the contents of __LDBLULLNG() and put it into __DBLULLNG(). Note that __DLBULLNG() has never been used by any previous DMD version, so it can be changed without breaking anything. Index: C:/dmd2/src/dmd/backend/cod4.c =================================================================== --- C:/dmd2/src/dmd/backend/cod4.c (revision 49) +++ C:/dmd2/src/dmd/backend/cod4.c (revision 62) @@ -2313,7 +2313,7 @@ case OPd_s64: return cnvt87(e,pretregs); case OPd_u32: // use subroutine, not 8087 -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if 1//TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS retregs = mST0; #else retregs = DOUBLEREGS; @@ -2321,7 +2321,8 @@ goto L1; case OPd_u64: - retregs = DOUBLEREGS; + retregs = mST0; + //retregs = DOUBLEREGS; goto L1; case OPu64_d: if (*pretregs & mST0) Index: C:/dmd2/src/dmd/backend/cod1.c =================================================================== --- C:/dmd2/src/dmd/backend/cod1.c (revision 49) +++ C:/dmd2/src/dmd/backend/cod1.c (revision 62) @@ -1936,15 +1936,16 @@ Y(DOUBLEREGS_16,"_INTDBL@"), Y(DOUBLEREGS_16,"_DBLUNS@"), Y(DOUBLEREGS_16,"_UNSDBL@"), - Y(DOUBLEREGS_16,"_DBLULNG@"), +// Y(DOUBLEREGS_16,"_DBLULNG@"), + Y(DOUBLEREGS_16,"__DBLULNG"), Y(DOUBLEREGS_16,"_ULNGDBL@"), Y(DOUBLEREGS_16,"_DBLFLT@"), Y(ALLREGS,"_FLTDBL@"), Y(DOUBLEREGS_16,"_DBLLLNG@"), Y(DOUBLEREGS_16,"_LLNGDBL@"), -#if 0 - Y(DOUBLEREGS_16,"__DBLULLNG"), +#if 1 + Y(mST0|mAX|mDX,"__DBLULLNG"), #else Y(DOUBLEREGS_16,"_DBLULLNG@"), #endif @@ -2022,7 +2023,7 @@ {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _INTDBL@ intdbl {mAX,mAX,0,INFfloat,1,1}, // _DBLUNS@ dbluns {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _UNSDBL@ unsdbl -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS +#if 1//TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS {mDX|mAX,mAX,0,INF32|INFfloat,0,1}, // _DBLULNG@ dblulng #else {mDX|mAX,mAX,0,INFfloat,1,1}, // _DBLULNG@ dblulng @@ -2035,8 +2036,8 @@ {DOUBLEREGS_16,mDX|mAX,0,INFfloat,1,1}, // _DBLLLNG@ {DOUBLEREGS_16,DOUBLEREGS_32,0,INFfloat,1,1}, // _LLNGDBL@ -#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS - {DOUBLEREGS_16,mDX|mAX,0,INFfloat,2,2}, // _DBLULLNG@ +#if 1//TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS + {0,mDX|mAX,0,INF32|INFfloat,1,2}, // __LDBLULLNG #else {DOUBLEREGS_16,mDX|mAX,0,INFfloat,1,1}, // _DBLULLNG@ #endif @@ -2143,7 +2144,6 @@ 0x66,0xf7,0xe1, // mul ECX 0x66,0x0f,0xa4,0xc2,0x10, // shld EDX,EAX,16 ;DX,AX = EAX }; - c = genasm(c,lmul,sizeof(lmul)); } else
Comment #9 by bugzilla — 2009-10-06T02:14:47Z
Fixed dmd 1.048 and 2.033