DMD compiler(seems from 1.029) produces bad optimized opcode which access a popped register. $cat test.d class foo { uint a, b; } void main () { foo f = new foo; uint c; with (f) { c = 0; b = 0; a = 0; if (a == b && (a + (c = 66)) <= 66) assert(c == 66); } } $dmd -O test.d $./test Error: AssertError Failure test.d(13) $objdump -d test.o ... ... 25: 53 push %ebx 26: bb 42 00 00 00 mov 0ドルx42,%ebx 2b: 89 d9 mov %ebx,%ecx 2d: 5b pop %ebx <----------- here 2e: 8d 14 0b lea (%ebx,%ecx,1),%edx 31: 3b d6 cmp %esi,%edx 33: 77 0f ja 44 <_Dmain+0x44> 35: 83 fb 42 cmp 0ドルx42,%ebx <---------- and here 38: 74 0a je 44 <_Dmain+0x44> ... ...
Reduced test case shows that this is very low level. It's very specific, changing the expression order slightly will make the problem go away. The assignment to c gets optimised away, so that it remains as 0. ------------------------- void bug3521(int *a) { int c = 0; *a = 0; if (*a == 0 && (*a + (c = 2)) == 2) assert(c == 2); } void main () { int x; bug3521(&x); }
I investigated this a bit, so far without success. I'm writing notes here for when I come back to it. It's something to do with the register allocation. The compiler is somehow forgetting that it already assigned a register for c when doing the assignment. I removed the assert to make disassembly even easier: this hits a breakpoint only when compiled with -O. It only happens if c is zero. You can change the != into any kind of comparison, and the c=200 into c+=200, without affecting the bug. However, changing it into ((c = 200)!=*a) avoids the bug. void crash(int x) { if (x==200) return; asm { int 3; } } void bug3521(int *a){ int c = 0; *a = 0; if ( *a || (*a != (c = 200)) ) crash(c); } void main (){ int x; bug3521(&x); }
I've found the cause of the problem; it's when a registered variable is on both sides of an operator and the right side tries to modify the variable. The fix isn't easy, I'll work on it.
Fixed changeset 272.
Fixed dmd 1.053 and 2.037
AltStyle によって変換されたページ (->オリジナル) / アドレス: モード: デフォルト 音声ブラウザ ルビ付き 配色反転 文字拡大 モバイル