Consider this code:
=== a.d ===
struct T { long a, b; }
T[] data = [{0,0}];
void main()
{
int a = 0;
for (int i = 0; i >= 0; i--)
{
data[0] = data[a];
a--;
}
assert(a == -1); // Fails with -O
}
=== end ===
Fails when built with "-O", succeeds without "-O". The full command line:
dmd a.d -ofa.exe -O
dmd a.d -ofb.exe
Windows 7, dmd 2.071.0
If you add "-m64" to dmd command, the bug disappears, but re-appears if "int a" is changed to "long a".
Reduced from a large project with the help of dustmite and a lot of "WTF!".
Comment #1 by ag0aep6g — 2016-06-21T18:54:38Z
Slightly more reduced:
----
void main()
{
ubyte[9][1] data;
size_t a = 0;
loop:
data[0] = data[a];
a--;
bool b = false;
if (b) goto loop;
assert(a == -1); // Fails with -O
}
----
Also happens on Linux.
Comment #2 by kkryukov — 2016-06-22T01:20:48Z
A possible workaround: change "a--;" into "{ auto a2 = a - 1; a = a2; }".
(This is NOT to suggest that the bug does not need fixing, as it's annoying as hell that even simplest C-like code does not work correctly.)
As for previous reduction - it hurts my eyes to see size_t (unsigned type) compared for equality with -1, so I suggest to at least use ptrdiff_t.
Comment #3 by ketmar — 2016-06-23T12:00:07Z
funny thing: adding `asm { nop; }` anywhere in `main` seems to turn the optimizer off, and the code works again. a hackish way to control optimizer on per-function basis. ;-)
Comment #4 by kkryukov — 2016-08-02T03:33:10Z
I thought this might be a regression, so I tested dmd versions down to 2.050. But no, the bug is there in each and every one of them.
Comment #5 by greensunny12 — 2018-02-07T15:20:07Z
See also the discussion in the forum: https://forum.dlang.org/post/[email protected]
My summary:
The optimizer seems to be confused and wrongly precomputes a.
Note that if you remove e.g. "if (b) goto loop;", a will be correctly statically set in the printf too:
---
mov RSI,0FFFFFFFFh ; look ma - a is now -1
lea RDI,FLAT:.rodata[00h][RIP]
xor EAX,EAX ; set eax to 0
call printf@PLT32
---
For comparison the assembly of -O.
Here with printf and a different value to be compared with a because it's easier to read:
---
main:
push RBP
mov RBP,RSP
sub RSP,010h
lea RAX,-010h[RBP]
xor ECX,ECX
mov [RAX],RCX
mov 8[RAX],CL
lea RSI,-010h[RBP]
lea RDI,-010h[RBP]
movsd
movsb
mov RSI,01C71C71C71C71C70h ; 2 function argument (value of a)
lea RDI,FLAT:.rodata[00h][RIP] ; "%d" (1st function argument)
xor EAX,EAX ; set eax to 0
call printf@PLT32 ; printf("%d", a)
mov EDX,0Ch
lea RSI,_TMP0@PC32[RIP] ; load function arguments (in reverse order)
lea RDI,_TMP0@PC32[RIP]
call __assert@PLT32 ; values load, let's call assert
add [RAX],AL
.text.main ends
end
----
Comment #6 by bitter.taste — 2018-02-20T19:57:18Z
The LICM pass is too eager and given this small snippet
```
void main()
{
ubyte[9][1] data;
size_t a = 0;
loop:
data[0] = data[a];
a--;
bool b = false;
if (b) goto loop;
assert(a == -1); // Fails with -O
}
```
`a' is considered to be as an "index" variable into `data' with starting value 0 and that's decremented until `a < 1', this condition is most likely derived by the bound-checking condition.
As a result we get
```
Adding (a(1) = ((_TMP1(3) - #data(0)) / 9LL )) to exit block B4
```
that doesn't play well with the constant folding pass
```
const prop (_TMP1(3) replaced by #data(0).xfffffffffffffff7)
```
as shown by
```
const prop (a(1) replaced by 2049638230412172400LL )
```