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 <[email protected]>
---
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