From: Kyrylo Tkachov Date: Wed, 27 May 2015 13:25:01 +0000 (+0000) Subject: [expr.c] PR target/65358 Avoid clobbering partial argument during sibcall X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=99206968a8da4e5342b498f264eb365e11a499c8;p=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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b496476e712..fae616144a5 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2015-05-27 Kyrylo Tkachov + + 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 * config/arm/linux-eabi.h (MUSL_DYNAMIC_LINKER): Define. diff --git a/gcc/calls.c b/gcc/calls.c index afe61f47d87..2158ebad0b4 100644 --- a/gcc/calls.c +++ b/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. diff --git a/gcc/expr.c b/gcc/expr.c index a613bebe824..1dd1cf301f2 100644 --- a/gcc/expr.c +++ b/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 diff --git a/gcc/expr.h b/gcc/expr.h index e3afa8db1f6..7b28ffd8d39 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -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); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e19504ee213..4119cb8be62 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2015-05-27 Honggyu Kim + + PR target/65358 + * gcc.dg/pr65358.c: New test. + 2015-05-27 Andre Vehreschild PR fortran/65548 diff --git a/gcc/testsuite/gcc.dg/pr65358.c b/gcc/testsuite/gcc.dg/pr65358.c new file mode 100644 index 00000000000..ba89fd4818b --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr65358.c @@ -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; +}