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 <[email protected]>
---
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 a55f8e38283..108191955e2 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -3660,6 +3660,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.49.0