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

Reply via email to