From b3a2269c7884378a9afd394ac7e669aab0443b57 Mon Sep 17 00:00:00 2001 From: hjl Date: Mon, 26 Feb 2018 15:29:30 +0000 Subject: [PATCH 11/12] 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. Revision 257992 removed the bool argument from ix86_output_indirect_jmp. Update comments to reflect it. Tested on i686 and x86-64. Backport from mainline * config/i386/i386.c (ix86_output_indirect_jmp): Update comments. 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/ Backport from mainline 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. Upstream-Status: Pending Signed-off-by: Juro Bystricky --- gcc/config/i386/i386-protos.h | 4 +- gcc/config/i386/i386.c | 127 +++++++++++++++++++++++---- gcc/config/i386/i386.md | 11 ++- 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 +++++++++ 8 files changed, 222 insertions(+), 20 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/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 620d70e..c7a0ccb5 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -311,8 +311,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, enum machine_mode mode); diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 66502ee..21c3c18 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -11080,6 +11080,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 @@ -11090,12 +11096,13 @@ static void indirect_thunk_name (char name[32], unsigned int regno, bool need_bnd_p, bool ret_p) { - if (regno != INVALID_REGNUM && ret_p) + if (regno != INVALID_REGNUM && regno != CX_REG && ret_p) gcc_unreachable (); if (USE_HIDDEN_LINKONCE) { const char *bnd = need_bnd_p ? "_bnd" : ""; + const char *ret = ret_p ? "return" : "indirect"; if (regno != INVALID_REGNUM) { const char *reg_prefix; @@ -11103,14 +11110,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", - bnd, reg_prefix, reg_names[regno]); + sprintf (name, "__x86_%s_thunk%s_%s%s", + ret, bnd, reg_prefix, reg_names[regno]); } else - { - const char *ret = ret_p ? "return" : "indirect"; - sprintf (name, "__x86_%s_thunk%s", ret, bnd); - } + sprintf (name, "__x86_%s_thunk%s", ret, bnd); } else { @@ -11274,9 +11278,23 @@ output_indirect_thunk_function (bool need_bnd_p, unsigned int regno) 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_bnd_p) + 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_bnd_p, true); @@ -28019,18 +28037,17 @@ 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. */ + +/* Output indirect jump. CALL_OP is the jump target. */ 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); @@ -28081,6 +28098,86 @@ 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]; + bool need_bnd_p = ix86_bnd_prefixed_insn_p (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_bnd_p, true); + if (need_bnd_p) + { + 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_bnd_p, 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 05a88ff..857466a 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -11813,7 +11813,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)") @@ -11868,7 +11868,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)") @@ -12520,11 +12520,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") @@ -12535,7 +12538,7 @@ [(simple_return) (use (match_operand:SI 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/gcc.target/i386/ret-thunk-22.c b/gcc/testsuite/gcc.target/i386/ret-thunk-22.c new file mode 100644 index 0000000..89e086d --- /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 0000000..43f0cca --- /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 0000000..8729e35 --- /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 0000000..f73553c --- /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 0000000..9144e98 --- /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.7.4