Commit b9d676b3 by H.J. Lu Committed by H.J. Lu

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
parent 98c26210
2018-02-26 H.J. Lu <hongjiu.lu@intel.com>
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 <jakub@redhat.com> 2018-02-26 Jakub Jelinek <jakub@redhat.com>
PR bootstrap/84405 PR bootstrap/84405
......
...@@ -305,8 +305,10 @@ extern enum attr_cpu ix86_schedule; ...@@ -305,8 +305,10 @@ extern enum attr_cpu ix86_schedule;
#endif #endif
extern const char * ix86_output_call_insn (rtx_insn *insn, rtx call_op); 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_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, extern bool ix86_operands_ok_for_move_multiple (rtx *operands, bool load,
machine_mode mode); machine_mode mode);
extern int ix86_min_insn_size (rtx_insn *); extern int ix86_min_insn_size (rtx_insn *);
......
...@@ -10812,6 +10812,12 @@ static int indirect_thunks_used; ...@@ -10812,6 +10812,12 @@ static int indirect_thunks_used;
by call and return thunks functions with the BND prefix. */ by call and return thunks functions with the BND prefix. */
static int indirect_thunks_bnd_used; 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 #ifndef INDIRECT_LABEL
# define INDIRECT_LABEL "LIND" # define INDIRECT_LABEL "LIND"
#endif #endif
...@@ -10852,7 +10858,7 @@ indirect_thunk_name (char name[32], unsigned int regno, ...@@ -10852,7 +10858,7 @@ indirect_thunk_name (char name[32], unsigned int regno,
enum indirect_thunk_prefix need_prefix, enum indirect_thunk_prefix need_prefix,
bool ret_p) bool ret_p)
{ {
if (regno != INVALID_REGNUM && ret_p) if (regno != INVALID_REGNUM && regno != CX_REG && ret_p)
gcc_unreachable (); gcc_unreachable ();
if (USE_HIDDEN_LINKONCE) if (USE_HIDDEN_LINKONCE)
...@@ -10872,6 +10878,8 @@ indirect_thunk_name (char name[32], unsigned int regno, ...@@ -10872,6 +10878,8 @@ indirect_thunk_name (char name[32], unsigned int regno,
else else
prefix = ""; prefix = "";
const char *ret = ret_p ? "return" : "indirect";
if (regno != INVALID_REGNUM) if (regno != INVALID_REGNUM)
{ {
const char *reg_prefix; const char *reg_prefix;
...@@ -10879,14 +10887,11 @@ indirect_thunk_name (char name[32], unsigned int regno, ...@@ -10879,14 +10887,11 @@ indirect_thunk_name (char name[32], unsigned int regno,
reg_prefix = TARGET_64BIT ? "r" : "e"; reg_prefix = TARGET_64BIT ? "r" : "e";
else else
reg_prefix = ""; reg_prefix = "";
sprintf (name, "__x86_indirect_thunk%s_%s%s", sprintf (name, "__x86_%s_thunk%s_%s%s",
prefix, reg_prefix, reg_names[regno]); ret, prefix, reg_prefix, reg_names[regno]);
} }
else else
{ sprintf (name, "__x86_%s_thunk%s", ret, prefix);
const char *ret = ret_p ? "return" : "indirect";
sprintf (name, "__x86_%s_thunk%s", ret, prefix);
}
} }
else else
{ {
...@@ -11055,9 +11060,23 @@ output_indirect_thunk_function (enum indirect_thunk_prefix need_prefix, ...@@ -11055,9 +11060,23 @@ output_indirect_thunk_function (enum indirect_thunk_prefix need_prefix,
ASM_OUTPUT_LABEL (asm_out_file, name); 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) 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]; char alias[32];
indirect_thunk_name (alias, regno, need_prefix, true); indirect_thunk_name (alias, regno, need_prefix, true);
...@@ -28979,18 +28998,18 @@ ix86_output_indirect_branch (rtx call_op, const char *xasm, ...@@ -28979,18 +28998,18 @@ ix86_output_indirect_branch (rtx call_op, const char *xasm,
else else
ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p); ix86_output_indirect_branch_via_push (call_op, xasm, sibcall_p);
} }
/* Output indirect jump. CALL_OP is the jump target. Jump is a /* Output indirect jump. CALL_OP is the jump target. Jump is a
function return if RET_P is true. */ function return if RET_P is true. */
const char * 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) if (cfun->machine->indirect_branch_type != indirect_branch_keep)
{ {
/* We can't have red-zone if this isn't a function return since /* We can't have red-zone since "call" in the indirect thunk
"call" in the indirect thunk pushes the return address onto pushes the return address onto stack, destroying red-zone. */
stack, destroying red-zone. */ if (ix86_red_zone_size != 0)
if (!ret_p && ix86_red_zone_size != 0)
gcc_unreachable (); gcc_unreachable ();
ix86_output_indirect_branch (call_op, "%0", true); ix86_output_indirect_branch (call_op, "%0", true);
...@@ -29042,6 +29061,87 @@ ix86_output_function_return (bool long_p) ...@@ -29042,6 +29061,87 @@ ix86_output_function_return (bool long_p)
return "rep%; ret"; 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. */ /* Output the assembly for a call instruction. */
const char * const char *
...@@ -12319,7 +12319,7 @@ ...@@ -12319,7 +12319,7 @@
(define_insn "*indirect_jump" (define_insn "*indirect_jump"
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))] [(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") [(set (attr "type")
(if_then_else (match_test "(cfun->machine->indirect_branch_type (if_then_else (match_test "(cfun->machine->indirect_branch_type
!= indirect_branch_keep)") != indirect_branch_keep)")
...@@ -12374,7 +12374,7 @@ ...@@ -12374,7 +12374,7 @@
[(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw")) [(set (pc) (match_operand:W 0 "indirect_branch_operand" "rBw"))
(use (label_ref (match_operand 1)))] (use (label_ref (match_operand 1)))]
"" ""
"* return ix86_output_indirect_jmp (operands[0], false);" "* return ix86_output_indirect_jmp (operands[0]);"
[(set (attr "type") [(set (attr "type")
(if_then_else (match_test "(cfun->machine->indirect_branch_type (if_then_else (match_test "(cfun->machine->indirect_branch_type
!= indirect_branch_keep)") != indirect_branch_keep)")
...@@ -13097,11 +13097,14 @@ ...@@ -13097,11 +13097,14 @@
(set_attr "prefix_rep" "1") (set_attr "prefix_rep" "1")
(set_attr "modrm" "0")]) (set_attr "modrm" "0")])
(define_insn "simple_return_pop_internal" (define_insn_and_split "simple_return_pop_internal"
[(simple_return) [(simple_return)
(use (match_operand:SI 0 "const_int_operand"))] (use (match_operand:SI 0 "const_int_operand"))]
"reload_completed" "reload_completed"
"%!ret\t%0" "%!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 "length" "3")
(set_attr "atom_unit" "jeu") (set_attr "atom_unit" "jeu")
(set_attr "length_immediate" "2") (set_attr "length_immediate" "2")
...@@ -13112,7 +13115,7 @@ ...@@ -13112,7 +13115,7 @@
[(simple_return) [(simple_return)
(use (match_operand 0 "register_operand" "r"))] (use (match_operand 0 "register_operand" "r"))]
"reload_completed" "reload_completed"
"* return ix86_output_indirect_jmp (operands[0], true);" "* return ix86_output_indirect_function_return (operands[0]);"
[(set (attr "type") [(set (attr "type")
(if_then_else (match_test "(cfun->machine->indirect_branch_type (if_then_else (match_test "(cfun->machine->indirect_branch_type
!= indirect_branch_keep)") != indirect_branch_keep)")
......
2018-02-26 H.J. Lu <hongjiu.lu@intel.com>
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 <paolo.carlini@oracle.com> 2018-02-26 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/84533 PR c++/84533
......
/* 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} } } */
/* 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} } } */
/* 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} } } */
/* 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} } } */
/* 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment