diff options
-rw-r--r-- | meta/recipes-devtools/gcc/gcc-5.3.inc | 3 | ||||
-rw-r--r-- | meta/recipes-devtools/gcc/gcc-5.3/0053-expr.c-PR-target-65358-Avoid-clobbering-partial-argu.patch | 307 |
2 files changed, 309 insertions, 1 deletions
diff --git a/meta/recipes-devtools/gcc/gcc-5.3.inc b/meta/recipes-devtools/gcc/gcc-5.3.inc index a522bc40c2..a2e347adfe 100644 --- a/meta/recipes-devtools/gcc/gcc-5.3.inc +++ b/meta/recipes-devtools/gcc/gcc-5.3.inc @@ -82,7 +82,8 @@ SRC_URI = "\ file://0050-powerpc-pass-secure-plt-to-the-linker.patch \ file://0051-Ignore-fdebug-prefix-map-in-producer-string-by-Danie.patch \ file://0052-nios2-use-ret-with-r31.patch \ - " + file://0053-expr.c-PR-target-65358-Avoid-clobbering-partial-argu.patch \ +" BACKPORTS = "" diff --git a/meta/recipes-devtools/gcc/gcc-5.3/0053-expr.c-PR-target-65358-Avoid-clobbering-partial-argu.patch b/meta/recipes-devtools/gcc/gcc-5.3/0053-expr.c-PR-target-65358-Avoid-clobbering-partial-argu.patch new file mode 100644 index 0000000000..c18f40e740 --- /dev/null +++ b/meta/recipes-devtools/gcc/gcc-5.3/0053-expr.c-PR-target-65358-Avoid-clobbering-partial-argu.patch @@ -0,0 +1,307 @@ +From 536b8318974495cde2b42c3c2742748e2b271be0 Mon Sep 17 00:00:00 2001 +From: ktkachov <ktkachov@138bc75d-0d04-0410-961f-82ee72b054a4> +Date: Wed, 27 May 2015 13:25:01 +0000 +Subject: [PATCH] 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. + +git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@223753 138bc75d-0d04-0410-961f-82ee72b054a4 + +Upstream-Status: Backport from 6.0 +Signed-off-by: Martin Jansa <Martin.Jansa@gmail.com> +--- + gcc/calls.c | 17 ++++++-- + gcc/expr.c | 90 +++++++++++++++++++++++++++++++++++++----- + gcc/expr.h | 4 +- + gcc/testsuite/gcc.dg/pr65358.c | 33 ++++++++++++++++ + 4 files changed, 129 insertions(+), 15 deletions(-) + create mode 100644 gcc/testsuite/gcc.dg/pr65358.c + +diff --git a/gcc/calls.c b/gcc/calls.c +index ee8ea5f..2334381 100644 +--- a/gcc/calls.c ++++ b/gcc/calls.c +@@ -3236,6 +3236,14 @@ 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 defined(ARGS_GROW_DOWNWARD) && !defined(STACK_GROWS_DOWNWARD) ++ sibcall_failure = 1; ++ break; ++#endif ++ + if (store_one_arg (&args[i], argblock, flags, + adjusted_args_size.var != 0, + reg_parm_stack_space) +@@ -4279,7 +4287,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 +4894,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. */ +@@ -5001,7 +5010,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 5c09550..24a6293 100644 +--- a/gcc/expr.c ++++ b/gcc/expr.c +@@ -4121,12 +4121,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. + +@@ -4152,11 +4175,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 +@@ -4179,6 +4202,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))) + { +@@ -4309,6 +4336,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); + } + } +@@ -4363,12 +4427,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 + { +@@ -4411,9 +4476,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. +@@ -4421,9 +4485,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]); ++ + } + } + +@@ -4432,6 +4502,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 867852e..5fcc13f 100644 +--- a/gcc/expr.h ++++ b/gcc/expr.h +@@ -218,8 +218,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/gcc.dg/pr65358.c b/gcc/testsuite/gcc.dg/pr65358.c +new file mode 100644 +index 0000000..ba89fd4 +--- /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; ++} +-- +2.7.0 + |