[PR67753] adjust for padding when bypassing memory in assign_parm_setup_block
authorAlexandre Oliva <aoliva@redhat.com>
Thu, 26 Nov 2015 21:57:40 +0000 (21:57 +0000)
committerAlexandre Oliva <aoliva@gcc.gnu.org>
Thu, 26 Nov 2015 21:57:40 +0000 (21:57 +0000)
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

gcc/ChangeLog
gcc/function.c

index 1b99db5292a78a3e5167b88cc10e4bad811fbd56..e48613e2ccd55eee11c7c98df6dc28484f7cea0a 100644 (file)
@@ -1,3 +1,10 @@
+2015-11-26  Alexandre Oliva <aoliva@redhat.com>
+
+       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.
+
 2015-11-26  Maciej W. Rozycki  <macro@imgtec.com>
 
        * doc/invoke.texi (Option Summary) <MIPS Options>: Reorder
index afc2c87dbcc1fd4dd62938cfc2289077ea75e417..515d7c042203d24f0ee5eb17bb58807eea14a53f 100644 (file)
@@ -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);