x86 conditional branch (jcc) target can be either a label or a symbol.
Add a pass to fold tail call with jcc by turning:

        jcc     .L6
...
.L6:
        jmp     tailcall

into:

        jcc     tailcall

Immediately before the pass which turning REG_EH_REGION notes back into
NOTE_INSN_EH_REGION notes, conditional branches look like

(jump_insn 7 6 14 2 (set (pc)
        (if_then_else (eq (reg:CCZ 17 flags)
                (const_int 0 [0]))
            (label_ref:DI 23)
            (pc))) "x.c":8:5 1458 {jcc}
     (expr_list:REG_DEAD (reg:CCZ 17 flags)
        (int_list:REG_BR_PROB 217325348 (nil)))
...
(code_label 23 20 8 4 4 (nil) [1 uses])
(note 8 23 9 4 [bb 4] NOTE_INSN_BASIC_BLOCK)
(call_insn/j 9 8 10 4 (call (mem:QI (symbol_ref:DI ("bar") [flags 0x41]  <functi
on_decl 0x7f4cff3c0b00 bar>) [0 bar S1 A8])
        (const_int 0 [0])) "x.c":8:14 discrim 1 1469 {sibcall_di}
     (expr_list:REG_CALL_DECL (symbol_ref:DI ("bar") [flags 0x41]  <function_dec
l 0x7f4cff3c0b00 bar>)
        (nil))
    (nil))

If the branch edge destination is a basic block with only a direct
sibcall, change the jcc target to the sibcall target, decrement the
destination basic block entry label use count and redirect the edge
to the exit basic block.  Call delete_unreachable_blocks to delete
the unreachable basic blocks at the end if edges are redirected.

gcc/

        PR target/47253
        * i386/i386-features.cc: Include "cfgcleanup.h".
        (sibcall_only_bb): New.
        (reg_eh_region_note_ok_p): Likewise.
        (fold_sibcall): Likewise.
        (pass_data_fold_sibcall): Likewise.
        (pass_fold_sibcall): Likewise.
        (make_pass_fold_sibcall): Likewise.
        * config/i386/i386-passes.def: Add pass_fold_sibcall before
        pass_convert_to_eh_region_ranges.
        * config/i386/i386-protos.h (ix86_output_jcc_insn): New.
        (make_pass_fold_sibcall): Likewise.
        * config/i386/i386.cc (ix86_output_jcc_insn): Likewise.
        * config/i386/i386.md (*jcc): Renamed to ...
        (jcc): This.  Replace label_ref with symbol_label_operand.  Use
        ix86_output_jcc_insn.  Set length to 6 if the branch target
        isn't a label.

gcc/testsuite/

        PR target/47253
        * gcc.target/i386/pr47253-1a.c: New file.
        * gcc.target/i386/pr47253-1b.c: Likewise.
        * gcc.target/i386/pr47253-2a.c: Likewise.
        * gcc.target/i386/pr47253-2b.c: Likewise.
        * gcc.target/i386/pr47253-3a.c: Likewise.
        * gcc.target/i386/pr47253-3b.c: Likewise.
        * gcc.target/i386/pr47253-3c.c: Likewise.
        * gcc.target/i386/pr47253-4a.c: Likewise.
        * gcc.target/i386/pr47253-4b.c: Likewise.
        * gcc.target/i386/pr47253-5.c: Likewise.
        * gcc.target/i386/pr47253-6.c: Likewise.
        * gcc.target/i386/pr47253-7a.c: Likewise.
        * gcc.target/i386/pr47253-7b.c: Likewise.
        * gcc.target/i386/pr47253-8.c: Likewise.

Signed-off-by: H.J. Lu <hjl.to...@gmail.com>
---
 gcc/config/i386/i386-features.cc           | 204 +++++++++++++++++++++
 gcc/config/i386/i386-passes.def            |   1 +
 gcc/config/i386/i386-protos.h              |   3 +
 gcc/config/i386/i386.cc                    |  12 ++
 gcc/config/i386/i386.md                    |   9 +-
 gcc/config/i386/predicates.md              |   4 +
 gcc/testsuite/gcc.target/i386/pr47253-1a.c |  24 +++
 gcc/testsuite/gcc.target/i386/pr47253-1b.c |  17 ++
 gcc/testsuite/gcc.target/i386/pr47253-2a.c |  29 +++
 gcc/testsuite/gcc.target/i386/pr47253-2b.c |  17 ++
 gcc/testsuite/gcc.target/i386/pr47253-3a.c |  32 ++++
 gcc/testsuite/gcc.target/i386/pr47253-3b.c |  20 ++
 gcc/testsuite/gcc.target/i386/pr47253-3c.c |  20 ++
 gcc/testsuite/gcc.target/i386/pr47253-4a.c |  26 +++
 gcc/testsuite/gcc.target/i386/pr47253-4b.c |  18 ++
 gcc/testsuite/gcc.target/i386/pr47253-5.c  |  15 ++
 gcc/testsuite/gcc.target/i386/pr47253-6.c  |  15 ++
 gcc/testsuite/gcc.target/i386/pr47253-7a.c |  52 ++++++
 gcc/testsuite/gcc.target/i386/pr47253-7b.c |  36 ++++
 gcc/testsuite/gcc.target/i386/pr47253-8.c  |  74 ++++++++
 20 files changed, 624 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-1a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-1b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-2a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-2b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-3a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-3b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-3c.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-4a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-4b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-7a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-7b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-8.c

diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index 31f3ee2ef17..a55f8e38283 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -90,6 +90,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-vector-builder.h"
 #include "debug.h"
 #include "dwarf2out.h"
+#include "cfgcleanup.h"
 #include "i386-builtins.h"
 #include "i386-features.h"
 #include "i386-expand.h"
@@ -3533,6 +3534,209 @@ make_pass_remove_redundant_vector_load (gcc::context 
*ctxt)
   return new pass_remove_redundant_vector_load (ctxt);
 }
 
+/* Return the sibcall target if BB only has a direct sibcall.  */
+
+static rtx
+sibcall_only_bb (basic_block bb, const rtx_insn **sibcall_insn,
+                rtx *sibcall_note)
+{
+  if (!single_succ_p (bb)
+      || single_succ (bb) != EXIT_BLOCK_PTR_FOR_FN (cfun))
+    return nullptr;
+
+  rtx_insn *insn, *sibcall = nullptr;
+
+  FOR_BB_INSNS (bb, insn)
+    {
+      if (!NONDEBUG_INSN_P (insn))
+       continue;
+      if (sibcall
+         || !CALL_P (insn)
+         || !SIBLING_CALL_P (insn)
+         /* NB: Without this check, g++.dg/ipa/pr63838.C fails since
+            the exception region doesn't cover the conditional jump.  */
+         || can_throw_external (insn))
+       return nullptr;
+      sibcall = insn;
+    }
+
+  rtx call = PATTERN (sibcall);
+  if (GET_CODE (call) == SET)
+    call = SET_SRC (call);
+  if (GET_CODE (call) != CALL)
+    return nullptr;
+  rtx fnaddr = XEXP (call, 0);
+  fnaddr = XEXP (fnaddr, 0);
+  /* Return the sibcall target if the basic block only has a direct
+     sibcall.  */
+  if (GET_CODE (fnaddr) == SYMBOL_REF
+      && !ix86_nopic_noplt_attribute_p (fnaddr))
+    {
+      *sibcall_insn = sibcall;
+      *sibcall_note = find_reg_note (sibcall, REG_EH_REGION, nullptr);
+      return fnaddr;
+    }
+  return nullptr;
+}
+
+/* Return true if the REG_EH_REGION note of INSN is nullptr or the same
+   as *SIBCALL_NOTE.  If they are the same, clear *SIBCALL_NOTE.  */
+
+static bool
+reg_eh_region_note_ok_p (rtx_insn *insn, rtx *sibcall_note)
+{
+  rtx note = find_reg_note (insn, REG_EH_REGION, nullptr);
+  if (note && *sibcall_note)
+    {
+      /* Return false if REG_EH_REGION notes are different.  */
+      if (!rtx_equal_p (*sibcall_note, note))
+       return false;
+
+      /* No need to add *SIBCALL_NOTE to INSN since it is the same as
+        the REG_EH_REGION note of INSN.  */
+      *sibcall_note = nullptr;
+    }
+
+  return true;
+}
+
+/* Fold direct sibcall.  */
+
+static unsigned int
+fold_sibcall (void)
+{
+  timevar_push (TV_MACH_DEP);
+
+  unsigned int todo = 0;
+  basic_block bb;
+  rtx_insn *insn;
+  bool changed = false;
+
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      FOR_BB_INSNS (bb, insn)
+       {
+         if (!JUMP_P (insn))
+           continue;
+
+         rtx label = JUMP_LABEL (insn);
+         rtx sibcall_target;
+         rtx sibcall_note;
+         /* Used to get the REG_CALL_DECL note from sibcall .  */
+         const rtx_insn *sibcall_insn = nullptr;
+
+         if (INSN_CODE (insn) == CODE_FOR_jcc)
+           {
+             edge branch_edge = BRANCH_EDGE (bb);
+             basic_block branch_bb = branch_edge->dest;
+             sibcall_target = sibcall_only_bb (branch_bb,
+                                               &sibcall_insn,
+                                               &sibcall_note);
+             if (!sibcall_target)
+               continue;
+
+             if (!reg_eh_region_note_ok_p (insn, &sibcall_note))
+               {
+                 sibcall_insn = nullptr;
+                 continue;
+               }
+
+             /* Decrement the label use count.  */
+             LABEL_NUSES (label) -= 1;
+             if (LABEL_NUSES (label) == 0)
+               delete_insn (JUMP_LABEL_AS_INSN (insn));
+
+             rtx set = pc_set (insn);
+             rtx src = SET_SRC (set);
+
+             /* Change the jcc code label to the sibcall target
+                and update JUMP_LABEL.  */
+             XEXP (src, 1) = sibcall_target;
+             rtx_jump_insn *jump = as_a <rtx_jump_insn *> (insn);
+             jump->set_jump_target (sibcall_target);
+
+             /* Redirect the edge to the exit basic block.  */
+             redirect_edge_succ (branch_edge,
+                                 EXIT_BLOCK_PTR_FOR_FN (cfun));
+             branch_edge->flags |= EDGE_SIBCALL | EDGE_ABNORMAL;
+           }
+
+         if (sibcall_insn)
+           {
+             /* Copy the REG_CALL_DECL note so that get_call_fndecl
+                can get the callee ABI.  */
+             for (rtx note = REG_NOTES (sibcall_insn);
+                  note;
+                  note = XEXP (note, 1))
+               {
+                 reg_note kind = REG_NOTE_KIND (note);
+                 if (kind == REG_CALL_DECL)
+                   {
+                     add_reg_note (insn, kind, XEXP (note, 0));
+                     break;
+                   }
+               }
+
+             /* Copy the REG_EH_REGION note.  */
+             if (sibcall_note)
+               add_reg_note (insn, REG_EH_REGION,
+                             XEXP (sibcall_note, 0));
+
+             changed = true;
+           }
+       }
+    }
+
+  if (changed)
+    delete_unreachable_blocks ();
+
+  timevar_pop (TV_MACH_DEP);
+
+  return todo;
+}
+
+namespace {
+
+const pass_data pass_data_fold_sibcall =
+{
+  RTL_PASS, /* type */
+  "fold_tail", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_MACH_DEP, /* tv_id */
+  0, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_fold_sibcall : public rtl_opt_pass
+{
+public:
+  pass_fold_sibcall (gcc::context *ctxt)
+    : rtl_opt_pass (pass_data_fold_sibcall, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) final override
+    {
+      return optimize;
+    }
+
+  unsigned int execute (function *) final override
+    {
+      return fold_sibcall ();
+    }
+}; // class pass_remove_redundant_vector_load
+
+} // anon namespace
+
+rtl_opt_pass *
+make_pass_fold_sibcall (gcc::context *ctxt)
+{
+  return new pass_fold_sibcall (ctxt);
+}
+
 /* Convert legacy instructions that clobbers EFLAGS to APX_NF
    instructions when there are no flag set between a flag
    producer and user.  */
diff --git a/gcc/config/i386/i386-passes.def b/gcc/config/i386/i386-passes.def
index 06f0288b067..a4a3bbd36f5 100644
--- a/gcc/config/i386/i386-passes.def
+++ b/gcc/config/i386/i386-passes.def
@@ -38,3 +38,4 @@ along with GCC; see the file COPYING3.  If not see
   INSERT_PASS_AFTER (pass_late_combine, 1, pass_remove_redundant_vector_load);
   INSERT_PASS_AFTER (pass_late_combine, 1, pass_remove_partial_avx_dependency);
   INSERT_PASS_AFTER (pass_rtl_ifcvt, 1, pass_apx_nf_convert);
+  INSERT_PASS_BEFORE (pass_convert_to_eh_region_ranges, 1, pass_fold_sibcall);
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index e85b925704b..16055da0b6a 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -374,6 +374,7 @@ extern enum attr_cpu ix86_schedule;
 
 extern bool ix86_nopic_noplt_attribute_p (rtx call_op);
 extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op);
+extern const char * ix86_output_jcc_insn (rtx call_op);
 extern const char * ix86_output_indirect_jmp (rtx call_op);
 extern const char * ix86_output_function_return (bool long_p);
 extern const char * ix86_output_indirect_function_return (rtx ret_op);
@@ -430,6 +431,8 @@ extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
   (gcc::context *);
 extern rtl_opt_pass *make_pass_remove_redundant_vector_load
   (gcc::context *);
+extern rtl_opt_pass *make_pass_fold_sibcall
+  (gcc::context *);
 extern rtl_opt_pass *make_pass_apx_nf_convert (gcc::context *);
 extern rtl_opt_pass *make_pass_align_tight_loops (gcc::context *);
 
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 5ad47e19434..73918a5f5bc 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -17424,6 +17424,18 @@ ix86_output_call_insn (rtx_insn *insn, rtx call_op)
 
   return "";
 }
+
+/* Output the assembly for a conditional jump instruction.  */
+
+const char *
+ix86_output_jcc_insn (rtx call_op)
+{
+  if (LABEL_P (call_op))
+    return "%!%+j%C1\t%l0";
+  else
+    return "%!%+j%C1\t%P0";
+}
+
 
 /* Return a MEM corresponding to a stack slot with mode MODE.
    Allocate a new slot if necessary.
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 44ee94a3e41..39bc94678e3 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -19773,19 +19773,20 @@ (define_split
 
 ;; We ignore the overflow flag for signed branch instructions.
 
-(define_insn "*jcc"
+(define_insn "jcc"
   [(set (pc)
        (if_then_else (match_operator 1 "ix86_comparison_operator"
                                      [(reg FLAGS_REG) (const_int 0)])
-                     (label_ref (match_operand 0))
+              (match_operand 0 "symbol_label_operand")
                      (pc)))]
   ""
-  "%!%+j%C1\t%l0"
+  "* return ix86_output_jcc_insn (operands[0]);"
   [(set_attr "type" "ibr")
    (set_attr "modrm" "0")
    (set (attr "length")
        (if_then_else
-         (and (ge (minus (match_dup 0) (pc))
+         (and (match_test ("LABEL_P (operands[0])"))
+           (ge (minus (match_dup 0) (pc))
                   (const_int -126))
               (lt (minus (match_dup 0) (pc))
                   (const_int 128)))
diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md
index 4b23e18eaf4..197ee5be8a4 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -768,6 +768,10 @@ (define_predicate "sibcall_memory_operand"
   return false;
 })
 
+;; Return true when operand is a symbol or label reference.
+(define_predicate "symbol_label_operand"
+  (match_code "symbol_ref,label_ref"))
+
 ;; Return true if OP is a GOT memory operand.
 (define_predicate "GOT_memory_operand"
   (and (match_operand 0 "memory_operand")
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-1a.c 
b/gcc/testsuite/gcc.target/i386/pr47253-1a.c
new file mode 100644
index 00000000000..7983f27f116
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-1a.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-options "-O2 -fpic -fplt" } */
+/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
+/* { dg-final { check-function-bodies "**" "" "" { target "*-*-*" } {^\t?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, %edi
+**     je      bar@PLT
+**     ret
+**     .cfi_endproc
+**...
+*/
+
+extern void bar (void);
+
+void
+func (int c)
+{
+  if (c == 1)
+    bar();
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-1b.c 
b/gcc/testsuite/gcc.target/i386/pr47253-1b.c
new file mode 100644
index 00000000000..c2198e06fd6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-1b.c
@@ -0,0 +1,17 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, 4\(%esp\)
+**     je      bar
+**     ret
+**     .cfi_endproc
+**...
+*/
+
+#include "pr47253-1a.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-2a.c 
b/gcc/testsuite/gcc.target/i386/pr47253-2a.c
new file mode 100644
index 00000000000..641c0b87b56
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-2a.c
@@ -0,0 +1,29 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     testb   %dil, %dil
+**     je      f
+**     jmp     t
+**     .cfi_endproc
+**...
+*/
+
+#include <stdbool.h>
+
+extern void t(void);
+extern void f(void);
+ 
+void
+func(bool ok)
+{
+  if (ok)
+    t();
+  else
+    f();
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-2b.c 
b/gcc/testsuite/gcc.target/i386/pr47253-2b.c
new file mode 100644
index 00000000000..0096b0266fb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-2b.c
@@ -0,0 +1,17 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpb    \$0, 4\(%esp\)
+**     je      f
+**     jmp     t
+**     .cfi_endproc
+**...
+*/
+
+#include "pr47253-2a.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-3a.c 
b/gcc/testsuite/gcc.target/i386/pr47253-3a.c
new file mode 100644
index 00000000000..f920c95fb45
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-3a.c
@@ -0,0 +1,32 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, %edi
+**     je      bar
+**     cmpl    \$2, %edi
+**     je      foo
+**     jmp     \*func_p\(%rip\)
+**     .cfi_endproc
+**...
+*/
+
+extern void bar(void);
+extern void foo(void);
+extern void (*func_p) (void);
+
+void
+func (int c)
+{
+  if (c == 1)
+    bar();
+  else if (c == 2)
+    foo();
+  else
+    func_p ();
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-3b.c 
b/gcc/testsuite/gcc.target/i386/pr47253-3b.c
new file mode 100644
index 00000000000..254b0c9145d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-3b.c
@@ -0,0 +1,20 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     movl    4\(%esp\), %eax
+**     cmpl    \$1, %eax
+**     je      bar
+**     cmpl    \$2, %eax
+**     je      foo
+**     jmp     \*func_p
+**     .cfi_endproc
+**...
+*/
+
+#include "pr47253-3a.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-3c.c 
b/gcc/testsuite/gcc.target/i386/pr47253-3c.c
new file mode 100644
index 00000000000..4b67b19dbd5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-3c.c
@@ -0,0 +1,20 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, %edi
+**     je      bar
+**     cmpl    \$2, %edi
+**     je      foo
+**     movl    func_p\(%rip\), %eax
+**     jmp     \*%rax
+**     .cfi_endproc
+**...
+*/
+
+#include "pr47253-3a.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-4a.c 
b/gcc/testsuite/gcc.target/i386/pr47253-4a.c
new file mode 100644
index 00000000000..1862d6297ac
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-4a.c
@@ -0,0 +1,26 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, %edi
+**     je      bar
+**     movl    \$2, %eax
+**     ret
+**     .cfi_endproc
+**...
+*/
+
+extern int bar(void);
+
+int
+func (int c)
+{
+  if (c == 1)
+    return bar();
+  return 2;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-4b.c 
b/gcc/testsuite/gcc.target/i386/pr47253-4b.c
new file mode 100644
index 00000000000..c489a77b15e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-4b.c
@@ -0,0 +1,18 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, 4\(%esp\)
+**     je      bar
+**     movl    \$2, %eax
+**     ret
+**     .cfi_endproc
+**...
+*/
+
+#include "pr47253-4a.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-5.c 
b/gcc/testsuite/gcc.target/i386/pr47253-5.c
new file mode 100644
index 00000000000..cf284d0659d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt" } */
+
+extern void bar (void);
+
+void
+func (int c)
+{
+  if (c == 1)
+    bar();
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]bar" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } 
} } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-6.c 
b/gcc/testsuite/gcc.target/i386/pr47253-6.c
new file mode 100644
index 00000000000..e7aa9f25249
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-6.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-plt" } */
+
+extern void bar (void);
+
+void
+func (int c)
+{
+  if (c == 1)
+    bar();
+}
+
+/* { dg-final { scan-assembler-not "jmp\[ \t\]bar" } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOTPCREL" { target { ! ia32 } 
} } } */
+/* { dg-final { scan-assembler "jmp\[ \t\]*.bar@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-7a.c 
b/gcc/testsuite/gcc.target/i386/pr47253-7a.c
new file mode 100644
index 00000000000..6e05ad3ec46
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-7a.c
@@ -0,0 +1,52 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, %edi
+**     je      bar
+**     cmpl    \$2, %edi
+**     je      foo
+**     cmpl    \$3, %edi
+**     je      .L12
+**     cmpl    \$5, %edi
+**     je      .L13
+**     ret
+**...
+**.L12:
+**     jmp     \*func_p\(%rip\)
+**...
+**.L13:
+**     subq    \$8, %rsp
+**     .cfi_def_cfa_offset 16
+**     call    \*func_p\(%rip\)
+**     addq    \$8, %rsp
+**     .cfi_def_cfa_offset 8
+**     jmp     foo
+**     .cfi_endproc
+**...
+*/
+
+extern void bar(void);
+extern void foo(void);
+extern void (*func_p) (void);
+
+void
+func (int c)
+{
+  if (c == 1)
+    bar();
+  else if (c == 2)
+    foo();
+  else if (c == 3)
+    func_p ();
+  else if (c == 5)
+    {
+      func_p ();
+      foo ();
+    }
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-7b.c 
b/gcc/testsuite/gcc.target/i386/pr47253-7b.c
new file mode 100644
index 00000000000..fa58b5e5016
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-7b.c
@@ -0,0 +1,36 @@
+/* { 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?\.}  
} } */
+
+/*
+**func:
+**.LFB0:
+**     .cfi_startproc
+**     cmpl    \$1, %edi
+**     je      bar
+**     cmpl    \$2, %edi
+**     je      foo
+**     cmpl    \$3, %edi
+**     je      .L12
+**     cmpl    \$5, %edi
+**     je      .L13
+**     ret
+**...
+**.L12:
+**     movl    func_p\(%rip\), %eax
+**     jmp     \*%rax
+**...
+**.L13:
+**     subl    \$8, %esp
+**     .cfi_def_cfa_offset 16
+**     movl    func_p\(%rip\), %eax
+**     call    \*%rax
+**     addl    \$8, %esp
+**     .cfi_def_cfa_offset 8
+**     jmp     foo
+**     .cfi_endproc
+**...
+*/
+
+#include "pr47253-7a.c"
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-8.c 
b/gcc/testsuite/gcc.target/i386/pr47253-8.c
new file mode 100644
index 00000000000..d12452d15c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-8.c
@@ -0,0 +1,74 @@
+/* { 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?\.}  
} } */
+
+/*
+**real_trunc:
+**.LFB2:
+**...
+**     pushq   %rbp
+**...
+**     movl    %esi, %ebp
+**     movq    %rdx, %rsi
+**     pushq   %rbx
+**...
+**     movq    %rdi, %rbx
+**...
+**     call    do_fix_trunc
+**...
+**     movq    %rbx, %rdx
+**     movl    %ebp, %esi
+**     movq    %rbx, %rdi
+**     popq    %rbx
+**     .cfi_def_cfa_offset 16
+**     popq    %rbp
+**     .cfi_def_cfa_offset 8
+**     jmp     real_convert
+**     .cfi_endproc
+**...
+*/
+
+struct real_value
+{
+  unsigned int cl : 2;
+  unsigned int decimal : 1;
+  unsigned int sign : 1;
+  unsigned int sig[3];
+};
+
+extern void decimal_do_fix_trunc (struct real_value *,
+                                  const struct real_value *);
+extern void real_convert (struct real_value *, int,
+                         const struct real_value *);
+
+static inline void
+get_zero (struct real_value *r, int sign)
+{
+  __builtin_memset (r, 0, sizeof (*r));
+  r->sign = sign;
+}
+__attribute__ ((noinline)) static void
+do_fix_trunc (struct real_value *r, const struct real_value *a)
+{
+  *r = *a;
+  if (r->cl)
+    {
+      if (r->decimal)
+        {
+          decimal_do_fix_trunc (r, a);
+          return;
+        }
+      get_zero (r, r->sign);
+    }
+}
+
+void
+real_trunc (struct real_value *r, int fmt, const struct real_value *x)
+{
+  do_fix_trunc (r, x);
+  if (fmt)
+    real_convert (r, fmt, r);
+}
+
+/* { dg-final { scan-assembler "jne\[ \t\]decimal_do_fix_trunc" } } */
-- 
2.49.0

Reply via email to