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