www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - issue with each specifically for x86

reply Matt Gamble <gamblemj gmail.com> writes:
This is a record for me with two 32bit vs 64bit issues in one 
day. Seems to be a problem with using "each" under 32bit which 
can be fixed by using foreach or switching to x64. Am I doing 
something wrong or is this the second bug I've found today?

Below is a silly case, that replicates an error. (i.e. I know I 
could use iota(0,9,2).array), but that does not demonstrate the 
potential bug and would not fix my actual program.)

import std.range;
import std.algorithm;
import std.stdio;

unittest
{
	auto a = new double[9];
	a[0] = 0;
	iota(1,a.length).each!(i => a[i] = a[i-1] + 2);
	writeln(a);
}

//x86, wrong, error
//[-nan, 2, 4, 6, 8, 10, 12, 14, 16]
//First-chance exception: std.format.FormatException Unterminated 
format specifier: "%" at 
C:\D\dmd2\windows\bin\..\..\src\phobos\std\format.d(1175)

//x64, correct
//[0, 2, 4, 6, 8, 10, 12, 14, 16]

unittest
{
	auto a = new double[9];
	a[0] = 0;
	foreach(i; 1..a.length) a[i] = a[i - 1] + 2;
	writeln(a);
}

//x86, correct
//[0, 2, 4, 6, 8, 10, 12, 14, 16]

//x64, correct
//[0, 2, 4, 6, 8, 10, 12, 14, 16]

This is windows 10, DMD v2.076.1
Mar 07 2018
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 03/07/2018 10:57 AM, Matt Gamble wrote:
 This is a record for me with two 32bit vs 64bit issues in one day. Seems 
 to be a problem with using "each" under 32bit which can be fixed by 
 using foreach or switching to x64. Am I doing something wrong or is this 
 the second bug I've found today?
 
 Below is a silly case, that replicates an error. (i.e. I know I could 
 use iota(0,9,2).array), but that does not demonstrate the potential bug 
 and would not fix my actual program.)
 
 import std.range;
 import std.algorithm;
 import std.stdio;
 
 unittest
 {
      auto a = new double[9];
      a[0] = 0;
      iota(1,a.length).each!(i => a[i] = a[i-1] + 2);
      writeln(a);
 }
 
 //x86, wrong, error
 //[-nan, 2, 4, 6, 8, 10, 12, 14, 16]
 //First-chance exception: std.format.FormatException Unterminated format 
 specifier: "%" at C:\D\dmd2\windows\bin\..\..\src\phobos\std\format.d(1175)
 
 //x64, correct
 //[0, 2, 4, 6, 8, 10, 12, 14, 16]
 
 unittest
 {
      auto a = new double[9];
      a[0] = 0;
      foreach(i; 1..a.length) a[i] = a[i - 1] + 2;
      writeln(a);
 }
 
 //x86, correct
 //[0, 2, 4, 6, 8, 10, 12, 14, 16]
 
 //x64, correct
 //[0, 2, 4, 6, 8, 10, 12, 14, 16]
 
 This is windows 10, DMD v2.076.1
 
Confirmed on Linux with dmd 2.078.1 It's somehow related to the unused return value of the lambda. The following code has the same error: iota(1,a.length).each!((i) { a[i] = a[i-1] + 2; return a[i]; }); The error disappears when that return statement is commented-out. Please file a dmd bug after making sure that 2.079 still has it. (Too lazy to install right now.) An ldc that I have handy does not have this bug: based on DMD v2.073.2 and LLVM 4.0.0 Ali
Mar 07 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 3/7/18 1:57 PM, Matt Gamble wrote:
 This is a record for me with two 32bit vs 64bit issues in one day. Seems 
 to be a problem with using "each" under 32bit which can be fixed by 
 using foreach or switching to x64. Am I doing something wrong or is this 
 the second bug I've found today?
 
 Below is a silly case, that replicates an error. (i.e. I know I could 
 use iota(0,9,2).array), but that does not demonstrate the potential bug 
 and would not fix my actual program.)
 
 import std.range;
 import std.algorithm;
 import std.stdio;
 
 unittest
 {
      auto a = new double[9];
      a[0] = 0;
      iota(1,a.length).each!(i => a[i] = a[i-1] + 2);
      writeln(a);
 }
 
 //x86, wrong, error
 //[-nan, 2, 4, 6, 8, 10, 12, 14, 16]
 //First-chance exception: std.format.FormatException Unterminated format 
 specifier: "%" at C:\D\dmd2\windows\bin\..\..\src\phobos\std\format.d(1175)
 
 //x64, correct
 //[0, 2, 4, 6, 8, 10, 12, 14, 16]
 
 unittest
 {
      auto a = new double[9];
      a[0] = 0;
      foreach(i; 1..a.length) a[i] = a[i - 1] + 2;
      writeln(a);
 }
 
 //x86, correct
 //[0, 2, 4, 6, 8, 10, 12, 14, 16]
 
 //x64, correct
 //[0, 2, 4, 6, 8, 10, 12, 14, 16]
 
 This is windows 10, DMD v2.076.1
 
It has something to do with the fact that you are returning the value: iota(1, a.length).each!((i) {a[i] = a[i - 1] + 2;}); // ok iota(1, a.length).each!((i) {return a[i] = a[i - 1] + 2;}); // shows error Which is odd to say the least, I don't think each is supposed to do anything with the return value. I don't get the exception BTW (2.078.1 Windows 10). Looking at each, it looks like it does this: cast(void) unaryFun!pred(r.front); So I tried this: auto pred = i => a[i] = a[i-1] + 2; foreach(i; 1 .. a.length) cast(void)pred(i); And I see the -nan value. Remove the cast(void) and I don't see it. Clearly there is some codegen issue here. -Steve
Mar 07 2018
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 03/07/2018 08:54 PM, Steven Schveighoffer wrote:
 Looking at each, it looks like it does this:
 
 cast(void) unaryFun!pred(r.front);
 
 So I tried this:
 
 auto pred = i => a[i] = a[i-1] + 2;
 foreach(i; 1 .. a.length)
     cast(void)pred(i);
 
 And I see the -nan value. Remove the cast(void) and I don't see it.
 
 Clearly there is some codegen issue here.
It's beautiful: ---- double f() { return 1; } void main() { cast(void) f(); cast(void) f(); cast(void) f(); cast(void) f(); cast(void) f(); cast(void) f(); cast(void) f(); double b = 2; assert(b == 2); /* fails; should pass */ } ----
Mar 07 2018
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 3/7/18 3:09 PM, ag0aep6g wrote:
 On 03/07/2018 08:54 PM, Steven Schveighoffer wrote:
 Clearly there is some codegen issue here.
It's beautiful: ---- double f() { return 1; } void main() {     cast(void) f();     cast(void) f();     cast(void) f();     cast(void) f();     cast(void) f();     cast(void) f();     cast(void) f();     double b = 2;     assert(b == 2); /* fails; should pass */ } ----
Are all those calls required? That's one crazy bug. -Steve
Mar 07 2018
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 03/07/2018 09:09 PM, ag0aep6g wrote:
 ----
 double f() { return 1; }
 
 void main()
 {
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
 
      double b = 2;
      assert(b == 2); /* fails; should pass */
 }
 ----
With `real` instead of `double` x86_64 is also affected.
Mar 07 2018
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
ag0aep6g wrote:

 On 03/07/2018 09:09 PM, ag0aep6g wrote:
 ----
 double f() { return 1; }
 void main()
 {
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
     double b = 2;
      assert(b == 2); /* fails; should pass */
 }
 ----
With `real` instead of `double` x86_64 is also affected.
yeah. that is 'cause SSE cannot do math with 80-bit floats, and compiler falls back to FPU in this case.
Mar 07 2018
prev sibling parent reply Matt Gamble <gamblemj gmail.com> writes:
On Wednesday, 7 March 2018 at 21:02:30 UTC, ag0aep6g wrote:
 On 03/07/2018 09:09 PM, ag0aep6g wrote:
 ----
 double f() { return 1; }
 
 void main()
 {
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
 
      double b = 2;
      assert(b == 2); /* fails; should pass */
 }
 ----
With `real` instead of `double` x86_64 is also affected.
Wow. Good to know I'm not crazy. I was afk for a bit, sorry. I guess I'm glad I found it and posted. The conversation has gone beyond my realm of understanding. Has anyone tested on 2.079 like Ali wanted. I have not had a chance to install. I was going to wait to post the bug till that was tried.
Mar 07 2018
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Matt Gamble wrote:

 On Wednesday, 7 March 2018 at 21:02:30 UTC, ag0aep6g wrote:
 On 03/07/2018 09:09 PM, ag0aep6g wrote:
 ----
 double f() { return 1; }
 void main()
 {
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
      cast(void) f();
     double b = 2;
      assert(b == 2); /* fails; should pass */
 }
 ----
With `real` instead of `double` x86_64 is also affected.
Wow. Good to know I'm not crazy. I was afk for a bit, sorry. I guess I'm glad I found it and posted. The conversation has gone beyond my realm of understanding. Has anyone tested on 2.079 like Ali wanted. I have not had a chance to install. I was going to wait to post the bug till that was tried.
sure, it is still there in git HEAD. it is not the bug that can be fixed "accidentally". %-)
Mar 07 2018
parent reply Matt Gamble <gamblemj gmail.com> writes:
On Wednesday, 7 March 2018 at 21:39:58 UTC, ketmar wrote:
 Matt Gamble wrote:

 On Wednesday, 7 March 2018 at 21:02:30 UTC, ag0aep6g wrote:
 On 03/07/2018 09:09 PM, ag0aep6g wrote:
 [...]
With `real` instead of `double` x86_64 is also affected.
Wow. Good to know I'm not crazy. I was afk for a bit, sorry. I guess I'm glad I found it and posted. The conversation has gone beyond my realm of understanding. Has anyone tested on 2.079 like Ali wanted. I have not had a chance to install. I was going to wait to post the bug till that was tried.
sure, it is still there in git HEAD. it is not the bug that can be fixed "accidentally". %-)
Ok, this has been submitted as a bug. https://issues.dlang.org/show_bug.cgi?id=18573 Thanks for the quick responses. Don't know what I'd do with out the community.
Mar 07 2018
parent ketmar <ketmar ketmar.no-ip.org> writes:
Matt Gamble wrote:

 Ok, this has been submitted as a bug. 
 https://issues.dlang.org/show_bug.cgi?id=18573
thank you.
Mar 07 2018
prev sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Steven Schveighoffer wrote:

it seems that the only difference between `void` and `double` lambda is one 
asm instruction: `fldl   (%edi)`. it is presend in `double` labmda, and 
absent in `void` lambda.

it looks like ignoring `double` result causes FPU stack imbalance ('cause 
compiler doesn't insert "FPU pop" instruction), and that affects the
computations.

on 64 bit it doesn't matter, 'cause no FPU is used there.

the fix prolly should be easy: just emit "FPU pop" if function result is 
ignored. codegen should have this info at hand, i believe.
Mar 07 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Mar 07, 2018 at 10:21:42PM +0200, ketmar via Digitalmars-d-learn wrote:
[...]
 it looks like ignoring `double` result causes FPU stack imbalance
 ('cause compiler doesn't insert "FPU pop" instruction), and that
 affects the computations.
 
 on 64 bit it doesn't matter, 'cause no FPU is used there.
 
 the fix prolly should be easy: just emit "FPU pop" if function result
 is ignored. codegen should have this info at hand, i believe.
Nice catch! Is there a bug filed for this yet? If not, it should be. T -- MACINTOSH: Most Applications Crash, If Not, The Operating System Hangs
Mar 07 2018
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
H. S. Teoh wrote:

 On Wed, Mar 07, 2018 at 10:21:42PM +0200, ketmar via Digitalmars-d-learn 
 wrote:
 [...]
 it looks like ignoring `double` result causes FPU stack imbalance
 ('cause compiler doesn't insert "FPU pop" instruction), and that
 affects the computations.
 on 64 bit it doesn't matter, 'cause no FPU is used there.
 the fix prolly should be easy: just emit "FPU pop" if function result
 is ignored. codegen should have this info at hand, i believe.
Nice catch! Is there a bug filed for this yet? If not, it should be.
it seems that no bug is filled yet. feel free to do so. ;-) or maybe OP should better do it, dunno. definitely not me. ;-)
Mar 07 2018
prev sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
H. S. Teoh wrote:

 On Wed, Mar 07, 2018 at 10:21:42PM +0200, ketmar via Digitalmars-d-learn 
 wrote:
 [...]
 it looks like ignoring `double` result causes FPU stack imbalance
 ('cause compiler doesn't insert "FPU pop" instruction), and that
 affects the computations.
 on 64 bit it doesn't matter, 'cause no FPU is used there.
 the fix prolly should be easy: just emit "FPU pop" if function result
 is ignored. codegen should have this info at hand, i believe.
Nice catch! Is there a bug filed for this yet? If not, it should be.
btw, this is specific to `cast(void)`. if you'll remove the cast, or do something like `cast(void)(pred(i)+42);`, the bug won't be there. so it looks like it is not a codegen bug after all, but glue layer. the codegen is correctly dropping the result without `cast(void)` (`fstp %st(0)` is inserted in `main`), but cannot do that if return type information is stripped. so it looks that glue layer should not strip return type info.
Mar 07 2018