aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Henderson <rth@cygnus.com>1998-07-01 05:10:51 +0000
committerRichard Henderson <rth@cygnus.com>1998-07-01 05:10:51 +0000
commit011c4be8179c2cfdcc8ec7c0cb2fc2d2e58490e2 (patch)
tree828f62b36645dd969e85f79cb627a5fabb232ee1
parenta7ae83d3b1b101fe081b62ff4729d07ca6f6e287 (diff)
* expr.c (emit_group_load, emit_group_store): Rewrite considering
the size and alignment of the structure being manipulated. * expr.c, calls.c, function.c: Update all callers. * expr.h: Update prototypes. * cse.c (invalidate): Cope with parallels. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@20867 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog8
-rw-r--r--gcc/calls.c13
-rw-r--r--gcc/cse.c18
-rw-r--r--gcc/expr.c222
-rw-r--r--gcc/expr.h4
-rw-r--r--gcc/function.c11
6 files changed, 200 insertions, 76 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index f468786bc38..ff467bfd9ac 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@
+Wed Jul 1 05:04:41 1998 Richard Henderson <rth@cygnus.com>
+
+ * expr.c (emit_group_load, emit_group_store): Rewrite considering
+ the size and alignment of the structure being manipulated.
+ * expr.c, calls.c, function.c: Update all callers.
+ * expr.h: Update prototypes.
+ * cse.c (invalidate): Cope with parallels.
+
Wed Jul 1 04:22:23 1998 Richard Henderson <rth@cygnus.com>
* sparc.c (function_arg_record_value): Take a MODE arg with which to
diff --git a/gcc/calls.c b/gcc/calls.c
index 6271a611a8c..8b140190c7e 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1922,7 +1922,12 @@ expand_call (exp, target, ignore)
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (reg) == PARALLEL)
- emit_group_load (reg, args[i].value);
+ {
+ emit_group_load (reg, args[i].value,
+ int_size_in_bytes (TREE_TYPE (args[i].tree_value)),
+ (TYPE_ALIGN (TREE_TYPE (args[i].tree_value))
+ / BITS_PER_UNIT));
+ }
/* If simple case, just do move. If normal partial, store_one_arg
has already loaded the register for us. In all other cases,
@@ -2089,15 +2094,17 @@ expand_call (exp, target, ignore)
The Irix 6 ABI has examples of this. */
else if (GET_CODE (valreg) == PARALLEL)
{
+ int bytes = int_size_in_bytes (TREE_TYPE (exp));
+
if (target == 0)
{
- int bytes = int_size_in_bytes (TREE_TYPE (exp));
target = assign_stack_temp (TYPE_MODE (TREE_TYPE (exp)), bytes, 0);
MEM_IN_STRUCT_P (target) = AGGREGATE_TYPE_P (TREE_TYPE (exp));
preserve_temp_slots (target);
}
- emit_group_store (target, valreg);
+ emit_group_store (target, valreg, bytes,
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
}
else if (target && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
&& GET_MODE (target) == GET_MODE (valreg))
diff --git a/gcc/cse.c b/gcc/cse.c
index bd2aa567d23..37055110575 100644
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1609,6 +1609,24 @@ invalidate (x, full_mode)
return;
}
+ /* If X is a parallel, invalidate all of its elements. */
+
+ if (GET_CODE (x) == PARALLEL)
+ {
+ for (i = XVECLEN (x, 0) - 1; i >= 0 ; --i)
+ invalidate (XVECEXP (x, 0, i), VOIDmode);
+ return;
+ }
+
+ /* If X is an expr_list, this is part of a disjoint return value;
+ extract the location in question ignoring the offset. */
+
+ if (GET_CODE (x) == EXPR_LIST)
+ {
+ invalidate (XEXP (x, 0), VOIDmode);
+ return;
+ }
+
/* X is not a register; it must be a memory reference with
a nonvarying address. Remove all hash table elements
that refer to overlapping pieces of memory. */
diff --git a/gcc/expr.c b/gcc/expr.c
index e01f08a69c8..a7abc5ab832 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -1822,103 +1822,187 @@ move_block_from_reg (regno, x, nregs, size)
}
}
-/* Emit code to move a block Y to a block X, where X is non-consecutive
- registers represented by a PARALLEL. */
+/* Emit code to move a block SRC to a block DST, where DST is non-consecutive
+ registers represented by a PARALLEL. SSIZE represents the total size of
+ block SRC in bytes, or -1 if not known. ALIGN is the known alignment of
+ SRC in bits. */
+/* ??? If SSIZE % UNITS_PER_WORD != 0, we make the blatent assumption that
+ the balance will be in what would be the low-order memory addresses, i.e.
+ left justified for big endian, right justified for little endian. This
+ happens to be true for the targets currently using this support. If this
+ ever changes, a new target macro along the lines of FUNCTION_ARG_PADDING
+ would be needed. */
void
-emit_group_load (x, y)
- rtx x, y;
+emit_group_load (dst, orig_src, ssize, align)
+ rtx dst, orig_src;
+ int align, ssize;
{
- rtx target_reg, source;
- int i;
+ rtx *tmps, src;
+ int start, i;
- if (GET_CODE (x) != PARALLEL)
+ if (GET_CODE (dst) != PARALLEL)
abort ();
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
- if (XEXP (XVECEXP (x, 0, 0), 0))
- i = 0;
+ if (XEXP (XVECEXP (dst, 0, 0), 0))
+ start = 0;
else
- i = 1;
-
- for (; i < XVECLEN (x, 0); i++)
- {
- rtx element = XVECEXP (x, 0, i);
-
- target_reg = XEXP (element, 0);
-
- if (GET_CODE (y) == MEM)
- source = change_address (y, GET_MODE (target_reg),
- plus_constant (XEXP (y, 0),
- INTVAL (XEXP (element, 1))));
- else if (XEXP (element, 1) == const0_rtx)
- {
- if (GET_MODE (target_reg) == GET_MODE (y))
- source = y;
- /* Allow for the target_reg to be smaller than the input register
- to allow for AIX with 4 DF arguments after a single SI arg. The
- last DF argument will only load 1 word into the integer registers,
- but load a DF value into the float registers. */
- else if ((GET_MODE_SIZE (GET_MODE (target_reg))
- <= GET_MODE_SIZE (GET_MODE (y)))
- && GET_MODE (target_reg) == word_mode)
- /* This might be a const_double, so we can't just use SUBREG. */
- source = operand_subword (y, 0, 0, VOIDmode);
- else if (GET_MODE_SIZE (GET_MODE (target_reg))
- == GET_MODE_SIZE (GET_MODE (y)))
- source = gen_lowpart (GET_MODE (target_reg), y);
- else
- abort ();
+ start = 1;
+
+ tmps = (rtx *) alloca (sizeof(rtx) * XVECLEN (dst, 0));
+
+ /* If we won't be loading directly from memory, protect the real source
+ from strange tricks we might play. */
+ src = orig_src;
+ if (GET_CODE (src) != MEM)
+ {
+ src = gen_reg_rtx (GET_MODE (orig_src));
+ emit_move_insn (src, orig_src);
+ }
+
+ /* Process the pieces. */
+ for (i = start; i < XVECLEN (dst, 0); i++)
+ {
+ enum machine_mode mode = GET_MODE (XEXP (XVECEXP (dst, 0, i), 0));
+ int bytepos = INTVAL (XEXP (XVECEXP (dst, 0, i), 1));
+ int bytelen = GET_MODE_SIZE (mode);
+ int shift = 0;
+
+ /* Handle trailing fragments that run over the size of the struct. */
+ if (ssize >= 0 && bytepos + bytelen > ssize)
+ {
+ shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT;
+ bytelen = ssize - bytepos;
+ if (bytelen <= 0)
+ abort();
+ }
+
+ /* Optimize the access just a bit. */
+ if (GET_CODE (src) == MEM
+ && align*BITS_PER_UNIT >= GET_MODE_ALIGNMENT (mode)
+ && bytepos*BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0
+ && bytelen == GET_MODE_SIZE (mode))
+ {
+ tmps[i] = gen_reg_rtx (mode);
+ emit_move_insn (tmps[i],
+ change_address (src, mode,
+ plus_constant (XEXP (src, 0),
+ bytepos)));
}
else
- abort ();
+ {
+ tmps[i] = extract_bit_field (src, bytelen*BITS_PER_UNIT,
+ bytepos*BITS_PER_UNIT, 1, NULL_RTX,
+ mode, mode, align, ssize);
+ }
- emit_move_insn (target_reg, source);
+ if (BYTES_BIG_ENDIAN && shift)
+ {
+ expand_binop (mode, ashl_optab, tmps[i], GEN_INT (shift),
+ tmps[i], 0, OPTAB_WIDEN);
+ }
}
+ emit_queue();
+
+ /* Copy the extracted pieces into the proper (probable) hard regs. */
+ for (i = start; i < XVECLEN (dst, 0); i++)
+ emit_move_insn (XEXP (XVECEXP (dst, 0, i), 0), tmps[i]);
}
-/* Emit code to move a block Y to a block X, where Y is non-consecutive
- registers represented by a PARALLEL. */
+/* Emit code to move a block SRC to a block DST, where SRC is non-consecutive
+ registers represented by a PARALLEL. SSIZE represents the total size of
+ block DST, or -1 if not known. ALIGN is the known alignment of DST. */
void
-emit_group_store (x, y)
- rtx x, y;
+emit_group_store (orig_dst, src, ssize, align)
+ rtx orig_dst, src;
+ int ssize, align;
{
- rtx source_reg, target;
- int i;
+ rtx *tmps, dst;
+ int start, i;
- if (GET_CODE (y) != PARALLEL)
+ if (GET_CODE (src) != PARALLEL)
abort ();
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
- if (XEXP (XVECEXP (y, 0, 0), 0))
- i = 0;
+ if (XEXP (XVECEXP (src, 0, 0), 0))
+ start = 0;
else
- i = 1;
+ start = 1;
- for (; i < XVECLEN (y, 0); i++)
+ tmps = (rtx *) alloca (sizeof(rtx) * XVECLEN (src, 0));
+
+ /* Copy the (probable) hard regs into pseudos. */
+ for (i = start; i < XVECLEN (src, 0); i++)
{
- rtx element = XVECEXP (y, 0, i);
+ rtx reg = XEXP (XVECEXP (src, 0, i), 0);
+ tmps[i] = gen_reg_rtx (GET_MODE (reg));
+ emit_move_insn (tmps[i], reg);
+ }
+ emit_queue();
- source_reg = XEXP (element, 0);
+ /* If we won't be storing directly into memory, protect the real destination
+ from strange tricks we might play. */
+ dst = orig_dst;
+ if (GET_CODE (dst) != MEM)
+ {
+ dst = gen_reg_rtx (GET_MODE (orig_dst));
+ /* Make life a bit easier for combine. */
+ emit_move_insn (dst, const0_rtx);
+ }
+ else if (! MEM_IN_STRUCT_P (dst))
+ {
+ /* store_bit_field requires that memory operations have
+ mem_in_struct_p set; we might not. */
- if (GET_CODE (x) == MEM)
- target = change_address (x, GET_MODE (source_reg),
- plus_constant (XEXP (x, 0),
- INTVAL (XEXP (element, 1))));
- else if (XEXP (element, 1) == const0_rtx)
+ dst = copy_rtx (orig_dst);
+ MEM_IN_STRUCT_P (dst) = 1;
+ }
+
+ /* Process the pieces. */
+ for (i = start; i < XVECLEN (src, 0); i++)
+ {
+ int bytepos = INTVAL (XEXP (XVECEXP (src, 0, i), 1));
+ enum machine_mode mode = GET_MODE (tmps[i]);
+ int bytelen = GET_MODE_SIZE (mode);
+
+ /* Handle trailing fragments that run over the size of the struct. */
+ if (ssize >= 0 && bytepos + bytelen > ssize)
{
- target = x;
- if (GET_MODE (target) != GET_MODE (source_reg))
- target = gen_lowpart (GET_MODE (source_reg), target);
+ if (BYTES_BIG_ENDIAN)
+ {
+ int shift = (bytelen - (ssize - bytepos)) * BITS_PER_UNIT;
+ expand_binop (mode, ashr_optab, tmps[i], GEN_INT (shift),
+ tmps[i], 0, OPTAB_WIDEN);
+ }
+ bytelen = ssize - bytepos;
}
- else
- abort ();
- emit_move_insn (target, source_reg);
+ /* Optimize the access just a bit. */
+ if (GET_CODE (dst) == MEM
+ && align*BITS_PER_UNIT >= GET_MODE_ALIGNMENT (mode)
+ && bytepos*BITS_PER_UNIT % GET_MODE_ALIGNMENT (mode) == 0
+ && bytelen == GET_MODE_SIZE (mode))
+ {
+ emit_move_insn (change_address (dst, mode,
+ plus_constant (XEXP (dst, 0),
+ bytepos)),
+ tmps[i]);
+ }
+ else
+ {
+ store_bit_field (dst, bytelen*BITS_PER_UNIT, bytepos*BITS_PER_UNIT,
+ mode, tmps[i], align, ssize);
+ }
}
+ emit_queue();
+
+ /* Copy from the pseudo into the (probable) hard reg. */
+ if (GET_CODE (dst) == REG)
+ emit_move_insn (orig_dst, dst);
}
/* Add a USE expression for REG to the (possibly empty) list pointed
@@ -2862,7 +2946,7 @@ emit_push_insn (x, mode, type, size, align, partial, reg, extra,
/* Handle calls that pass values in multiple non-contiguous locations.
The Irix 6 ABI has examples of this. */
if (GET_CODE (reg) == PARALLEL)
- emit_group_load (reg, x);
+ emit_group_load (reg, x, -1, align); /* ??? size? */
else
move_block_to_reg (REGNO (reg), x, partial, mode);
}
@@ -3071,7 +3155,8 @@ expand_assignment (to, from, want_value, suggest_reg)
/* Handle calls that return values in multiple non-contiguous locations.
The Irix 6 ABI has examples of this. */
if (GET_CODE (to_rtx) == PARALLEL)
- emit_group_load (to_rtx, value);
+ emit_group_load (to_rtx, value, int_size_in_bytes (TREE_TYPE (from)),
+ TYPE_ALIGN (TREE_TYPE (from)) / BITS_PER_UNIT);
else if (GET_MODE (to_rtx) == BLKmode)
emit_block_move (to_rtx, value, expr_size (from),
TYPE_ALIGN (TREE_TYPE (from)) / BITS_PER_UNIT);
@@ -3476,7 +3561,8 @@ store_expr (exp, target, want_value)
/* Handle calls that return values in multiple non-contiguous locations.
The Irix 6 ABI has examples of this. */
else if (GET_CODE (target) == PARALLEL)
- emit_group_load (target, temp);
+ emit_group_load (target, temp, int_size_in_bytes (TREE_TYPE (exp)),
+ TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
else if (GET_MODE (temp) == BLKmode)
emit_block_move (target, temp, expr_size (exp),
TYPE_ALIGN (TREE_TYPE (exp)) / BITS_PER_UNIT);
diff --git a/gcc/expr.h b/gcc/expr.h
index f75828f212a..11f4673efa1 100644
--- a/gcc/expr.h
+++ b/gcc/expr.h
@@ -714,10 +714,10 @@ extern void move_block_from_reg PROTO((int, rtx, int, int));
/* Load a BLKmode value into non-consecutive registers represented by a
PARALLEL. */
-extern void emit_group_load PROTO((rtx, rtx));
+extern void emit_group_load PROTO((rtx, rtx, int, int));
/* Store a BLKmode value from non-consecutive registers represented by a
PARALLEL. */
-extern void emit_group_store PROTO((rtx, rtx));
+extern void emit_group_store PROTO((rtx, rtx, int, int));
/* Mark REG as holding a parameter for the next CALL_INSN. */
extern void use_reg PROTO((rtx *, rtx));
diff --git a/gcc/function.c b/gcc/function.c
index 9150ebc2bac..67bbc698cfe 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -3945,8 +3945,10 @@ assign_parms (fndecl, second_time)
/* Handle calls that pass values in multiple non-contiguous
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (entry_parm) == PARALLEL)
- emit_group_store (validize_mem (stack_parm),
- entry_parm);
+ emit_group_store (validize_mem (stack_parm), entry_parm,
+ int_size_in_bytes (TREE_TYPE (parm)),
+ (TYPE_ALIGN (TREE_TYPE (parm))
+ / BITS_PER_UNIT));
else
move_block_from_reg (REGNO (entry_parm),
validize_mem (stack_parm), nregs,
@@ -4116,7 +4118,10 @@ assign_parms (fndecl, second_time)
/* Handle calls that pass values in multiple non-contiguous
locations. The Irix 6 ABI has examples of this. */
if (GET_CODE (entry_parm) == PARALLEL)
- emit_group_store (validize_mem (stack_parm), entry_parm);
+ emit_group_store (validize_mem (stack_parm), entry_parm,
+ int_size_in_bytes (TREE_TYPE (parm)),
+ (TYPE_ALIGN (TREE_TYPE (parm))
+ / BITS_PER_UNIT));
else
move_block_from_reg (REGNO (entry_parm),
validize_mem (stack_parm),