diff options
author | Alexandre Oliva <aoliva@redhat.com> | 2015-11-26 21:57:40 +0000 |
---|---|---|
committer | Alexandre Oliva <aoliva@gcc.gnu.org> | 2015-11-26 21:57:40 +0000 |
commit | 1e5d7fd638ca44b45e9d1a49da0b39e6d0cb1fc7 (patch) | |
tree | 5b030f1af1cc3c775d28f45c3afdaf55906e4e93 /gcc/function.c | |
parent | 4d6ca95b9a1deb58a5f4ff81d90ee3a0d75ed330 (diff) | |
download | gcc-1e5d7fd638ca44b45e9d1a49da0b39e6d0cb1fc7.zip gcc-1e5d7fd638ca44b45e9d1a49da0b39e6d0cb1fc7.tar.gz gcc-1e5d7fd638ca44b45e9d1a49da0b39e6d0cb1fc7.tar.bz2 |
[PR67753] adjust for padding when bypassing memory in assign_parm_setup_block
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.
From-SVN: r230985
Diffstat (limited to 'gcc/function.c')
-rw-r--r-- | gcc/function.c | 44 |
1 files changed, 41 insertions, 3 deletions
diff --git a/gcc/function.c b/gcc/function.c index afc2c87..515d7c0 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); |