Bug 15538 – [REG 2.064] wrong code with switch

Status
RESOLVED
Resolution
FIXED
Severity
regression
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
All
Creation time
2016-01-09T23:48:02Z
Last change time
2017-10-15T01:54:42Z
Keywords
wrong-code
Assigned to
No Owner
Creator
Johannes Loher
See also
https://issues.dlang.org/show_bug.cgi?id=14587

Attachments

IDFilenameSummaryContent-TypeSize
1573test.dcode exampletext/x-dsrc1062
1600switch_bug.dnew code exampletext/x-dsrc1114

Comments

Comment #0 by johannes.loher — 2016-01-09T23:48:02Z
Created attachment 1573 code example When compiling the attached code with DMD and the running it, core.exception.SwitchError is raised, even though all cases are covered. The conditions for this to happen seem quite random to me: Small changes in the setup that should not have any effect on the outcome actually change whether an exception is raised or not. Compiling with -g prevents an exception from being raised (i.e. it works as it should). More examples are covered in my code example. Binaries created by gdc and ldc behave as expected and do not raise an exception.
Comment #1 by johannes.loher — 2016-06-13T14:36:37Z
Created attachment 1600 new code example
Comment #2 by johannes.loher — 2016-08-08T15:04:46Z
Everything seems to work fine when running the program from within gdb. It also does not seem to be a problem of final switch, but switch in general. I tried it with a usual switch clause, in that case It always goes to default:
Comment #3 by dfj1esp02 — 2016-08-09T14:09:08Z
Does optimization flag -O affect the behavior?
Comment #4 by ag0aep6g — 2016-08-09T15:02:03Z
Reduced a bit more: ---- struct S { int a = 0; int b = 1; } int f1(int a) { switch (a) { case 0: return 10; case 1: return 20; case 2: return 30; case 3: return 40; default: return 99; } } int f2(S s) { return f1(s.a); } void main() { S s; assert(f1(s.a) == 10); /* passes */ assert(f2(s) == 10); /* fails */ } ---- (In reply to Sobirari Muhomori from comment #3) > Does optimization flag -O affect the behavior? No. -inline has an effect, presumably because f2 is inlined.
Comment #5 by johannes.loher — 2016-10-15T13:34:54Z
In the reduced example, the bug also appears when running in gdb.
Comment #6 by ag0aep6g — 2017-04-24T16:26:41Z
*** Issue 17347 has been marked as a duplicate of this issue. ***
Comment #7 by alexander.breckel — 2017-04-25T09:38:41Z
Further reduced: ---- struct S { int a = 0; int b = 1; } int f1(S s) { switch (s.a) { case 0: return 10; case 1: return 20; case 2: return 30; case 3: return 40; default: return 99; } } void main() { S s; assert(f1(s) == 10); /* fails */ } The generates assembly for f1 is this: ---- 00000000004271a8 <_D3app2f1FS3app1SZi>: 4271a8: 55 push %rbp 4271a9: 48 8b ec mov %rsp,%rbp 4271ac: 48 83 ec 10 sub $0x10,%rsp 4271b0: 48 89 7d f8 mov %rdi,-0x8(%rbp) 4271b4: 48 83 ff 03 cmp $0x3,%rdi 4271b8: 77 2d ja 4271e7 <_D3app2f1FS3app1SZi+0x3f> 4271ba: 48 8d 05 a7 3f 02 00 lea 0x23fa7(%rip),%rax # 44b168 <_D3app1S6__initZ+0x8> 4271c1: 48 63 0c b8 movslq (%rax,%rdi,4),%rcx 4271c5: 48 8d 04 01 lea (%rcx,%rax,1),%rax 4271c9: ff e0 jmpq *%rax 4271cb: b8 0a 00 00 00 mov $0xa,%eax 4271d0: c9 leaveq 4271d1: c3 retq 4271d2: b8 14 00 00 00 mov $0x14,%eax 4271d7: c9 leaveq 4271d8: c3 retq 4271d9: b8 1e 00 00 00 mov $0x1e,%eax 4271de: c9 leaveq 4271df: c3 retq 4271e0: b8 28 00 00 00 mov $0x28,%eax 4271e5: c9 leaveq 4271e6: c3 retq 4271e7: b8 63 00 00 00 mov $0x63,%eax 4271ec: c9 leaveq 4271ed: c3 retq ... Dmd packs the whole struct into a single register (%rdi). Since the switch statement contains consecutive cases, Dmd rightfully tries to skip as many of them as possible by doing a single comparison (cmp $0x3,%rdi). However, this comparison is performed on the whole register, which also contains the second field of the Struct.
Comment #8 by johannes.loher — 2017-07-01T10:47:29Z
I tracked this down a bit. It worked fine till 2.063.2 (including). From 2.064 onward it sarts to segfault. Finally, from 2.068 onward, the assert error starts hppening.
Comment #9 by ag0aep6g — 2017-07-01T11:01:57Z
(In reply to Johannes Loher from comment #8) > I tracked this down a bit. It worked fine till 2.063.2 (including). From > 2.064 onward it sarts to segfault. Finally, from 2.068 onward, the assert > error starts hppening. That makes it a regression.
Comment #10 by dlang-bugzilla — 2017-07-01T11:32:51Z
I couldn't find the point in the source history at which it started bisecting, because when building DMD from source, the test program bisects as far back as I can get source builds to work (2011), so it's likely a latent bug which became exposed by some change in 2.064. However, I did find that the assert replaced the segfault in https://github.com/dlang/dmd/pull/4666.
Comment #11 by bugzilla — 2017-10-07T07:56:32Z
Happens on Win64 too.
Comment #12 by bugzilla — 2017-10-07T08:38:59Z
Comment #13 by github-bugzilla — 2017-10-15T01:54:41Z
Commits pushed to master at https://github.com/dlang/dmd https://github.com/dlang/dmd/commit/ef56ed9f8e2d778de2396bde91d34b407e02248d fix Issue 15538 - [REG 2.064] wrong code with switch https://github.com/dlang/dmd/commit/8c8bb595057f896ba5f8a5b3d02dc8e86721c77a Merge pull request #7188 from WalterBright/fix15538 fix Issue 15538 - [REG 2.064] wrong code with switch