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

Reply via email to