https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79472
Bug ID: 79472 Summary: x86-64: Switch table generation fails if default case has different code Product: gcc Version: 7.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: yuriks at yuriks dot net Target Milestone: --- Consider the following code, compiled at -O2: (Compiler explorer link: https://godbolt.org/g/GljuJt) #include <cstdint> #include <cstdio> #include <cstdlib> void frobulate(uint32_t v) { const char* s; switch (v) { case 0: s = "foo"; break; case 1: s = "bar"; break; case 2: s = "spam"; break; default: abort(); break; } printf("%s\n", s); } void frobulate_for_gcc(uint32_t v) { const char* s; switch (v) { case 0: s = "foo"; break; case 1: s = "bar"; break; case 2: s = "spam"; break; default: s = nullptr; break; } if (s == nullptr) { abort(); } printf("%s\n", s); } ------------------------------------------------------------------------------ gcc 6.3 produces the following code. It fails to generate a data table in "frobulate", even though it it doing a branch beforehand that could be used to insert the abort into. If I add the indirection of checking for the null pointer instead then the check is actually hoisted up into the range check for the table: frobulate(unsigned int): cmp edi, 1 je .L3 jb .L4 cmp edi, 2 jne .L12 mov edi, OFFSET FLAT:.LC2 jmp puts .L12: sub rsp, 8 call abort .L4: mov edi, OFFSET FLAT:.LC0 jmp puts .L3: mov edi, OFFSET FLAT:.LC1 jmp puts frobulate_for_gcc(unsigned int): cmp edi, 2 jbe .L18 .L14: sub rsp, 8 call abort .L18: mov edi, edi mov rdi, QWORD PTR CSWTCH.2[0+rdi*8] test rdi, rdi je .L14 jmp puts CSWTCH.2: .quad .LC0 .quad .LC1 .quad .LC2 ------------------------------------------------------------------------------ gcc 7 (20170211) appears to regress further, it fails to generate a table in both cases: frobulate(unsigned int): cmp edi, 1 je .L3 jb .L4 cmp edi, 2 jne .L12 mov edi, OFFSET FLAT:.LC2 jmp puts .L4: mov edi, OFFSET FLAT:.LC0 jmp puts .L3: mov edi, OFFSET FLAT:.LC1 jmp puts .L12: sub rsp, 8 call abort ------------------------------------------------------------------------------ For comparison, clang 3.9.1 generates optimal code in both instances: frobulate(unsigned int): # @frobulate(unsigned int) cmp edi, 3 jae .LBB0_1 movsxd rax, edi mov rdi, qword ptr [8*rax + .Lswitch.table.4] jmp puts # TAILCALL .LBB0_1: push rax call abort frobulate_for_gcc(unsigned int): # @frobulate_for_gcc(unsigned int) cmp edi, 3 jae .LBB1_1 movsxd rax, edi mov rdi, qword ptr [8*rax + .Lswitch.table.4] jmp puts # TAILCALL .LBB1_1: push rax call abort