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
After basic block reordering 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 and decrement
the target basic block entry label use count. If the target basic block
isn't kept for JUMP_LABEL of the conditional tailcall, delete it if its
entry label use count is 0.
Update create_trace_edges to skip symbol reference in jump table and
update final_scan_insn_1 to support symbol reference in jump table.
gcc/
PR target/14721
* dwarf2cfi.cc (create_trace_edges): Skip symbol reference in
jump table.
* final.cc (final_scan_insn_1): Support symbol reference in
jump table.
* config/i386/i386-features.cc (jump_table_label_to_basic_block):
New.
(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 | 70 +++++++++++++++++++++-
gcc/dwarf2cfi.cc | 7 ++-
gcc/final.cc | 22 ++++++-
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 ++++++++++++
12 files changed, 499 insertions(+), 5 deletions(-)
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 bb1e428bb1b..e89c8324f34 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -3328,6 +3328,22 @@ sibcall_only_bb (basic_block bb, bitmap sibcall_bbs)
return nullptr;
}
+/* Return the sibcall target if the basic block referenced by LABEL only
+ has a direct sibcall. */
+
+static rtx
+jump_table_label_to_basic_block (rtx label, bitmap sibcall_bbs)
+{
+ label = XEXP (label, 0);
+ basic_block bb = BLOCK_FOR_INSN (label);
+ rtx target = sibcall_only_bb (bb, sibcall_bbs);
+ /* Decrement the label use count if jump table entry will use the
+ sibcall directly. */
+ if (target)
+ LABEL_NUSES (label) -= 1;
+ return target;
+}
+
/* Fold direct sibcall. */
static unsigned int
@@ -3337,11 +3353,13 @@ fold_sibcall (void)
bitmap_obstack_initialize (NULL);
bitmap jcc_sibcall_bbs = BITMAP_ALLOC (NULL);
+ bitmap table_sibcall_bbs = BITMAP_ALLOC (NULL);
unsigned int todo = 0;
basic_block bb;
rtx_insn *insn;
bool jcc_sibcall = false;
+ bool table_sibcall = false;
FOR_EACH_BB_FN (bb, cfun)
{
@@ -3350,10 +3368,11 @@ fold_sibcall (void)
if (!JUMP_P (insn))
continue;
+ rtx label = JUMP_LABEL (insn);
+ rtx sibcall_target;
+
if (INSN_CODE (insn) == CODE_FOR_jcc)
{
- rtx label = JUMP_LABEL (insn);
- rtx sibcall_target;
edge branch_edge = BRANCH_EDGE (bb);
basic_block branch_bb = branch_edge->dest;
sibcall_target = sibcall_only_bb (branch_bb,
@@ -3373,6 +3392,33 @@ fold_sibcall (void)
jcc_sibcall = true;
}
}
+ 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;
+
+ rtx body = PATTERN (table);
+ unsigned int len = XVECLEN (body, 0);
+ for (unsigned int i = 0; i < len; i++)
+ {
+ rtx elt = XVECEXP (body, 0, i);
+ sibcall_target
+ = jump_table_label_to_basic_block (elt,
+ table_sibcall_bbs);
+ /* Fold the sibcall target by changing the jump table
+ entry to the sibcall target. */
+ if (sibcall_target)
+ {
+ XVECEXP (body, 0, i) = sibcall_target;
+ table_sibcall = true;
+ }
+ }
+ }
}
}
@@ -3440,6 +3486,26 @@ fold_sibcall (void)
}
}
+ if (table_sibcall)
+ {
+ bitmap_iterator bi;
+ unsigned id;
+ EXECUTE_IF_SET_IN_BITMAP (table_sibcall_bbs, 0, id, bi)
+ {
+ /* Check if this basic block should be kept for JUMP_LABEL of
+ the conditional tailcall. */
+ if (jcc_sibcall_bbs && bitmap_bit_p (jcc_sibcall_bbs, id))
+ continue;
+ bb = BASIC_BLOCK_FOR_FN (cfun, id);
+ insn = BB_HEAD (bb);
+ gcc_assert (LABEL_P (insn));
+ /* Delete the basic block if its entry label is unused now. */
+ if (LABEL_NUSES (insn) == 0)
+ delete_basic_block (bb);
+ }
+ }
+
+ BITMAP_FREE (table_sibcall_bbs);
BITMAP_FREE (jcc_sibcall_bbs);
bitmap_obstack_release (NULL);
diff --git a/gcc/dwarf2cfi.cc b/gcc/dwarf2cfi.cc
index a5353d39e7e..e80b2061453 100644
--- a/gcc/dwarf2cfi.cc
+++ b/gcc/dwarf2cfi.cc
@@ -2657,7 +2657,12 @@ create_trace_edges (rtx_insn *insn)
n = GET_NUM_ELEM (vec);
for (i = 0; i < n; ++i)
{
- rtx_insn *lab = as_a <rtx_insn *> (XEXP (RTVEC_ELT (vec, i), 0));
+ rtx l = RTVEC_ELT (vec, i);
+ /* Skip symbol reference. */
+ if (SYMBOL_REF_P (l))
+ continue;
+ l = XEXP (l, 0);
+ rtx_insn *lab = as_a <rtx_insn *> (l);
maybe_record_trace_start (lab, insn);
}
diff --git a/gcc/final.cc b/gcc/final.cc
index 5fb44433fb8..602bfb09005 100644
--- a/gcc/final.cc
+++ b/gcc/final.cc
@@ -2528,13 +2528,31 @@ final_scan_insn_1 (rtx_insn *insn, FILE *file, int
optimize_p ATTRIBUTE_UNUSED,
}
#else
vlen = XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC);
+ const char *asm_op = integer_asm_op (POINTER_SIZE_UNITS, true);
+ gcc_assert (asm_op != NULL);
for (idx = 0; idx < vlen; idx++)
{
if (GET_CODE (body) == ADDR_VEC)
{
#ifdef ASM_OUTPUT_ADDR_VEC_ELT
- ASM_OUTPUT_ADDR_VEC_ELT
- (file, CODE_LABEL_NUMBER (XEXP (XVECEXP (body, 0, idx),
0)));
+ rtx l = XVECEXP (body, 0, idx);
+ if (SYMBOL_REF_P (l))
+ {
+ fprintf (file, "%s", asm_op);
+ assemble_external (SYMBOL_REF_DECL (l));
+#ifdef ASM_OUTPUT_SYMBOL_REF
+ ASM_OUTPUT_SYMBOL_REF (file, l);
+#else
+ assemble_name (file, XSTR (l, 0));
+#endif
+ putc ('\n', file);
+ }
+ else
+ {
+ l = XEXP (l, 0);
+ ASM_OUTPUT_ADDR_VEC_ELT
+ (file, CODE_LABEL_NUMBER (l));
+ }
#else
gcc_unreachable ();
#endif
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