aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Kenner <kenner@gcc.gnu.org>1993-02-08 05:51:58 -0500
committerRichard Kenner <kenner@gcc.gnu.org>1993-02-08 05:51:58 -0500
commit4ab561180caefa229eaae824dee8d910c069f5ee (patch)
tree096f0b584f4b1c0501ebb45297ef544b115baa20
parentcd048831883f3b3acdd4dc315cc44df8aea04ed0 (diff)
downloadgcc-4ab561180caefa229eaae824dee8d910c069f5ee.zip
gcc-4ab561180caefa229eaae824dee8d910c069f5ee.tar.gz
gcc-4ab561180caefa229eaae824dee8d910c069f5ee.tar.bz2
Allow passing nonaligned BLKmode structs in regs.
(struct arg_data): New fields aligned_regs and n_aligned_regs. (expand_call): Set and use these fields when required. (store_one_arg): Don't pass reg to emit_push_insn if we have previously formed aligned registers. From-SVN: r3440
-rw-r--r--gcc/calls.c89
1 files changed, 85 insertions, 4 deletions
diff --git a/gcc/calls.c b/gcc/calls.c
index 5b9c9ca..bd12293 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -84,6 +84,14 @@ struct arg_data
/* Place that this stack area has been saved, if needed. */
rtx save_area;
#endif
+#ifdef STRICT_ALIGNMENT
+ /* If an argument's alignment does not permit direct copying into registers,
+ copy in smaller-sized pieces into pseudos. These are stored in a
+ block pointed to by this field. The next field says how many
+ word-sized pseudos we made. */
+ rtx *aligned_regs;
+ int n_aligned_regs;
+#endif
};
#ifdef ACCUMULATE_OUTGOING_ARGS
@@ -514,7 +522,7 @@ expand_call (exp, target, ignore)
rtx use_insns = 0;
register tree p;
- register int i;
+ register int i, j;
/* See if we can find a DECL-node for the actual function.
As a result, decide whether this is a call to an integrable function. */
@@ -625,8 +633,6 @@ expand_call (exp, target, ignore)
/* If inlining succeeded, return. */
if ((HOST_WIDE_INT) temp != -1)
{
- int i;
-
/* Perform all cleanups needed for the arguments of this call
(i.e. destructors in C++). It is ok if these destructors
clobber RETURN_VALUE_REG, because the only time we care about
@@ -1529,6 +1535,63 @@ expand_call (exp, target, ignore)
store_one_arg (&args[i], argblock, may_be_alloca,
args_size.var != 0, fndecl, reg_parm_stack_space);
+#ifdef STRICT_ALIGNMENT
+ /* If we have a parm that is passed in registers but not in memory
+ and whose alignment does not permit a direct copy into registers,
+ make a group of pseudos that correspond to each register that we
+ will later fill. */
+
+ for (i = 0; i < num_actuals; i++)
+ if (args[i].reg != 0 && ! args[i].pass_on_stack
+ && args[i].mode == BLKmode
+ && (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
+ < MIN (BIGGEST_ALIGNMENT, BITS_PER_WORD)))
+ {
+ int bytes = int_size_in_bytes (TREE_TYPE (args[i].tree_value));
+
+ args[i].n_aligned_regs
+ = args[i].partial ? args[i].partial
+ : (bytes + (UNITS_PER_WORD - 1)) / UNITS_PER_WORD;
+
+ args[i].aligned_regs = (rtx *) alloca (sizeof (rtx)
+ * args[i].n_aligned_regs);
+
+ for (j = 0; j < args[i].n_aligned_regs; j++)
+ {
+ rtx reg = gen_reg_rtx (word_mode);
+ rtx word = operand_subword_force (args[i].value, j, BLKmode);
+ int bitsize = TYPE_ALIGN (TREE_TYPE (args[i].tree_value));
+ int bitpos;
+
+ args[i].aligned_regs[j] = reg;
+
+ /* Clobber REG and move each partword into it. Ensure we don't
+ go past the end of the structure. Note that the loop below
+ works because we've already verified that padding
+ and endianness are compatible. */
+
+ emit_insn (gen_rtx (CLOBBER, VOIDmode, reg));
+
+ for (bitpos = 0;
+ bitpos < BITS_PER_WORD && bytes >= 0;
+ bitpos += bitsize, bytes -= bitsize / BITS_PER_UNIT)
+ {
+ int xbitpos = (BYTES_BIG_ENDIAN
+ ? bitpos = BITS_PER_WORD - bitpos - bitsize
+ : bitpos);
+
+ store_bit_field (reg, bitsize, xbitpos, word_mode,
+ extract_bit_field (word, bitsize, xbitpos, 1,
+ NULL_RTX, word_mode,
+ word_mode,
+ bitsize / BITS_PER_UNIT,
+ BITS_PER_WORD),
+ bitsize / BITS_PER_UNIT, BITS_PER_WORD);
+ }
+ }
+ }
+#endif
+
/* Now store any partially-in-registers parm.
This is the last place a block-move can happen. */
if (reg_parm_seen)
@@ -1609,6 +1672,17 @@ expand_call (exp, target, ignore)
if (nregs == 0)
emit_move_insn (reg, args[i].value);
+
+#ifdef STRICT_ALIGNMENT
+ /* If we have pre-computed the values to put in the registers in
+ the case of non-aligned structures, copy them in now. */
+
+ else if (args[i].n_aligned_regs != 0)
+ for (j = 0; j < args[i].n_aligned_regs; j++)
+ emit_move_insn (gen_rtx (REG, word_mode, REGNO (reg) + j),
+ args[i].aligned_regs[j]);
+#endif
+
else if (args[i].partial == 0 || args[i].pass_on_stack)
move_block_to_reg (REGNO (reg),
validize_mem (args[i].value), nregs,
@@ -1989,13 +2063,20 @@ store_one_arg (arg, argblock, may_be_alloca, variable_size, fndecl,
this case. */
abort ();
+#ifdef STRICT_ALIGNMENT
+ /* If this arg needs special alignment, don't load the registers
+ here. */
+ if (arg->n_aligned_regs != 0)
+ reg = 0;
+#endif
+
/* If this is being partially passed in a register, but multiple locations
are specified, we assume that the one partially used is the one that is
listed first. */
if (reg && GET_CODE (reg) == EXPR_LIST)
reg = XEXP (reg, 0);
- /* If this is being passes partially in a register, we can't evaluate
+ /* If this is being passed partially in a register, we can't evaluate
it directly into its stack slot. Otherwise, we can. */
if (arg->value == 0)
{