Enhance fold sibcall pass to fold sibcall targets into jump table by turning:
foo: .cfi_startproc cmpl $4, %edi ja .L1 movl %edi, %edi jmp *.L4(,%rdi,8) .section .rodata .L4: .quad .L8 .quad .L7 .quad .L6 .quad .L5 .quad .L3 .text .L5: jmp bar3 .L3: jmp bar4 .L8: jmp bar0 .L7: jmp bar1 .L6: jmp bar2 .L1: ret .cfi_endproc into: foo: .cfi_startproc cmpl $4, %edi ja .L1 movl %edi, %edi jmp *.L4(,%rdi,8) .section .rodata .L4: .quad bar0 .quad bar1 .quad bar2 .quad bar3 .quad bar4 .text .L1: ret .cfi_endproc Before DWARF frame generation pass, jump tables look like: (jump_table_data 16 15 17 (addr_vec:DI [ (label_ref:DI 18) (label_ref:DI 22) (label_ref:DI 26) (label_ref:DI 30) (label_ref:DI 34) ])) ... (code_label 30 17 31 4 5 (nil) [1 uses]) (note 31 30 32 4 [bb 4] NOTE_INSN_BASIC_BLOCK) (call_insn/j 32 31 33 4 (call (mem:QI (symbol_ref:DI ("bar3") [flags 0x41] <function_decl 0x7f21be3c0e00 bar3>) [0 bar3 S1 A8]) (const_int 0 [0])) "j.c":15:13 1469 {sibcall_di} (expr_list:REG_CALL_DECL (symbol_ref:DI ("bar3") [flags 0x41] <function_decl 0x7f21be3c0e00 bar3>) (nil)) (nil)) If the jump table entry points to a target basic block with only a direct sibcall, change the entry to point to the sibcall target, decrement the target basic block entry label use count and redirect the edge to the exit basic block. gcc/ PR target/14721 * config/i386/i386-features.cc (fold_sibcall): Fold the sibcall targets into jump table. gcc/testsuite/ PR target/14721 * gcc.target/i386/pr14721-1a.c: New. * gcc.target/i386/pr14721-1b.c: Likewise. * gcc.target/i386/pr14721-1c.c: Likewise. * gcc.target/i386/pr14721-2c.c: Likewise. * gcc.target/i386/pr14721-2b.c: Likewise. * gcc.target/i386/pr14721-2c.c: Likewise. * gcc.target/i386/pr14721-3c.c: Likewise. * gcc.target/i386/pr14721-3b.c: Likewise. * gcc.target/i386/pr14721-3c.c: Likewise. Signed-off-by: H.J. Lu <hjl.to...@gmail.com> --- gcc/config/i386/i386-features.cc | 76 ++++++++++++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-1a.c | 54 +++++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-1b.c | 37 +++++++++++ gcc/testsuite/gcc.target/i386/pr14721-1c.c | 37 +++++++++++ gcc/testsuite/gcc.target/i386/pr14721-2a.c | 58 +++++++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-2b.c | 41 ++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-2c.c | 43 ++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-3a.c | 56 ++++++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-3b.c | 40 ++++++++++++ gcc/testsuite/gcc.target/i386/pr14721-3c.c | 39 +++++++++++ 10 files changed, 481 insertions(+) create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-1a.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-1b.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-1c.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-2a.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-2b.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-2c.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-3a.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-3b.c create mode 100644 gcc/testsuite/gcc.target/i386/pr14721-3c.c diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc index 06e4e84361a..cd3b2d13264 100644 --- a/gcc/config/i386/i386-features.cc +++ b/gcc/config/i386/i386-features.cc @@ -3415,6 +3415,82 @@ fold_sibcall (void) EXIT_BLOCK_PTR_FOR_FN (cfun)); branch_edge->flags |= EDGE_SIBCALL | EDGE_ABNORMAL; } + else if (label && !ANY_RETURN_P (label)) + { + /* Check if it is a jump table with addresses. */ + rtx_insn *target = as_a<rtx_insn *> (label); + rtx_insn *table = next_insn (target); + if (!table + || !JUMP_TABLE_DATA_P (table) + || GET_CODE (PATTERN (table)) != ADDR_VEC) + continue; + + basic_block bb_dest; + rtx body = PATTERN (table); + unsigned int i, len = XVECLEN (body, 0); + rtx *sibcall_targets = new rtx [len](); + rtx *sibcall_notes = new rtx [len](); + + for (i = 0; i < len; i++) + { + label = XVECEXP (body, 0, i); + label = XEXP (label, 0); + /* Check if the basic block referenced by LABEL only + has a direct sibcall. */ + bb_dest = BLOCK_FOR_INSN (label); + sibcall_targets[i] + = sibcall_only_bb (bb_dest, &sibcall_insn, + &sibcall_notes[i]); + } + + sibcall_note = sibcall_notes[0]; + if (!reg_eh_region_note_ok_p (insn, &sibcall_note)) + { + sibcall_insn = nullptr; + goto skip; + } + + /* All sibcalls in the jump table must have the same + REG_EH_REGION note. */ + for (i = 1; i < len; i++) + if (sibcall_note != sibcall_notes[i] + || (sibcall_note + && !rtx_equal_p (sibcall_note, + sibcall_notes[i]))) + { + sibcall_insn = nullptr; + goto skip; + } + + for (i = 0; i < len; i++) + if (sibcall_targets[i]) + { + label = XVECEXP (body, 0, i); + label = XEXP (label, 0); + basic_block bb_dest = BLOCK_FOR_INSN (label); + + edge e; + edge_iterator ei; + /* Redirect the edge to the exit basic block. */ + FOR_EACH_EDGE (e, ei, bb->succs) + { + if (e->dest == bb_dest) + { + redirect_edge_succ (e, + EXIT_BLOCK_PTR_FOR_FN (cfun)); + e->flags |= EDGE_SIBCALL | EDGE_ABNORMAL; + } + } + + /* Fold the sibcall target by changing the jump + table entry to the sibcall target. */ + XVECEXP (body, 0, i) = sibcall_targets[i]; + } + +skip: + delete []sibcall_notes; + delete []sibcall_targets; + } if (sibcall_insn) { diff --git a/gcc/testsuite/gcc.target/i386/pr14721-1a.c b/gcc/testsuite/gcc.target/i386/pr14721-1a.c new file mode 100644 index 00000000000..0ebd16554b2 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-1a.c @@ -0,0 +1,54 @@ +/* { dg-do compile { target { *-*-linux* && lp64 } } } */ +/* { dg-options "-O2 -fno-pic -fplt" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** cmpl \$4, %edi +** ja .L1 +** movl %edi, %edi +** jmp \*.L4\(,%rdi,8\) +** .section .rodata +**... +**.L4: +** .quad bar0 +** .quad bar1 +** .quad bar2 +** .quad bar3 +** .quad bar4 +**... +** .text +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +extern void bar0 (void); +extern void bar1 (void); +extern void bar2 (void); +extern void bar3 (void); +extern void bar4 (void); + +void +foo (int i) +{ + switch (i) + { + case 0: bar0 (); break; + case 1: bar1 (); break; + case 2: bar2 (); break; + case 3: bar3 (); break; + case 4: bar4 (); break; + } +} + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar1" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar2" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-1b.c b/gcc/testsuite/gcc.target/i386/pr14721-1b.c new file mode 100644 index 00000000000..d194a332221 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-1b.c @@ -0,0 +1,37 @@ +/* { dg-do compile { target { *-*-linux* && ia32 } } } */ +/* { dg-options "-O2 -fno-pic -fplt" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** movl 4\(%esp\), %eax +** cmpl \$4, %eax +** ja .L1 +** jmp \*.L4\(,%eax,4\) +** .section .rodata +**... +**.L4: +** .long bar0 +** .long bar1 +** .long bar2 +** .long bar3 +** .long bar4 +**... +** .text +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +#include "pr14721-1a.c" + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar1" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar2" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-1c.c b/gcc/testsuite/gcc.target/i386/pr14721-1c.c new file mode 100644 index 00000000000..1a9b137d5a0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-1c.c @@ -0,0 +1,37 @@ +/* { dg-do compile { target { *-*-linux* && maybe_x32 } } } */ +/* { dg-options "-O2 -fno-pic -fplt -mx32" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** cmpl \$4, %edi +** ja .L1 +** movl .L4\(,%edi,4\), %eax +** jmp \*%rax +** .section .rodata +**... +**.L4: +** .long bar0 +** .long bar1 +** .long bar2 +** .long bar3 +** .long bar4 +**... +** .text +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +#include "pr14721-1a.c" + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar1" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar2" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-2a.c b/gcc/testsuite/gcc.target/i386/pr14721-2a.c new file mode 100644 index 00000000000..a5a0d6c147a --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-2a.c @@ -0,0 +1,58 @@ +/* { dg-do compile { target { *-*-linux* && lp64 } } } */ +/* { dg-options "-O2 -fno-pic -fplt" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** cmpl \$4, %edi +** ja .L1 +** movl %edi, %edi +** jmp \*.L4\(,%rdi,8\) +** .section .rodata +**... +**.L4: +** .quad bar0 +** .quad .L7 +** .quad .L6 +** .quad bar3 +** .quad bar4 +**... +** .text +**... +**.L7: +** jmp \*bar1\(%rip\) +**... +**.L6: +** jmp \*bar2\(%rip\) +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +extern void bar0 (void); +extern void (*bar1) (void); +extern void (*bar2) (void); +extern void bar3 (void); +extern void bar4 (void); + +void +foo (int i) +{ + switch (i) + { + case 0: bar0 (); break; + case 1: bar1 (); break; + case 2: bar2 (); break; + case 3: bar3 (); break; + case 4: bar4 (); break; + } +} + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-2b.c b/gcc/testsuite/gcc.target/i386/pr14721-2b.c new file mode 100644 index 00000000000..9edc23a063d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-2b.c @@ -0,0 +1,41 @@ +/* { dg-do compile { target { *-*-linux* && ia32 } } } */ +/* { dg-options "-O2 -fno-pic -fplt" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** movl 4\(%esp\), %eax +** cmpl \$4, %eax +** ja .L1 +** jmp \*.L4\(,%eax,4\) +** .section .rodata +**... +**.L4: +** .long bar0 +** .long .L7 +** .long .L6 +** .long bar3 +** .long bar4 +**... +** .text +**... +**.L7: +** jmp \*bar1 +**... +**.L6: +** jmp \*bar2 +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +#include "pr14721-2a.c" + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-2c.c b/gcc/testsuite/gcc.target/i386/pr14721-2c.c new file mode 100644 index 00000000000..c0d8d7b23cb --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-2c.c @@ -0,0 +1,43 @@ +/* { dg-do compile { target { *-*-linux* && maybe_x32 } } } */ +/* { dg-options "-O2 -fno-pic -fplt -mx32" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** cmpl \$4, %edi +** ja .L1 +** movl .L4\(,%edi,4\), %eax +** jmp \*%rax +** .section .rodata +**... +**.L4: +** .long bar0 +** .long .L7 +** .long .L6 +** .long bar3 +** .long bar4 +**... +** .text +**... +**.L7: +** movl bar1\(%rip\), %eax +** jmp \*%rax +**... +**.L6: +** movl bar2\(%rip\), %eax +** jmp \*%rax +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +#include "pr14721-2a.c" + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-3a.c b/gcc/testsuite/gcc.target/i386/pr14721-3a.c new file mode 100644 index 00000000000..78e073723eb --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-3a.c @@ -0,0 +1,56 @@ +/* { dg-do compile { target { *-*-linux* && lp64 } } } */ +/* { dg-options "-O2 -fno-pic -fplt" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** testl %esi, %esi +** jne bar0 +** cmpl \$4, %edi +** ja .L1 +** movl %edi, %edi +** jmp \*.L5\(,%rdi,8\) +** .section .rodata +**... +**.L5: +** .quad bar0 +** .quad bar1 +** .quad bar2 +** .quad bar3 +** .quad bar4 +**... +** .text +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +extern void bar0 (void); +extern void bar1 (void); +extern void bar2 (void); +extern void bar3 (void); +extern void bar4 (void); + +void +foo (int i, int j) +{ + if (j) + { + bar0 (); + return; + } + + switch (i) + { + case 0: bar0 (); break; + case 1: bar1 (); break; + case 2: bar2 (); break; + case 3: bar3 (); break; + case 4: bar4 (); break; + } +} diff --git a/gcc/testsuite/gcc.target/i386/pr14721-3b.c b/gcc/testsuite/gcc.target/i386/pr14721-3b.c new file mode 100644 index 00000000000..3870e963e4b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-3b.c @@ -0,0 +1,40 @@ +/* { dg-do compile { target { *-*-linux* && ia32 } } } */ +/* { dg-options "-O2 -fno-pic -fplt" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** movl 8\(%esp\), %edx +** movl 4\(%esp\), %eax +** testl %edx, %edx +** jne bar0 +** cmpl \$4, %eax +** ja .L1 +** jmp \*.L5\(,%eax,4\) +** .section .rodata +**... +**.L5: +** .long bar0 +** .long bar1 +** .long bar2 +** .long bar3 +** .long bar4 +**... +** .text +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +#include "pr14721-3a.c" + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar1" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar2" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr14721-3c.c b/gcc/testsuite/gcc.target/i386/pr14721-3c.c new file mode 100644 index 00000000000..fa763099bd3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr14721-3c.c @@ -0,0 +1,39 @@ +/* { dg-do compile { target { *-*-linux* && maybe_x32 } } } */ +/* { dg-options "-O2 -fno-pic -fplt -mx32" } */ +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc'). */ +/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.} } } */ + +/* +**foo: +**.LFB0: +** .cfi_startproc +** testl %esi, %esi +** jne bar0 +** cmpl \$4, %edi +** ja .L1 +** movl .L5\(,%edi,4\), %eax +** jmp \*%rax +** .section .rodata +**... +**.L5: +** .long bar0 +** .long bar1 +** .long bar2 +** .long bar3 +** .long bar4 +**... +** .text +**... +**.L1: +** ret +** .cfi_endproc +**... +*/ + +#include "pr14721-3a.c" + +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar0" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar1" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar2" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar3" } } */ +/* { dg-final { scan-assembler-not "jmp\[\\t \]*bar4" } } */ -- 2.48.1