mirror of git://gcc.gnu.org/git/gcc.git
[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
This commit is contained in:
parent
66371f94f5
commit
99206968a8
|
|
@ -1,3 +1,19 @@
|
|||
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>
|
||||
|
||||
* config/arm/linux-eabi.h (MUSL_DYNAMIC_LINKER): Define.
|
||||
|
|
|
|||
18
gcc/calls.c
18
gcc/calls.c
|
|
@ -3234,6 +3234,15 @@ expand_call (tree exp, rtx target, int ignore)
|
|||
{
|
||||
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,
|
||||
adjusted_args_size.var != 0,
|
||||
reg_parm_stack_space)
|
||||
|
|
@ -4276,7 +4285,7 @@ emit_library_call_value_1 (int retval, rtx orgfun, rtx value,
|
|||
partial, reg, 0, argblock,
|
||||
GEN_INT (argvec[argnum].locate.offset.constant),
|
||||
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. */
|
||||
if (ACCUMULATE_OUTGOING_ARGS)
|
||||
|
|
@ -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 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,
|
||||
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
|
||||
in the stack. */
|
||||
|
|
@ -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,
|
||||
parm_align, partial, reg, excess, argblock,
|
||||
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
|
||||
in the stack.
|
||||
|
|
|
|||
90
gcc/expr.c
90
gcc/expr.c
|
|
@ -4104,12 +4104,35 @@ emit_single_push_insn (machine_mode mode, rtx x, tree type)
|
|||
}
|
||||
#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
|
||||
type TYPE.
|
||||
MODE is redundant except when X is a CONST_INT (since they don't
|
||||
carry mode info).
|
||||
SIZE is an rtx for the size of data to be copied (in bytes),
|
||||
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.
|
||||
|
||||
|
|
@ -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
|
||||
of bytes required. */
|
||||
|
||||
void
|
||||
bool
|
||||
emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
|
||||
unsigned int align, int partial, rtx reg, int extra,
|
||||
rtx args_addr, rtx args_so_far, int reg_parm_stack_space,
|
||||
rtx alignment_pad)
|
||||
rtx alignment_pad, bool sibcall_p)
|
||||
{
|
||||
rtx xinner;
|
||||
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,
|
|||
|
||||
xinner = x;
|
||||
|
||||
int nregs = partial / UNITS_PER_WORD;
|
||||
rtx *tmp_regs = NULL;
|
||||
int overlapping = 0;
|
||||
|
||||
if (mode == BLKmode
|
||||
|| (STRICT_ALIGNMENT && align < GET_MODE_ALIGNMENT (mode)))
|
||||
{
|
||||
|
|
@ -4287,6 +4314,43 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
|
|||
PARM_BOUNDARY. Assume the caller isn't lying. */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -4341,12 +4405,13 @@ emit_push_insn (rtx x, machine_mode mode, tree type, rtx size,
|
|||
has a size a multiple of a word. */
|
||||
for (i = size - 1; i >= not_stack; i--)
|
||||
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,
|
||||
0, args_addr,
|
||||
GEN_INT (args_offset + ((i - not_stack + skip)
|
||||
* UNITS_PER_WORD)),
|
||||
reg_parm_stack_space, alignment_pad);
|
||||
reg_parm_stack_space, alignment_pad, sibcall_p))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -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
|
||||
into the appropriate registers. Do this now, at the end,
|
||||
since mem-to-mem copies above may do function calls. */
|
||||
/* Move the partial arguments into the registers and any overlapping
|
||||
values that we moved into the pseudos in tmp_regs. */
|
||||
if (partial > 0 && reg != 0)
|
||||
{
|
||||
/* 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,
|
|||
if (GET_CODE (reg) == PARALLEL)
|
||||
emit_group_load (reg, x, type, -1);
|
||||
else
|
||||
{
|
||||
{
|
||||
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,
|
|||
|
||||
if (alignment_pad && args_addr == 0)
|
||||
anti_adjust_stack (alignment_pad);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 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);
|
|||
extern rtx push_block (rtx, int, int);
|
||||
|
||||
/* 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,
|
||||
int, rtx, int, rtx, rtx, int, rtx);
|
||||
extern bool emit_push_insn (rtx, machine_mode, tree, rtx, unsigned int,
|
||||
int, rtx, int, rtx, rtx, int, rtx, bool);
|
||||
|
||||
/* Expand an assignment that stores the value of FROM into TO. */
|
||||
extern void expand_assignment (tree, tree, bool);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
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>
|
||||
|
||||
PR fortran/65548
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
/* { 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;
|
||||
}
|
||||
Loading…
Reference in New Issue