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))

Searching backward from exit basic blocks with only sibcalls, check the
last instruction in each predecessor.  If the last instruction is a
conditional jump and its target is the exit block, change the conditional
jump target to the sibcall target, decrement the destination basic block
entry label use count, redirect the edge to the exit basic block and call
delete_unreachable_blocks to delete the unreachable basic blocks.  Repeat
it until there is no conditional jump to update.

gcc/

        PR target/47253
        * i386/i386-features.cc: Include "cfgcleanup.h".
        (sibcall_only_bb): New.
        (reg_eh_region_note_ok_p): Likewise.
        (fold_one_sibcall): 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           | 237 +++++++++++++++++++++
 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-10.c |  15 ++
 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 +++++++
 gcc/testsuite/gcc.target/i386/pr47253-9.c  |  22 ++
 22 files changed, 694 insertions(+), 4 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-10.c
 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
 create mode 100644 gcc/testsuite/gcc.target/i386/pr47253-9.c

diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index 1ba5ac4faa4..b58fd79e68d 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"
@@ -3564,6 +3565,242 @@ 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)
+{
+  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))
+       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;
+}
+
+/* Return true if a direct sibcall has been folded into a conditional
+   jump.  */
+
+static bool
+fold_one_sibcall (void)
+{
+  rtx_insn *insn;
+  edge e;
+  edge_iterator ei;
+
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
+    {
+      /* Search backward from basic blocks with only sibcalls.  */
+      basic_block exit_bb = e->src;
+      insn = BB_END (exit_bb);
+      if (!insn || !CALL_P (insn) || !SIBLING_CALL_P (insn))
+       continue;
+
+      rtx sibcall_target;
+      rtx sibcall_note;
+      /* Used to get the REG_CALL_DECL note from sibcall .  */
+      const rtx_insn *sibcall_insn = nullptr;
+
+      sibcall_target = sibcall_only_bb (exit_bb,
+                                       &sibcall_insn,
+                                       &sibcall_note);
+      /* Skip this if the exit block has more than just a sibcall.  */
+      if (!sibcall_target)
+       continue;
+
+      edge branch_edge;
+      edge_iterator branch_edgei;
+
+      /* Check for conditional jump to this block.  */
+      FOR_EACH_EDGE (branch_edge, branch_edgei, exit_bb->preds)
+       {
+         basic_block bb = branch_edge->src;
+         insn = BB_END (bb);
+         if (!insn || !JUMP_P (insn))
+           continue;
+
+         rtx label = JUMP_LABEL (insn);
+
+         bool changed = false;
+
+         if (INSN_CODE (insn) == CODE_FOR_jcc)
+           {
+             /* Skip the conditional sibcall.  */
+             if (!LABEL_P (label))
+               continue;
+
+             /* Skip this edge if it doesn't lead to the sibcall.  */
+             basic_block label_bb = BLOCK_FOR_INSN (label);
+             if (label_bb != exit_bb)
+               continue;
+
+             if (!reg_eh_region_note_ok_p (insn, &sibcall_note))
+               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;
+           }
+         else
+           continue;
+
+         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 ();
+             /* Can't use FOR_EACH_EDGE any more since some edges have
+                been deleted.  */
+             return true;
+           }
+       }
+    }
+
+  return false;
+}
+
+/* Fold direct sibcall.  */
+
+static unsigned int
+fold_sibcall (void)
+{
+  timevar_push (TV_MACH_DEP);
+
+  /* Fold one conditional jump at a time repeatedly until there is
+     conditional jump to fold.  */
+  while (fold_one_sibcall ())
+    ;
+
+  timevar_pop (TV_MACH_DEP);
+
+  return 0;
+}
+
+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 flag_optimize_sibling_calls != 0 && dbg_cnt (tail_call);;
+    }
+
+  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 fd36ea802c0..5a9d1431028 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -17508,6 +17508,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 d67148034aa..d9277a97af7 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -19775,19 +19775,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 fbf2dd9fc51..0aac61ec929 100644
--- a/gcc/config/i386/predicates.md
+++ b/gcc/config/i386/predicates.md
@@ -769,6 +769,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-10.c 
b/gcc/testsuite/gcc.target/i386/pr47253-10.c
new file mode 100644
index 00000000000..a624e6db6ce
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-10.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic -fplt" } */
+
+extern void *func (int);
+extern void foo (void)
+      __attribute__ ((__noreturn__)) __attribute__ ((__cold__));
+void *
+bar (int size, int f)
+{
+  if (f)
+    foo ();
+  return func (size);
+}
+
+/* { dg-final { scan-assembler-not "je\[ \t\]+_?func" } } */
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" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr47253-9.c 
b/gcc/testsuite/gcc.target/i386/pr47253-9.c
new file mode 100644
index 00000000000..6108071cd49
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr47253-9.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic -fplt" } */
+
+extern void yyfree (void *);
+struct yy_buffer_state
+{
+  void *yy_ch_buf;
+  int yy_is_our_buffer;
+};
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+void
+yy_delete_buffer (YY_BUFFER_STATE b)
+{
+  if (b->yy_is_our_buffer)
+    yyfree (b->yy_ch_buf);
+
+  yyfree (b);
+}
+
+/* { dg-final { scan-assembler-not "je\[ \t\]+_?yyfree" } } */
-- 
2.49.0

Reply via email to