/* Convert tree expression to rtl instructions, for GNU compiler.
Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GCC.
#endif
static void do_tablejump (rtx, enum machine_mode, rtx, rtx, rtx);
static rtx const_vector_from_tree (tree);
+static void write_complex_part (rtx, rtx, bool);
/* Record for each mode whether we can move a register directly to or
from an object of that mode in memory. If we can't, we won't try
rtx tmp = FUNCTION_ARG (args_so_far, mode, NULL_TREE, 1);
if (!tmp || !REG_P (tmp))
return false;
- if (FUNCTION_ARG_PARTIAL_NREGS (args_so_far, mode,
- NULL_TREE, 1))
+ if (targetm.calls.arg_partial_bytes (&args_so_far, mode, NULL, 1))
return false;
FUNCTION_ARG_ADVANCE (args_so_far, mode, NULL_TREE, 1);
}
rtx
clear_storage (rtx object, rtx size)
{
- rtx retval = 0;
- unsigned int align = (MEM_P (object) ? MEM_ALIGN (object)
- : GET_MODE_ALIGNMENT (GET_MODE (object)));
+ enum machine_mode mode = GET_MODE (object);
+ unsigned int align;
/* If OBJECT is not BLKmode and SIZE is the same size as its mode,
just move a zero. Otherwise, do this a piece at a time. */
- if (GET_MODE (object) != BLKmode
+ if (mode != BLKmode
&& GET_CODE (size) == CONST_INT
- && INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (GET_MODE (object)))
- emit_move_insn (object, CONST0_RTX (GET_MODE (object)));
- else
+ && INTVAL (size) == (HOST_WIDE_INT) GET_MODE_SIZE (mode))
{
- if (size == const0_rtx)
- ;
- else if (GET_CODE (size) == CONST_INT
- && CLEAR_BY_PIECES_P (INTVAL (size), align))
- clear_by_pieces (object, INTVAL (size), align);
- else if (clear_storage_via_clrmem (object, size, align))
- ;
- else
- retval = clear_storage_via_libcall (object, size);
+ rtx zero = CONST0_RTX (mode);
+ if (zero != NULL)
+ {
+ emit_move_insn (object, zero);
+ return NULL;
+ }
+
+ if (COMPLEX_MODE_P (mode))
+ {
+ zero = CONST0_RTX (GET_MODE_INNER (mode));
+ if (zero != NULL)
+ {
+ write_complex_part (object, zero, 0);
+ write_complex_part (object, zero, 1);
+ return NULL;
+ }
+ }
}
- return retval;
+ if (size == const0_rtx)
+ return NULL;
+
+ align = MEM_ALIGN (object);
+
+ if (GET_CODE (size) == CONST_INT
+ && CLEAR_BY_PIECES_P (INTVAL (size), align))
+ clear_by_pieces (object, INTVAL (size), align);
+ else if (clear_storage_via_clrmem (object, size, align))
+ ;
+ else
+ return clear_storage_via_libcall (object, size);
+
+ return NULL;
}
/* A subroutine of clear_storage. Expand a clrmem pattern;
true, NULL_RTX, imode, imode);
}
\f
-/* A subroutine of emit_move_via_alt_mode. Yet another lowpart generator.
+/* A subroutine of emit_move_insn_1. Yet another lowpart generator.
NEW_MODE and OLD_MODE are the same size. Return NULL if X cannot be
- represented in NEW_MODE. */
+ represented in NEW_MODE. If FORCE is true, this will never happen, as
+ we'll force-create a SUBREG if needed. */
static rtx
emit_move_change_mode (enum machine_mode new_mode,
- enum machine_mode old_mode, rtx x)
+ enum machine_mode old_mode, rtx x, bool force)
{
rtx ret;
}
else
{
- /* Note that we do want simplify_subreg's behaviour of validating
+ /* Note that we do want simplify_subreg's behavior of validating
that the new mode is ok for a hard register. If we were to use
simplify_gen_subreg, we would create the subreg, but would
probably run into the target not being able to implement it. */
- ret = simplify_subreg (new_mode, x, old_mode, 0);
+ /* Except, of course, when FORCE is true, when this is exactly what
+ we want. Which is needed for CCmodes on some targets. */
+ if (force)
+ ret = simplify_gen_subreg (new_mode, x, old_mode, 0);
+ else
+ ret = simplify_subreg (new_mode, x, old_mode, 0);
}
return ret;
}
/* A subroutine of emit_move_insn_1. Generate a move from Y into X using
- ALT_MODE instead of the operand's natural mode, MODE. CODE is the insn
- code for the move in ALT_MODE, and is known to be valid. Returns the
- instruction emitted, or NULL if X or Y cannot be represented in ALT_MODE. */
-
-static rtx
-emit_move_via_alt_mode (enum machine_mode alt_mode, enum machine_mode mode,
- enum insn_code code, rtx x, rtx y)
-{
- x = emit_move_change_mode (alt_mode, mode, x);
- if (x == NULL_RTX)
- return NULL_RTX;
- y = emit_move_change_mode (alt_mode, mode, y);
- if (y == NULL_RTX)
- return NULL_RTX;
- return emit_insn (GEN_FCN (code) (x, y));
-}
-
-/* A subroutine of emit_move_insn_1. Generate a move from Y into X using
an integer mode of the same size as MODE. Returns the instruction
emitted, or NULL if such a move could not be generated. */
if (code == CODE_FOR_nothing)
return NULL_RTX;
- return emit_move_via_alt_mode (imode, mode, code, x, y);
+ x = emit_move_change_mode (imode, mode, x, false);
+ if (x == NULL_RTX)
+ return NULL_RTX;
+ y = emit_move_change_mode (imode, mode, y, false);
+ if (y == NULL_RTX)
+ return NULL_RTX;
+ return emit_insn (GEN_FCN (code) (x, y));
}
/* A subroutine of emit_move_insn_1. X is a push_operand in MODE.
if (push_operand (x, mode))
return emit_move_complex_push (mode, x, y);
- /* For memory to memory moves, optimial behaviour can be had with the
+ /* For memory to memory moves, optimal behavior can be had with the
existing block move logic. */
if (MEM_P (x) && MEM_P (y))
{
{
enum insn_code code = mov_optab->handlers[CCmode].insn_code;
if (code != CODE_FOR_nothing)
- return emit_move_via_alt_mode (CCmode, mode, code, x, y);
+ {
+ x = emit_move_change_mode (CCmode, mode, x, true);
+ y = emit_move_change_mode (CCmode, mode, y, true);
+ return emit_insn (GEN_FCN (code) (x, y));
+ }
}
/* Otherwise, find the MODE_INT mode of the same width. */
ALIGN (in bits) is maximum alignment we can assume.
If PARTIAL and REG are both nonzero, then copy that many of the first
- words of X into registers starting with REG, and push the rest of X.
- The amount of space pushed is decreased by PARTIAL words,
- rounded *down* to a multiple of PARM_BOUNDARY.
+ bytes of X into registers starting with REG, and push the rest of X.
+ The amount of space pushed is decreased by PARTIAL bytes.
REG must be a hard register in this case.
If REG is zero but PARTIAL is not, take any all others actions for an
argument partially in registers, but do not actually load any
/* Copy a block into the stack, entirely or partially. */
rtx temp;
- int used = partial * UNITS_PER_WORD;
+ int used;
int offset;
int skip;
- if (reg && GET_CODE (reg) == PARALLEL)
- {
- /* Use the size of the elt to compute offset. */
- rtx elt = XEXP (XVECEXP (reg, 0, 0), 0);
- used = partial * GET_MODE_SIZE (GET_MODE (elt));
- offset = used % (PARM_BOUNDARY / BITS_PER_UNIT);
- }
- else
- offset = used % (PARM_BOUNDARY / BITS_PER_UNIT);
+ offset = partial % (PARM_BOUNDARY / BITS_PER_UNIT);
+ used = partial - offset;
gcc_assert (size);
- used -= offset;
-
/* USED is now the # of bytes we need not copy to the stack
because registers will take care of them. */
int size = GET_MODE_SIZE (mode) / UNITS_PER_WORD;
int i;
int not_stack;
- /* # words of start of argument
+ /* # bytes of start of argument
that we must make space for but need not store. */
int offset = partial % (PARM_BOUNDARY / BITS_PER_WORD);
int args_offset = INTVAL (args_so_far);
/* Now NOT_STACK gets the number of words that we don't need to
allocate on the stack. */
- not_stack = partial - offset;
+ not_stack = (partial - offset) / UNITS_PER_WORD;
/* If the partial register-part of the arg counts in its stack size,
skip the part of stack space corresponding to the registers.
if (GET_CODE (reg) == PARALLEL)
emit_group_load (reg, x, type, -1);
else
- move_block_to_reg (REGNO (reg), x, partial, mode);
+ {
+ gcc_assert (partial % UNITS_PER_WORD == 0);
+ move_block_to_reg (REGNO (reg), x, partial / UNITS_PER_WORD, mode);
+ }
}
if (extra && args_addr == 0 && where_pad == stack_direction)
push_temp_slots ();
tem = get_inner_reference (to, &bitsize, &bitpos, &offset, &mode1,
- &unsignedp, &volatilep);
+ &unsignedp, &volatilep, true);
/* If we are going to use store_bit_field and extract_bit_field,
make sure to_rtx will be safe for multiple use. */
enum machine_mode eltmode = TYPE_MODE (elttype);
HOST_WIDE_INT bitsize;
HOST_WIDE_INT bitpos;
- rtx *vector = NULL;
+ rtvec vector = NULL;
unsigned n_elts;
gcc_assert (eltmode != BLKmode);
{
unsigned int i;
- vector = alloca (n_elts);
+ vector = rtvec_alloc (n_elts);
for (i = 0; i < n_elts; i++)
- vector [i] = CONST0_RTX (GET_MODE_INNER (mode));
+ RTVEC_ELT (vector, i) = CONST0_RTX (GET_MODE_INNER (mode));
}
}
/* Vector CONSTRUCTORs should only be built from smaller
vectors in the case of BLKmode vectors. */
gcc_assert (TREE_CODE (TREE_TYPE (value)) != VECTOR_TYPE);
- vector[eltpos] = expand_expr (value, NULL_RTX, VOIDmode, 0);
+ RTVEC_ELT (vector, eltpos)
+ = expand_expr (value, NULL_RTX, VOIDmode, 0);
}
else
{
if (vector)
emit_insn (GEN_FCN (icode)
(target,
- gen_rtx_PARALLEL (GET_MODE (target),
- gen_rtvec_v (n_elts, vector))));
+ gen_rtx_PARALLEL (GET_MODE (target), vector)));
break;
}
If the field describes a variable-sized object, *PMODE is set to
VOIDmode and *PBITSIZE is set to -1. An access cannot be made in
- this case, but the address of the object can be found. */
+ this case, but the address of the object can be found.
+
+ If KEEP_ALIGNING is true and the target is STRICT_ALIGNMENT, we don't
+ look through nodes that serve as markers of a greater alignment than
+ the one that can be deduced from the expression. These nodes make it
+ possible for front-ends to prevent temporaries from being created by
+ the middle-end on alignment considerations. For that purpose, the
+ normal operating mode at high-level is to always pass FALSE so that
+ the ultimate containing object is really returned; moreover, the
+ associated predicate handled_component_p will always return TRUE
+ on these nodes, thus indicating that they are essentially handled
+ by get_inner_reference. TRUE should only be passed when the caller
+ is scanning the expression in order to build another representation
+ and specifically knows how to handle these nodes; as such, this is
+ the normal operating mode in the RTL expanders. */
tree
get_inner_reference (tree exp, HOST_WIDE_INT *pbitsize,
HOST_WIDE_INT *pbitpos, tree *poffset,
enum machine_mode *pmode, int *punsignedp,
- int *pvolatilep)
+ int *pvolatilep, bool keep_aligning)
{
tree size_tree = 0;
enum machine_mode mode = VOIDmode;
bitsize_int (*pbitsize));
break;
- /* We can go inside most conversions: all NON_VALUE_EXPRs, all normal
- conversions that don't change the mode, and all view conversions
- except those that need to "step up" the alignment. */
-
case VIEW_CONVERT_EXPR:
- if ((TYPE_ALIGN (TREE_TYPE (exp))
+ if (keep_aligning && STRICT_ALIGNMENT
+ && (TYPE_ALIGN (TREE_TYPE (exp))
> TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0))))
- && STRICT_ALIGNMENT
&& (TYPE_ALIGN (TREE_TYPE (TREE_OPERAND (exp, 0)))
< BIGGEST_ALIGNMENT)
&& (TYPE_ALIGN_OK (TREE_TYPE (exp))
return result;
}
+ /* Pass FALSE as the last argument to get_inner_reference although
+ we are expanding to RTL. The rationale is that we know how to
+ handle "aligning nodes" here: we can just bypass them because
+ they won't change the final object whose address will be returned
+ (they actually exist only for that purpose). */
inner = get_inner_reference (exp, &bitsize, &bitpos, &offset,
- &mode1, &unsignedp, &volatilep);
+ &mode1, &unsignedp, &volatilep, false);
break;
}
tree offset;
int volatilep = 0;
tree tem = get_inner_reference (exp, &bitsize, &bitpos, &offset,
- &mode1, &unsignedp, &volatilep);
+ &mode1, &unsignedp, &volatilep, true);
rtx orig_op0;
/* If we got back the original object, something is wrong. Perhaps