return gen_rtx_PARALLEL (GET_MODE (orig), gen_rtvec_v (length, tmps));
}
-/* Emit code to move a block ORIG_SRC of type TYPE to a block DST,
- where DST is non-consecutive registers represented by a PARALLEL.
- SSIZE represents the total size of block ORIG_SRC in bytes, or -1
- if not known. */
+/* A subroutine of emit_group_load. Arguments as for emit_group_load,
+ except that values are placed in TMPS[i], and must later be moved
+ into corresponding XEXP (XVECEXP (DST, 0, i), 0) element. */
-void
-emit_group_load (rtx dst, rtx orig_src, tree type ATTRIBUTE_UNUSED, int ssize)
+static void
+emit_group_load_1 (rtx *tmps, rtx dst, rtx orig_src, tree type, int ssize)
{
- rtx *tmps, src;
+ rtx src;
int start, i;
+ enum machine_mode m = GET_MODE (orig_src);
gcc_assert (GET_CODE (dst) == PARALLEL);
+ if (!SCALAR_INT_MODE_P (m)
+ && !MEM_P (orig_src) && GET_CODE (orig_src) != CONCAT)
+ {
+ enum machine_mode imode = int_mode_for_mode (GET_MODE (orig_src));
+ if (imode == BLKmode)
+ src = assign_stack_temp (GET_MODE (orig_src), ssize, 0);
+ else
+ src = gen_reg_rtx (imode);
+ if (imode != BLKmode)
+ src = gen_lowpart (GET_MODE (orig_src), src);
+ emit_move_insn (src, orig_src);
+ /* ...and back again. */
+ if (imode != BLKmode)
+ src = gen_lowpart (imode, src);
+ emit_group_load_1 (tmps, dst, src, type, ssize);
+ return;
+ }
+
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
if (XEXP (XVECEXP (dst, 0, 0), 0))
else
start = 1;
- tmps = alloca (sizeof (rtx) * XVECLEN (dst, 0));
-
/* Process the pieces. */
for (i = start; i < XVECLEN (dst, 0); i++)
{
else
{
rtx mem;
-
+
gcc_assert (!bytepos);
mem = assign_stack_temp (GET_MODE (src), slen, 0);
emit_move_insn (mem, src);
- tmps[i] = adjust_address (mem, mode, 0);
+ tmps[i] = extract_bit_field (mem, bytelen * BITS_PER_UNIT,
+ 0, 1, NULL_RTX, mode, mode);
}
}
/* FIXME: A SIMD parallel will eventually lead to a subreg of a
tmps[i] = expand_shift (LSHIFT_EXPR, mode, tmps[i],
build_int_cst (NULL_TREE, shift), tmps[i], 0);
}
+}
+
+/* Emit code to move a block SRC of type TYPE to a block DST,
+ where DST is non-consecutive registers represented by a PARALLEL.
+ SSIZE represents the total size of block ORIG_SRC in bytes, or -1
+ if not known. */
+
+void
+emit_group_load (rtx dst, rtx src, tree type, int ssize)
+{
+ rtx *tmps;
+ int i;
+
+ tmps = alloca (sizeof (rtx) * XVECLEN (dst, 0));
+ emit_group_load_1 (tmps, dst, src, type, ssize);
/* 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]);
+ for (i = 0; i < XVECLEN (dst, 0); i++)
+ {
+ rtx d = XEXP (XVECEXP (dst, 0, i), 0);
+ if (d == NULL)
+ continue;
+ emit_move_insn (d, tmps[i]);
+ }
+}
+
+/* Similar, but load SRC into new pseudos in a format that looks like
+ PARALLEL. This can later be fed to emit_group_move to get things
+ in the right place. */
+
+rtx
+emit_group_load_into_temps (rtx parallel, rtx src, tree type, int ssize)
+{
+ rtvec vec;
+ int i;
+
+ vec = rtvec_alloc (XVECLEN (parallel, 0));
+ emit_group_load_1 (&RTVEC_ELT (vec, 0), parallel, src, type, ssize);
+
+ /* Convert the vector to look just like the original PARALLEL, except
+ with the computed values. */
+ for (i = 0; i < XVECLEN (parallel, 0); i++)
+ {
+ rtx e = XVECEXP (parallel, 0, i);
+ rtx d = XEXP (e, 0);
+
+ if (d)
+ {
+ d = force_reg (GET_MODE (d), RTVEC_ELT (vec, i));
+ e = alloc_EXPR_LIST (REG_NOTE_KIND (e), d, XEXP (e, 1));
+ }
+ RTVEC_ELT (vec, i) = e;
+ }
+
+ return gen_rtx_PARALLEL (GET_MODE (parallel), vec);
}
/* Emit code to move a block SRC to block DST, where SRC and DST are
XEXP (XVECEXP (src, 0, i), 0));
}
+/* Move a group of registers represented by a PARALLEL into pseudos. */
+
+rtx
+emit_group_move_into_temps (rtx src)
+{
+ rtvec vec = rtvec_alloc (XVECLEN (src, 0));
+ int i;
+
+ for (i = 0; i < XVECLEN (src, 0); i++)
+ {
+ rtx e = XVECEXP (src, 0, i);
+ rtx d = XEXP (e, 0);
+
+ if (d)
+ e = alloc_EXPR_LIST (REG_NOTE_KIND (e), copy_to_reg (d), XEXP (e, 1));
+ RTVEC_ELT (vec, i) = e;
+ }
+
+ return gen_rtx_PARALLEL (GET_MODE (src), vec);
+}
+
/* Emit code to move a block SRC to a block ORIG_DST of type TYPE,
where SRC is non-consecutive registers represented by a PARALLEL.
SSIZE represents the total size of block ORIG_DST, or -1 if not
{
rtx *tmps, dst;
int start, i;
+ enum machine_mode m = GET_MODE (orig_dst);
gcc_assert (GET_CODE (src) == PARALLEL);
+ if (!SCALAR_INT_MODE_P (m)
+ && !MEM_P (orig_dst) && GET_CODE (orig_dst) != CONCAT)
+ {
+ enum machine_mode imode = int_mode_for_mode (GET_MODE (orig_dst));
+ if (imode == BLKmode)
+ dst = assign_stack_temp (GET_MODE (orig_dst), ssize, 0);
+ else
+ dst = gen_reg_rtx (imode);
+ emit_group_store (dst, src, type, ssize);
+ if (imode != BLKmode)
+ dst = gen_lowpart (GET_MODE (orig_dst), dst);
+ emit_move_insn (orig_dst, dst);
+ return;
+ }
+
/* Check for a NULL entry, used to indicate that the parameter goes
both on the stack and in registers. */
if (XEXP (XVECEXP (src, 0, 0), 0))
/* Generate code for computing expression EXP,
and storing the value into TARGET.
- If WANT_VALUE & 1 is nonzero, return a copy of the value
- not in TARGET, so that we can be sure to use the proper
- value in a containing expression even if TARGET has something
- else stored in it. If possible, we copy the value through a pseudo
- and return that pseudo. Or, if the value is constant, we try to
- return the constant. In some cases, we return a pseudo
- copied *from* TARGET.
-
If the mode is BLKmode then we may return TARGET itself.
It turns out that in BLKmode it doesn't cause a problem.
because C has no operators that could combine two different
with no sequence point. Will other languages need this to
be more thorough?
- If WANT_VALUE & 1 is 0, we return NULL, to make sure
- to catch quickly any cases where the caller uses the value
- and fails to set WANT_VALUE.
-
- If WANT_VALUE & 2 is set, this is a store into a call param on the
+ If CALL_PARAM_P is nonzero, this is a store into a call param on the
stack, and block moves may need to be treated specially. */
rtx
-store_expr (tree exp, rtx target, int want_value)
+store_expr (tree exp, rtx target, int call_param_p)
{
rtx temp;
rtx alt_rtl = NULL_RTX;
int dont_return_target = 0;
- int dont_store_target = 0;
if (VOID_TYPE_P (TREE_TYPE (exp)))
{
/* C++ can generate ?: expressions with a throw expression in one
branch and an rvalue in the other. Here, we resolve attempts to
store the throw expression's nonexistent result. */
- gcc_assert (!want_value);
+ gcc_assert (!call_param_p);
expand_expr (exp, const0_rtx, VOIDmode, 0);
return NULL_RTX;
}
/* Perform first part of compound expression, then assign from second
part. */
expand_expr (TREE_OPERAND (exp, 0), const0_rtx, VOIDmode,
- want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
- return store_expr (TREE_OPERAND (exp, 1), target, want_value);
+ call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
+ return store_expr (TREE_OPERAND (exp, 1), target, call_param_p);
}
else if (TREE_CODE (exp) == COND_EXPR && GET_MODE (target) == BLKmode)
{
do_pending_stack_adjust ();
NO_DEFER_POP;
jumpifnot (TREE_OPERAND (exp, 0), lab1);
- store_expr (TREE_OPERAND (exp, 1), target, want_value & 2);
+ store_expr (TREE_OPERAND (exp, 1), target, call_param_p);
emit_jump_insn (gen_jump (lab2));
emit_barrier ();
emit_label (lab1);
- store_expr (TREE_OPERAND (exp, 2), target, want_value & 2);
+ store_expr (TREE_OPERAND (exp, 2), target, call_param_p);
emit_label (lab2);
OK_DEFER_POP;
- return want_value & 1 ? target : NULL_RTX;
- }
- else if ((want_value & 1) != 0
- && MEM_P (target)
- && ! MEM_VOLATILE_P (target)
- && GET_MODE (target) != BLKmode)
- /* If target is in memory and caller wants value in a register instead,
- arrange that. Pass TARGET as target for expand_expr so that,
- if EXP is another assignment, WANT_VALUE will be nonzero for it.
- We know expand_expr will not use the target in that case.
- Don't do this if TARGET is volatile because we are supposed
- to write it and then read it. */
- {
- temp = expand_expr (exp, target, GET_MODE (target),
- want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
- if (GET_MODE (temp) != BLKmode && GET_MODE (temp) != VOIDmode)
- {
- /* If TEMP is already in the desired TARGET, only copy it from
- memory and don't store it there again. */
- if (temp == target
- || (rtx_equal_p (temp, target)
- && ! side_effects_p (temp) && ! side_effects_p (target)))
- dont_store_target = 1;
- temp = copy_to_reg (temp);
- }
- dont_return_target = 1;
+ return NULL_RTX;
}
else if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
/* If this is a scalar in a register that is stored in a wider mode
{
rtx inner_target = 0;
- /* If we don't want a value, we can do the conversion inside EXP,
- which will often result in some optimizations. Do the conversion
- in two steps: first change the signedness, if needed, then
- the extend. But don't do this if the type of EXP is a subtype
- of something else since then the conversion might involve
- more than just converting modes. */
- if ((want_value & 1) == 0
- && INTEGRAL_TYPE_P (TREE_TYPE (exp))
+ /* We can do the conversion inside EXP, which will often result
+ in some optimizations. Do the conversion in two steps: first
+ change the signedness, if needed, then the extend. But don't
+ do this if the type of EXP is a subtype of something else
+ since then the conversion might involve more than just
+ converting modes. */
+ if (INTEGRAL_TYPE_P (TREE_TYPE (exp))
&& TREE_TYPE (TREE_TYPE (exp)) == 0
&& (!lang_hooks.reduce_bit_field_operations
|| (GET_MODE_PRECISION (GET_MODE (target))
}
temp = expand_expr (exp, inner_target, VOIDmode,
- want_value & 2 ? EXPAND_STACK_PARM : EXPAND_NORMAL);
-
- /* If TEMP is a MEM and we want a result value, make the access
- now so it gets done only once. Strictly speaking, this is
- only necessary if the MEM is volatile, or if the address
- overlaps TARGET. But not performing the load twice also
- reduces the amount of rtl we generate and then have to CSE. */
- if (MEM_P (temp) && (want_value & 1) != 0)
- temp = copy_to_reg (temp);
+ call_param_p ? EXPAND_STACK_PARM : EXPAND_NORMAL);
/* If TEMP is a VOIDmode constant, use convert_modes to make
sure that we properly convert it. */
convert_move (SUBREG_REG (target), temp,
SUBREG_PROMOTED_UNSIGNED_P (target));
- /* If we promoted a constant, change the mode back down to match
- target. Otherwise, the caller might get confused by a result whose
- mode is larger than expected. */
-
- if ((want_value & 1) != 0 && GET_MODE (temp) != GET_MODE (target))
- {
- if (GET_MODE (temp) != VOIDmode)
- {
- temp = gen_lowpart_SUBREG (GET_MODE (target), temp);
- SUBREG_PROMOTED_VAR_P (temp) = 1;
- SUBREG_PROMOTED_UNSIGNED_SET (temp,
- SUBREG_PROMOTED_UNSIGNED_P (target));
- }
- else
- temp = convert_modes (GET_MODE (target),
- GET_MODE (SUBREG_REG (target)),
- temp, SUBREG_PROMOTED_UNSIGNED_P (target));
- }
-
- return want_value & 1 ? temp : NULL_RTX;
+ return NULL_RTX;
}
else
{
temp = expand_expr_real (exp, target, GET_MODE (target),
- (want_value & 2
+ (call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL),
&alt_rtl);
/* Return TARGET if it's a specified hardware register.
&& REGNO (target) < FIRST_PSEUDO_REGISTER)
&& !(MEM_P (target) && MEM_VOLATILE_P (target))
&& ! rtx_equal_p (temp, target)
- && (CONSTANT_P (temp) || (want_value & 1) != 0))
+ && CONSTANT_P (temp))
dont_return_target = 1;
}
|| (temp != target && (side_effects_p (temp)
|| side_effects_p (target))))
&& TREE_CODE (exp) != ERROR_MARK
- && ! dont_store_target
/* If store_expr stores a DECL whose DECL_RTL(exp) == TARGET,
but TARGET is not valid memory reference, TEMP will differ
from TARGET although it is really the same location. */
if (GET_CODE (size) == CONST_INT
&& INTVAL (size) < TREE_STRING_LENGTH (exp))
emit_block_move (target, temp, size,
- (want_value & 2
+ (call_param_p
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
else
{
size_int (TREE_STRING_LENGTH (exp)));
rtx copy_size_rtx
= expand_expr (copy_size, NULL_RTX, VOIDmode,
- (want_value & 2
+ (call_param_p
? EXPAND_STACK_PARM : EXPAND_NORMAL));
rtx label = 0;
copy_size_rtx = convert_to_mode (ptr_mode, copy_size_rtx,
TYPE_UNSIGNED (sizetype));
emit_block_move (target, temp, copy_size_rtx,
- (want_value & 2
+ (call_param_p
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
/* Figure out how much is left in TARGET that we have to clear.
int_size_in_bytes (TREE_TYPE (exp)));
else if (GET_MODE (temp) == BLKmode)
emit_block_move (target, temp, expr_size (exp),
- (want_value & 2
+ (call_param_p
? BLOCK_OP_CALL_PARM : BLOCK_OP_NORMAL));
else
{
}
}
- /* If we don't want a value, return NULL_RTX. */
- if ((want_value & 1) == 0)
- return NULL_RTX;
-
- /* If we are supposed to return TEMP, do so as long as it isn't a MEM.
- ??? The latter test doesn't seem to make sense. */
- else if (dont_return_target && !MEM_P (temp))
- return temp;
-
- /* Return TARGET itself if it is a hard register. */
- else if ((want_value & 1) != 0
- && GET_MODE (target) != BLKmode
- && ! (REG_P (target)
- && REGNO (target) < FIRST_PSEUDO_REGISTER))
- return copy_to_reg (target);
-
- else
- return target;
+ return NULL_RTX;
}
\f
/* Examine CTOR. Discover how many scalar fields are set to nonzero
/* Return 1 if EXP contains mostly (3/4) zeros. */
-int
+static int
mostly_zeros_p (tree exp)
{
if (TREE_CODE (exp) == CONSTRUCTOR)
/* Store data into beginning of memory target. */
store_expr (TREE_OPERAND (exp, 0),
adjust_address (target, TYPE_MODE (valtype), 0),
- modifier == EXPAND_STACK_PARM ? 2 : 0);
+ modifier == EXPAND_STACK_PARM);
else
{
op1 = gen_label_rtx ();
jumpifnot (TREE_OPERAND (exp, 0), op0);
store_expr (TREE_OPERAND (exp, 1), temp,
- modifier == EXPAND_STACK_PARM ? 2 : 0);
+ modifier == EXPAND_STACK_PARM);
emit_jump_insn (gen_jump (op1));
emit_barrier ();
emit_label (op0);
store_expr (TREE_OPERAND (exp, 2), temp,
- modifier == EXPAND_STACK_PARM ? 2 : 0);
+ modifier == EXPAND_STACK_PARM);
emit_label (op1);
OK_DEFER_POP;