From b9d676b3c0f53504d48bc3f0fe3fec6dbddaed6e Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Mon, 26 Feb 2018 15:29:30 +0000 Subject: [PATCH] i386: Update -mfunction-return= for return with pop When -mfunction-return= is used, simple_return_pop_internal should pop return address into ECX register, adjust stack by bytes to pop from stack and jump to the return thunk via ECX register. Tested on i686 and x86-64. PR target/84530 * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove the bool argument. (ix86_output_indirect_function_return): New prototype. (ix86_split_simple_return_pop_internal): Likewise. * config/i386/i386.c (indirect_return_via_cx): New. (indirect_return_via_cx_bnd): Likewise. (indirect_thunk_name): Handle return va CX_REG. (output_indirect_thunk_function): Create alias for __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd. (ix86_output_indirect_jmp): Remove the bool argument. (ix86_output_indirect_function_return): New function. (ix86_split_simple_return_pop_internal): Likewise. * config/i386/i386.md (*indirect_jump): Don't pass false to ix86_output_indirect_jmp. (*tablejump_1): Likewise. (simple_return_pop_internal): Change it to define_insn_and_split. Call ix86_split_simple_return_pop_internal to split it for -mfunction-return=. (simple_return_indirect_internal): Call ix86_output_indirect_function_return instead of ix86_output_indirect_jmp. gcc/testsuite/ PR target/84530 * gcc.target/i386/ret-thunk-22.c: New test. * gcc.target/i386/ret-thunk-23.c: Likewise. * gcc.target/i386/ret-thunk-24.c: Likewise. * gcc.target/i386/ret-thunk-25.c: Likewise. * gcc.target/i386/ret-thunk-26.c: Likewise. From-SVN: r257992 --- gcc/ChangeLog | 25 ++++ gcc/config/i386/i386-protos.h | 4 +- gcc/config/i386/i386.c | 126 +++++++++++++++++-- gcc/config/i386/i386.md | 11 +- gcc/testsuite/ChangeLog | 9 ++ gcc/testsuite/gcc.target/i386/ret-thunk-22.c | 15 +++ gcc/testsuite/gcc.target/i386/ret-thunk-23.c | 15 +++ gcc/testsuite/gcc.target/i386/ret-thunk-24.c | 15 +++ gcc/testsuite/gcc.target/i386/ret-thunk-25.c | 15 +++ gcc/testsuite/gcc.target/i386/ret-thunk-26.c | 40 ++++++ 10 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-22.c create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-23.c create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-24.c create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-25.c create mode 100644 gcc/testsuite/gcc.target/i386/ret-thunk-26.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index ebbc25b6c1d..d2ca7b0c5e4 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,28 @@ +2018-02-26 H.J. Lu + + PR target/84530 + * config/i386/i386-protos.h (ix86_output_indirect_jmp): Remove + the bool argument. + (ix86_output_indirect_function_return): New prototype. + (ix86_split_simple_return_pop_internal): Likewise. + * config/i386/i386.c (indirect_return_via_cx): New. + (indirect_return_via_cx_bnd): Likewise. + (indirect_thunk_name): Handle return va CX_REG. + (output_indirect_thunk_function): Create alias for + __x86_return_thunk_[re]cx and __x86_return_thunk_[re]cx_bnd. + (ix86_output_indirect_jmp): Remove the bool argument. + (ix86_output_indirect_function_return): New function. + (ix86_split_simple_return_pop_internal): Likewise. + * config/i386/i386.md (*indirect_jump): Don't pass false + to ix86_output_indirect_jmp. + (*tablejump_1): Likewise. + (simple_return_pop_internal): Change it to define_insn_and_split. + Call ix86_split_simple_return_pop_internal to split it for + -mfunction-return=. + (simple_return_indirect_internal): Call + ix86_output_indirect_function_return instead of + ix86_output_indirect_jmp. + 2018-02-26 Jakub Jelinek PR bootstrap/84405 diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index fb86f00b3a6..79b96c0f80b 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -305,8 +305,10 @@ extern enum attr_cpu ix86_schedule; #endif extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op); -extern const char * ix86_output_indirect_jmp (rtx call_op, bool ret_p); +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); +extern void ix86_split_simple_return_pop_internal (rtx); extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load, machine_mode mode); extern int ix86_min_insn_size (rtx_insn *); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 640c5921863..98075ad42c7 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -10812,6 +10812,12 @@ static int indirect_thunks_used; by call and return thunks functions with the BND prefix. */ static int indirect_thunks_bnd_used; +/* True if return thunk function via CX is needed. */ +static bool indirect_return_via_cx; +/* True if return thunk function via CX with the BND prefix is + needed. */ +static bool indirect_return_via_cx_bnd; + #ifndef INDIRECT_LABEL # define INDIRECT_LABEL "LIND" #endif @@ -10852,7 +10858,7 @@ indirect_thunk_name (char name[32], unsigned int regno, enum indirect_thunk_prefix need_prefix, bool ret_p) { - if (regno != INVALID_REGNUM && ret_p) + if (regno != INVALID_REGNUM && regno != CX_REG && ret_p) gcc_unreachable (); if (USE_HIDDEN_LINKONCE) @@ -10872,6 +10878,8 @@ indirect_thunk_name (char name[32], unsigned int regno, else prefix = ""; + const char *ret = ret_p ? "return" : "indirect"; + if (regno != INVALID_REGNUM) { const char *reg_prefix; @@ -10879,14 +10887,11 @@ indirect_thunk_name (char name[32], unsigned int regno, reg_prefix = TARGET_64BIT ? "r" : "e"; else reg_prefix = ""; - sprintf (name, "__x86_indirect_thunk%s_%s%s", - prefix, reg_prefix, reg_names[regno]); + sprintf (name, "__x86_%s_thunk%s_%s%s", + ret, prefix, reg_prefix, reg_names[regno]); } else - { - const char *ret = ret_p ? "return" : "indirect"; - sprintf (name, "__x86_%s_thunk%s", ret, prefix); - } + sprintf (name, "__x86_%s_thunk%s", ret, prefix); } else { @@ -11055,9 +11060,23 @@ output_indirect_thunk_function (enum indirect_thunk_prefix need_prefix, ASM_OUTPUT_LABEL (asm_out_file, name); } + /* Create alias for __x86_return_thunk/__x86_return_thunk_bnd or + __x86_return_thunk_ecx/__x86_return_thunk_ecx_bnd. */ + bool need_alias; if (regno == INVALID_REGNUM) + need_alias = true; + else if (regno == CX_REG) + { + if (need_prefix == indirect_thunk_prefix_bnd) + need_alias = indirect_return_via_cx_bnd; + else + need_alias = indirect_return_via_cx; + } + else + need_alias = false; + + if (need_alias) { - /* Create alias for __x86.return_thunk/__x86.return_thunk_bnd. */ char alias[32]; indirect_thunk_name (alias, regno, need_prefix, true); @@ -28979,18 +28998,18 @@ ix86_output_indirect_branch (rtx call_op, const char *xasm, else ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p); } + /* Output indirect jump. CALL_OP is the jump target. Jump is a function return if RET_P is true. */ const char * -ix86_output_indirect_jmp (rtx call_op, bool ret_p) +ix86_output_indirect_jmp (rtx call_op) { if (cfun->machine->indirect_branch_type != indirect_branch_keep) { - /* We can't have red-zone if this isn't a function return since - "call" in the indirect thunk pushes the return address onto - stack, destroying red-zone. */ - if (!ret_p && ix86_red_zone_size != 0) + /* We can't have red-zone since "call" in the indirect thunk + pushes the return address onto stack, destroying red-zone. */ + if (ix86_red_zone_size != 0) gcc_unreachable (); ix86_output_indirect_branch (call_op, "%0", true); @@ -29042,6 +29061,87 @@ ix86_output_function_return (bool long_p) return "rep%; ret"; } +/* Output indirect function return. RET_OP is the function return + target. */ + +const char * +ix86_output_indirect_function_return (rtx ret_op) +{ + if (cfun->machine->function_return_type != indirect_branch_keep) + { + char thunk_name[32]; + enum indirect_thunk_prefix need_prefix + = indirect_thunk_need_prefix (current_output_insn); + unsigned int regno = REGNO (ret_op); + gcc_assert (regno == CX_REG); + + if (cfun->machine->function_return_type + != indirect_branch_thunk_inline) + { + bool need_thunk = (cfun->machine->function_return_type + == indirect_branch_thunk); + indirect_thunk_name (thunk_name, regno, need_prefix, true); + if (need_prefix == indirect_thunk_prefix_bnd) + { + if (need_thunk) + { + indirect_return_via_cx_bnd = true; + indirect_thunks_bnd_used |= 1 << CX_REG; + } + fprintf (asm_out_file, "\tbnd jmp\t%s\n", thunk_name); + } + else + { + if (need_thunk) + { + indirect_return_via_cx = true; + indirect_thunks_used |= 1 << CX_REG; + } + fprintf (asm_out_file, "\tjmp\t%s\n", thunk_name); + } + } + else + output_indirect_thunk (need_prefix, regno); + + return ""; + } + else + return "%!jmp\t%A0"; +} + +/* Split simple return with popping POPC bytes from stack to indirect + branch with stack adjustment . */ + +void +ix86_split_simple_return_pop_internal (rtx popc) +{ + struct machine_function *m = cfun->machine; + rtx ecx = gen_rtx_REG (SImode, CX_REG); + rtx_insn *insn; + + /* There is no "pascal" calling convention in any 64bit ABI. */ + gcc_assert (!TARGET_64BIT); + + insn = emit_insn (gen_pop (ecx)); + m->fs.cfa_offset -= UNITS_PER_WORD; + m->fs.sp_offset -= UNITS_PER_WORD; + + rtx x = plus_constant (Pmode, stack_pointer_rtx, UNITS_PER_WORD); + x = gen_rtx_SET (stack_pointer_rtx, x); + add_reg_note (insn, REG_CFA_ADJUST_CFA, x); + add_reg_note (insn, REG_CFA_REGISTER, gen_rtx_SET (ecx, pc_rtx)); + RTX_FRAME_RELATED_P (insn) = 1; + + x = gen_rtx_PLUS (Pmode, stack_pointer_rtx, popc); + x = gen_rtx_SET (stack_pointer_rtx, x); + insn = emit_insn (x); + add_reg_note (insn, REG_CFA_ADJUST_CFA, x); + RTX_FRAME_RELATED_P (insn) = 1; + + /* Now return address is in ECX. */ + emit_jump_insn (gen_simple_return_indirect_internal (ecx)); +} + /* Output the assembly for a call instruction. */ const char * diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 3998053a506..f271278ed58 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -12319,7 +12319,7 @@ (define_insn "*indirect_jump" [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))] "" - "* return ix86_output_indirect_jmp (operands[0], false);" + "* return ix86_output_indirect_jmp (operands[0]);" [(set (attr "type") (if_then_else (match_test "(cfun->machine->indirect_branch_type != indirect_branch_keep)") @@ -12374,7 +12374,7 @@ [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw")) (use (label_ref (match_operand 1)))] "" - "* return ix86_output_indirect_jmp (operands[0], false);" + "* return ix86_output_indirect_jmp (operands[0]);" [(set (attr "type") (if_then_else (match_test "(cfun->machine->indirect_branch_type != indirect_branch_keep)") @@ -13097,11 +13097,14 @@ (set_attr "prefix_rep" "1") (set_attr "modrm" "0")]) -(define_insn "simple_return_pop_internal" +(define_insn_and_split "simple_return_pop_internal" [(simple_return) (use (match_operand:SI 0 "const_int_operand"))] "reload_completed" "%!ret\t%0" + "&& cfun->machine->function_return_type != indirect_branch_keep" + [(const_int 0)] + "ix86_split_simple_return_pop_internal (operands[0]); DONE;" [(set_attr "length" "3") (set_attr "atom_unit" "jeu") (set_attr "length_immediate" "2") @@ -13112,7 +13115,7 @@ [(simple_return) (use (match_operand 0 "register_operand" "r"))] "reload_completed" - "* return ix86_output_indirect_jmp (operands[0], true);" + "* return ix86_output_indirect_function_return (operands[0]);" [(set (attr "type") (if_then_else (match_test "(cfun->machine->indirect_branch_type != indirect_branch_keep)") diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d53ab4d3c8f..bccf1ba2d8f 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2018-02-26 H.J. Lu + + PR target/84530 + * gcc.target/i386/ret-thunk-22.c: New test. + * gcc.target/i386/ret-thunk-23.c: Likewise. + * gcc.target/i386/ret-thunk-24.c: Likewise. + * gcc.target/i386/ret-thunk-25.c: Likewise. + * gcc.target/i386/ret-thunk-26.c: Likewise. + 2018-02-26 Paolo Carlini PR c++/84533 diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-22.c b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c new file mode 100644 index 00000000000..89e086de97b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c @@ -0,0 +1,15 @@ +/* PR target/r84530 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mfunction-return=thunk" } */ + +struct s { _Complex unsigned short x; }; +struct s gs = { 100 + 200i }; +struct s __attribute__((noinline)) foo (void) { return gs; } + +/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */ +/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_ecx" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tpause} } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-23.c b/gcc/testsuite/gcc.target/i386/ret-thunk-23.c new file mode 100644 index 00000000000..43f0ccaa854 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-23.c @@ -0,0 +1,15 @@ +/* PR target/r84530 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mfunction-return=thunk-extern" } */ + +struct s { _Complex unsigned short x; }; +struct s gs = { 100 + 200i }; +struct s __attribute__((noinline)) foo (void) { return gs; } + +/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */ +/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_ecx" } } */ +/* { dg-final { scan-assembler-not "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler-not "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler-not {\tpause} } } */ +/* { dg-final { scan-assembler-not {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-24.c b/gcc/testsuite/gcc.target/i386/ret-thunk-24.c new file mode 100644 index 00000000000..8729e35147e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-24.c @@ -0,0 +1,15 @@ +/* PR target/r84530 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mfunction-return=thunk-inline" } */ + +struct s { _Complex unsigned short x; }; +struct s gs = { 100 + 200i }; +struct s __attribute__((noinline)) foo (void) { return gs; } + +/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */ +/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */ +/* { dg-final { scan-assembler-not "jmp\[ \t\]*__x86_return_thunk_ecx" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tpause} } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-25.c b/gcc/testsuite/gcc.target/i386/ret-thunk-25.c new file mode 100644 index 00000000000..f73553c9a9f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-25.c @@ -0,0 +1,15 @@ +/* PR target/r84530 */ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2 -mfunction-return=thunk -fcheck-pointer-bounds -mmpx -fno-pic" } */ + +struct s { _Complex unsigned short x; }; +struct s gs = { 100 + 200i }; +struct s __attribute__((noinline)) foo (void) { return gs; } + +/* { dg-final { scan-assembler-times "popl\[\\t \]*%ecx" 1 } } */ +/* { dg-final { scan-assembler "lea\[l\]?\[\\t \]*4\\(%esp\\), %esp" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*__x86_return_thunk_bnd_ecx" } } */ +/* { dg-final { scan-assembler "jmp\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler "call\[ \t\]*\.LIND" } } */ +/* { dg-final { scan-assembler {\tpause} } } */ +/* { dg-final { scan-assembler {\tlfence} } } */ diff --git a/gcc/testsuite/gcc.target/i386/ret-thunk-26.c b/gcc/testsuite/gcc.target/i386/ret-thunk-26.c new file mode 100644 index 00000000000..9144e988735 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/ret-thunk-26.c @@ -0,0 +1,40 @@ +/* PR target/r84530 */ +/* { dg-do run } */ +/* { dg-options "-Os -mfunction-return=thunk" } */ + +struct S { int i; }; +__attribute__((const, noinline, noclone)) +struct S foo (int x) +{ + struct S s; + s.i = x; + return s; +} + +int a[2048], b[2048], c[2048], d[2048]; +struct S e[2048]; + +__attribute__((noinline, noclone)) void +bar (void) +{ + int i; + for (i = 0; i < 1024; i++) + { + e[i] = foo (i); + a[i+2] = a[i] + a[i+1]; + b[10] = b[10] + i; + c[i] = c[2047 - i]; + d[i] = d[i + 1]; + } +} + +int +main () +{ + int i; + bar (); + for (i = 0; i < 1024; i++) + if (e[i].i != i) + __builtin_abort (); + return 0; +} -- 2.30.2