summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandre Oliva <aoliva@redhat.com>2015-11-10 18:32:27 -0200
committerAlexandre Oliva <aoliva@redhat.com>2015-11-10 18:32:27 -0200
commit4fb19b947a9ccfe9186751f142a76140206eb924 (patch)
tree07268f2f7514b0c141a1c789567522544f4a5e71
parent23759288b8f28a97d97f9b7f1c91fcc4ba4957ac (diff)
downloadgcc-aoliva/pr67753.tar.gz
[PR67753] adjust for padding when bypassing memory in assign_parm_setup_blockaoliva/pr67753
Storing a register in memory as a full word and then accessing the same memory address under a smaller-than-word mode amounts to right-shifting of the register word on big endian machines. So, if BLOCK_REG_PADDING chooses upward padding for BYTES_BIG_ENDIAN, and we're copying from the entry_parm REG directly to a pseudo, bypassing any stack slot, perform the shifting explicitly. This fixes the miscompile of function_return_val_10 in gcc.target/aarch64/aapcs64/func-ret-4.c for target aarch64_be-elf introduced in the first patch for 67753. for gcc/ChangeLog PR rtl-optimization/67753 PR rtl-optimization/64164 * function.c (assign_parm_setup_block): Right-shift upward-padded big-endian args when bypassing the stack slot.
-rw-r--r--gcc/function.c44
1 files changed, 41 insertions, 3 deletions
diff --git a/gcc/function.c b/gcc/function.c
index 156c72bbf99..8a98e034dce 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -3002,6 +3002,38 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
emit_move_insn (change_address (mem, mode, 0), reg);
}
+#ifdef BLOCK_REG_PADDING
+ /* Storing the register in memory as a full word, as
+ move_block_from_reg below would do, and then using the
+ MEM in a smaller mode, has the effect of shifting right
+ if BYTES_BIG_ENDIAN. If we're bypassing memory, the
+ shifting must be explicit. */
+ else if (!MEM_P (mem))
+ {
+ rtx x;
+
+ /* If the assert below fails, we should have taken the
+ mode != BLKmode path above, unless we have downward
+ padding of smaller-than-word arguments on a machine
+ with little-endian bytes, which would likely require
+ additional changes to work correctly. */
+ gcc_checking_assert (BYTES_BIG_ENDIAN
+ && (BLOCK_REG_PADDING (mode,
+ data->passed_type, 1)
+ == upward));
+
+ int by = (UNITS_PER_WORD - size) * BITS_PER_UNIT;
+
+ x = gen_rtx_REG (word_mode, REGNO (entry_parm));
+ x = expand_shift (RSHIFT_EXPR, word_mode, x, by,
+ NULL_RTX, 1);
+ x = force_reg (word_mode, x);
+ x = gen_lowpart_SUBREG (GET_MODE (mem), x);
+
+ emit_move_insn (mem, x);
+ }
+#endif
+
/* Blocks smaller than a word on a BYTES_BIG_ENDIAN
machine must be aligned to the left before storing
to memory. Note that the previous test doesn't
@@ -3023,14 +3055,20 @@ assign_parm_setup_block (struct assign_parm_data_all *all,
tem = change_address (mem, word_mode, 0);
emit_move_insn (tem, x);
}
- else if (!MEM_P (mem))
- emit_move_insn (mem, entry_parm);
else
move_block_from_reg (REGNO (entry_parm), mem,
size_stored / UNITS_PER_WORD);
}
else if (!MEM_P (mem))
- emit_move_insn (mem, entry_parm);
+ {
+ gcc_checking_assert (size > UNITS_PER_WORD);
+#ifdef BLOCK_REG_PADDING
+ gcc_checking_assert (BLOCK_REG_PADDING (GET_MODE (mem),
+ data->passed_type, 0)
+ == upward);
+#endif
+ emit_move_insn (mem, entry_parm);
+ }
else
move_block_from_reg (REGNO (entry_parm), mem,
size_stored / UNITS_PER_WORD);