Commit 99206968 by Kyrylo Tkachov

[expr.c] PR target/65358 Avoid clobbering partial argument during sibcall

	PR target/65358
	* expr.c (memory_load_overlap): New function.
	(emit_push_insn): When pushing partial args to the stack would
	clobber the register part load the overlapping part into a pseudo
	and put it into the hard reg after pushing.  Change return type
	to bool.  Add bool argument.
	* expr.h (emit_push_insn): Change return type to bool.
	Add bool argument.
	* calls.c (expand_call): Cancel sibcall optimization when encountering
	partial argument on targets with ARGS_GROW_DOWNWARD and
	!STACK_GROWS_DOWNWARD.
	(emit_library_call_value_1): Update callsite of emit_push_insn.
	(store_one_arg): Likewise. 

	PR target/65358
	* gcc.dg/pr65358.c: New test.

From-SVN: r223753
parent 66371f94
2015-05-27 Kyrylo Tkachov <kyrylo.tkachov@arm.com>
PR target/65358
* expr.c (memory_load_overlap): New function.
(emit_push_insn): When pushing partial args to the stack would
clobber the register part load the overlapping part into a pseudo
and put it into the hard reg after pushing. Change return type
to bool. Add bool argument.
* expr.h (emit_push_insn): Change return type to bool.
Add bool argument.
* calls.c (expand_call): Cancel sibcall optimization when encountering
partial argument on targets with ARGS_GROW_DOWNWARD and
!STACK_GROWS_DOWNWARD.
(emit_library_call_value_1): Update callsite of emit_push_insn.
(store_one_arg): Likewise.
2015-05-27 Gregor Richards <gregor.richards@uwaterloo.ca> 2015-05-27 Gregor Richards <gregor.richards@uwaterloo.ca>
* config/arm/linux-eabi.h (MUSL_DYNAMIC_LINKER): Define. * config/arm/linux-eabi.h (MUSL_DYNAMIC_LINKER): Define.
......
...@@ -3234,6 +3234,15 @@ expand_call (tree exp, rtx target, int ignore) ...@@ -3234,6 +3234,15 @@ expand_call (tree exp, rtx target, int ignore)
{ {
rtx_insn *before_arg = get_last_insn (); rtx_insn *before_arg = get_last_insn ();
/* On targets with weird calling conventions (e.g. PA) it's
hard to ensure that all cases of argument overlap between
stack and registers work. Play it safe and bail out. */
if (ARGS_GROW_DOWNWARD && !STACK_GROWS_DOWNWARD)
{
sibcall_failure = 1;
break;
}
if (store_one_arg (&args[i], argblock, flags, if (store_one_arg (&args[i], argblock, flags,
adjusted_args_size.var != 0, adjusted_args_size.var != 0,
reg_parm_stack_space) reg_parm_stack_space)
...@@ -4276,7 +4285,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value, ...@@ -4276,7 +4285,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
partial, reg, 0, argblock, partial, reg, 0, argblock,
GEN_INT (argvec[argnum].locate.offset.constant), GEN_INT (argvec[argnum].locate.offset.constant),
reg_parm_stack_space, reg_parm_stack_space,
ARGS_SIZE_RTX (argvec[argnum].locate.alignment_pad)); ARGS_SIZE_RTX (argvec[argnum].locate.alignment_pad), false);
/* Now mark the segment we just used. */ /* Now mark the segment we just used. */
if (ACCUMULATE_OUTGOING_ARGS) if (ACCUMULATE_OUTGOING_ARGS)
...@@ -4886,10 +4895,11 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, ...@@ -4886,10 +4895,11 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
/* This isn't already where we want it on the stack, so put it there. /* This isn't already where we want it on the stack, so put it there.
This can either be done with push or copy insns. */ This can either be done with push or copy insns. */
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX, if (!emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), NULL_RTX,
parm_align, partial, reg, used - size, argblock, parm_align, partial, reg, used - size, argblock,
ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space, ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->locate.alignment_pad)); ARGS_SIZE_RTX (arg->locate.alignment_pad), true))
sibcall_failure = 1;
/* Unless this is a partially-in-register argument, the argument is now /* Unless this is a partially-in-register argument, the argument is now
in the stack. */ in the stack. */
...@@ -4994,7 +5004,7 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags, ...@@ -4994,7 +5004,7 @@ store_one_arg (struct arg_data *arg, rtx argblock, int flags,
emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx, emit_push_insn (arg->value, arg->mode, TREE_TYPE (pval), size_rtx,
parm_align, partial, reg, excess, argblock, parm_align, partial, reg, excess, argblock,
ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space, ARGS_SIZE_RTX (arg->locate.offset), reg_parm_stack_space,
ARGS_SIZE_RTX (arg->locate.alignment_pad)); ARGS_SIZE_RTX (arg->locate.alignment_pad), false);
/* Unless this is a partially-in-register argument, the argument is now /* Unless this is a partially-in-register argument, the argument is now
in the stack. in the stack.
......
...@@ -4104,12 +4104,35 @@ emit_single_push_insn (machine_mode mode, rtx x, tree type) ...@@ -4104,12 +4104,35 @@ emit_single_push_insn (machine_mode mode, rtx x, tree type)
} }
#endif #endif
/* If reading SIZE bytes from X will end up reading from
Y return the number of bytes that overlap. Return -1
if there is no overlap or -2 if we can't determine
(for example when X and Y have different base registers). */
static int
memory_load_overlap (rtx x, rtx y, HOST_WIDE_INT size)
{
rtx tmp = plus_constant (Pmode, x, size);
rtx sub = simplify_gen_binary (MINUS, Pmode, tmp, y);
if (!CONST_INT_P (sub))
return -2;
HOST_WIDE_INT val = INTVAL (sub);
return IN_RANGE (val, 1, size) ? val : -1;
}
/* Generate code to push X onto the stack, assuming it has mode MODE and /* Generate code to push X onto the stack, assuming it has mode MODE and
type TYPE. type TYPE.
MODE is redundant except when X is a CONST_INT (since they don't MODE is redundant except when X is a CONST_INT (since they don't
carry mode info). carry mode info).
SIZE is an rtx for the size of data to be copied (in bytes), SIZE is an rtx for the size of data to be copied (in bytes),
needed only if X is BLKmode. needed only if X is BLKmode.
Return true if successful. May return false if asked to push a
partial argument during a sibcall optimization (as specified by
SIBCALL_P) and the incoming and outgoing pointers cannot be shown
to not overlap.
ALIGN (in bits) is maximum alignment we can assume. ALIGN (in bits) is maximum alignment we can assume.
...@@ -4135,11 +4158,11 @@ emit_single_push_insn (machine_mode mode, rtx x, tree type) ...@@ -4135,11 +4158,11 @@ emit_single_push_insn (machine_mode mode, rtx x, tree type)
for arguments passed in registers. If nonzero, it will be the number for arguments passed in registers. If nonzero, it will be the number
of bytes required. */ of bytes required. */
void bool
emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
unsigned int align, int partial, rtx reg, int extra, unsigned int align, int partial, rtx reg, int extra,
rtx args_addr, rtx args_so_far, int reg_parm_stack_space, rtx args_addr, rtx args_so_far, int reg_parm_stack_space,
rtx alignment_pad) rtx alignment_pad, bool sibcall_p)
{ {
rtx xinner; rtx xinner;
enum direction stack_direction = STACK_GROWS_DOWNWARD ? downward : upward; enum direction stack_direction = STACK_GROWS_DOWNWARD ? downward : upward;
...@@ -4157,6 +4180,10 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, ...@@ -4157,6 +4180,10 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
xinner = x; xinner = x;
int nregs = partial / UNITS_PER_WORD;
rtx *tmp_regs = NULL;
int overlapping = 0;
if (mode == BLKmode if (mode == BLKmode
|| (STRICT_ALIGNMENT && align < GET_MODE_ALIGNMENT (mode))) || (STRICT_ALIGNMENT && align < GET_MODE_ALIGNMENT (mode)))
{ {
...@@ -4287,6 +4314,43 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, ...@@ -4287,6 +4314,43 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
PARM_BOUNDARY. Assume the caller isn't lying. */ PARM_BOUNDARY. Assume the caller isn't lying. */
set_mem_align (target, align); set_mem_align (target, align);
/* If part should go in registers and pushing to that part would
overwrite some of the values that need to go into regs, load the
overlapping values into temporary pseudos to be moved into the hard
regs at the end after the stack pushing has completed.
We cannot load them directly into the hard regs here because
they can be clobbered by the block move expansions.
See PR 65358. */
if (partial > 0 && reg != 0 && mode == BLKmode
&& GET_CODE (reg) != PARALLEL)
{
overlapping = memory_load_overlap (XEXP (x, 0), temp, partial);
if (overlapping > 0)
{
gcc_assert (overlapping % UNITS_PER_WORD == 0);
overlapping /= UNITS_PER_WORD;
tmp_regs = XALLOCAVEC (rtx, overlapping);
for (int i = 0; i < overlapping; i++)
tmp_regs[i] = gen_reg_rtx (word_mode);
for (int i = 0; i < overlapping; i++)
emit_move_insn (tmp_regs[i],
operand_subword_force (target, i, mode));
}
else if (overlapping == -1)
overlapping = 0;
/* Could not determine whether there is overlap.
Fail the sibcall. */
else
{
overlapping = 0;
if (sibcall_p)
return false;
}
}
emit_block_move (target, xinner, size, BLOCK_OP_CALL_PARM); emit_block_move (target, xinner, size, BLOCK_OP_CALL_PARM);
} }
} }
...@@ -4341,12 +4405,13 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, ...@@ -4341,12 +4405,13 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
has a size a multiple of a word. */ has a size a multiple of a word. */
for (i = size - 1; i >= not_stack; i--) for (i = size - 1; i >= not_stack; i--)
if (i >= not_stack + offset) if (i >= not_stack + offset)
emit_push_insn (operand_subword_force (x, i, mode), if (!emit_push_insn (operand_subword_force (x, i, mode),
word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX, word_mode, NULL_TREE, NULL_RTX, align, 0, NULL_RTX,
0, args_addr, 0, args_addr,
GEN_INT (args_offset + ((i - not_stack + skip) GEN_INT (args_offset + ((i - not_stack + skip)
* UNITS_PER_WORD)), * UNITS_PER_WORD)),
reg_parm_stack_space, alignment_pad); reg_parm_stack_space, alignment_pad, sibcall_p))
return false;
} }
else else
{ {
...@@ -4389,9 +4454,8 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, ...@@ -4389,9 +4454,8 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
} }
} }
/* If part should go in registers, copy that part /* Move the partial arguments into the registers and any overlapping
into the appropriate registers. Do this now, at the end, values that we moved into the pseudos in tmp_regs. */
since mem-to-mem copies above may do function calls. */
if (partial > 0 && reg != 0) if (partial > 0 && reg != 0)
{ {
/* Handle calls that pass values in multiple non-contiguous locations. /* Handle calls that pass values in multiple non-contiguous locations.
...@@ -4399,9 +4463,15 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, ...@@ -4399,9 +4463,15 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
if (GET_CODE (reg) == PARALLEL) if (GET_CODE (reg) == PARALLEL)
emit_group_load (reg, x, type, -1); emit_group_load (reg, x, type, -1);
else else
{ {
gcc_assert (partial % UNITS_PER_WORD == 0); gcc_assert (partial % UNITS_PER_WORD == 0);
move_block_to_reg (REGNO (reg), x, partial / UNITS_PER_WORD, mode); move_block_to_reg (REGNO (reg), x, nregs - overlapping, mode);
for (int i = 0; i < overlapping; i++)
emit_move_insn (gen_rtx_REG (word_mode, REGNO (reg)
+ nregs - overlapping + i),
tmp_regs[i]);
} }
} }
...@@ -4410,6 +4480,8 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size, ...@@ -4410,6 +4480,8 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
if (alignment_pad && args_addr == 0) if (alignment_pad && args_addr == 0)
anti_adjust_stack (alignment_pad); anti_adjust_stack (alignment_pad);
return true;
} }
/* Return X if X can be used as a subtarget in a sequence of arithmetic /* Return X if X can be used as a subtarget in a sequence of arithmetic
......
...@@ -219,8 +219,8 @@ extern rtx emit_move_resolve_push (machine_mode, rtx); ...@@ -219,8 +219,8 @@ extern rtx emit_move_resolve_push (machine_mode, rtx);
extern rtx push_block (rtx, int, int); extern rtx push_block (rtx, int, int);
/* Generate code to push something onto the stack, given its mode and type. */ /* Generate code to push something onto the stack, given its mode and type. */
extern void emit_push_insn (rtx, machine_mode, tree, rtx, unsigned int, extern bool emit_push_insn (rtx, machine_mode, tree, rtx, unsigned int,
int, rtx, int, rtx, rtx, int, rtx); int, rtx, int, rtx, rtx, int, rtx, bool);
/* Expand an assignment that stores the value of FROM into TO. */ /* Expand an assignment that stores the value of FROM into TO. */
extern void expand_assignment (tree, tree, bool); extern void expand_assignment (tree, tree, bool);
......
2015-05-27 Honggyu Kim <hong.gyu.kim@lge.com>
PR target/65358
* gcc.dg/pr65358.c: New test.
2015-05-27 Andre Vehreschild <vehre@gmx.de> 2015-05-27 Andre Vehreschild <vehre@gmx.de>
PR fortran/65548 PR fortran/65548
......
/* { dg-do run } */
/* { dg-options "-O2" } */
struct pack
{
int fine;
int victim;
int killer;
};
int __attribute__ ((__noinline__, __noclone__))
bar (int a, int b, struct pack p)
{
if (a != 20 || b != 30)
__builtin_abort ();
if (p.fine != 40 || p.victim != 50 || p.killer != 60)
__builtin_abort ();
return 0;
}
int __attribute__ ((__noinline__, __noclone__))
foo (int arg1, int arg2, int arg3, struct pack p)
{
return bar (arg2, arg3, p);
}
int main (void)
{
struct pack p = { 40, 50, 60 };
(void) foo (10, 20, 30, p);
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