/* Subroutines for manipulating rtx's in semantically interesting ways.
- Copyright (C) 1987, 1991, 1994 Free Software Foundation, Inc.
+ Copyright (C) 1987, 91, 94, 95, 96, 1997 Free Software Foundation, Inc.
This file is part of GNU CC.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
#include "config.h"
#include "insn-flags.h"
#include "insn-codes.h"
+static rtx break_out_memory_refs PROTO((rtx));
+
/* Return an rtx for the sum of X and the integer C.
This function should be used via the `plus_constant' macro. */
if (GET_CODE (x) == MEM
|| (CONSTANT_P (x) && CONSTANT_ADDRESS_P (x)
&& GET_MODE (x) != VOIDmode))
- {
- register rtx temp = force_reg (GET_MODE (x), x);
- mark_reg_pointer (temp);
- x = temp;
- }
+ x = force_reg (GET_MODE (x), x);
else if (GET_CODE (x) == PLUS || GET_CODE (x) == MINUS
|| GET_CODE (x) == MULT)
{
register rtx op0 = break_out_memory_refs (XEXP (x, 0));
register rtx op1 = break_out_memory_refs (XEXP (x, 1));
+
if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
x = gen_rtx (GET_CODE (x), Pmode, op0, op1);
}
+
return x;
}
+#ifdef POINTERS_EXTEND_UNSIGNED
+
+/* Given X, a memory address in ptr_mode, convert it to an address
+ in Pmode, or vice versa (TO_MODE says which way). We take advantage of
+ the fact that pointers are not allowed to overflow by commuting arithmetic
+ operations over conversions so that address arithmetic insns can be
+ used. */
+
+rtx
+convert_memory_address (to_mode, x)
+ enum machine_mode to_mode;
+ rtx x;
+{
+ enum machine_mode from_mode = to_mode == ptr_mode ? Pmode : ptr_mode;
+ rtx temp;
+
+ /* Here we handle some special cases. If none of them apply, fall through
+ to the default case. */
+ switch (GET_CODE (x))
+ {
+ case CONST_INT:
+ case CONST_DOUBLE:
+ return x;
+
+ case LABEL_REF:
+ return gen_rtx (LABEL_REF, to_mode, XEXP (x, 0));
+
+ case SYMBOL_REF:
+ temp = gen_rtx (SYMBOL_REF, to_mode, XSTR (x, 0));
+ SYMBOL_REF_FLAG (temp) = SYMBOL_REF_FLAG (x);
+ CONSTANT_POOL_ADDRESS_P (temp) = CONSTANT_POOL_ADDRESS_P (x);
+ return temp;
+
+ case CONST:
+ return gen_rtx (CONST, to_mode,
+ convert_memory_address (to_mode, XEXP (x, 0)));
+
+ case PLUS:
+ case MULT:
+ /* For addition the second operand is a small constant, we can safely
+ permute the converstion and addition operation. We can always safely
+ permute them if we are making the address narrower. In addition,
+ always permute the operations if this is a constant. */
+ if (GET_MODE_SIZE (to_mode) < GET_MODE_SIZE (from_mode)
+ || (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT
+ && (INTVAL (XEXP (x, 1)) + 20000 < 40000
+ || CONSTANT_P (XEXP (x, 0)))))
+ return gen_rtx (GET_CODE (x), to_mode,
+ convert_memory_address (to_mode, XEXP (x, 0)),
+ convert_memory_address (to_mode, XEXP (x, 1)));
+ }
+
+ return convert_modes (to_mode, from_mode,
+ x, POINTERS_EXTEND_UNSIGNED);
+}
+#endif
+
/* Given a memory address or facsimile X, construct a new address,
currently equivalent, that is stable: future stores won't change it.
{
register rtx oldx = x;
+#ifdef POINTERS_EXTEND_UNSIGNED
+ if (GET_MODE (x) == ptr_mode)
+ x = convert_memory_address (Pmode, x);
+#endif
+
/* By passing constant addresses thru registers
we get a chance to cse them. */
if (! cse_not_expected && CONSTANT_P (x) && CONSTANT_ADDRESS_P (x))
}
}
- if (GET_CODE (x) == MULT || GET_CODE (x) == MINUS)
+ else if (GET_CODE (x) == MULT || GET_CODE (x) == MINUS)
x = force_operand (x, NULL_RTX);
/* If we have a register that's an invalid address,
x = force_reg (Pmode, x);
goto done;
- }
- win2:
- x = oldx;
- win:
- if (flag_force_addr && ! cse_not_expected && GET_CODE (x) != REG
- /* Don't copy an addr via a reg if it is one of our stack slots. */
- && ! (GET_CODE (x) == PLUS
- && (XEXP (x, 0) == virtual_stack_vars_rtx
- || XEXP (x, 0) == virtual_incoming_args_rtx)))
- {
- if (general_operand (x, Pmode))
- x = force_reg (Pmode, x);
- else
- x = force_operand (x, NULL_RTX);
+ win2:
+ x = oldx;
+ win:
+ if (flag_force_addr && ! cse_not_expected && GET_CODE (x) != REG
+ /* Don't copy an addr via a reg if it is one of our stack slots. */
+ && ! (GET_CODE (x) == PLUS
+ && (XEXP (x, 0) == virtual_stack_vars_rtx
+ || XEXP (x, 0) == virtual_incoming_args_rtx)))
+ {
+ if (general_operand (x, Pmode))
+ x = force_reg (Pmode, x);
+ else
+ x = force_operand (x, NULL_RTX);
+ }
}
done:
+ /* If we didn't change the address, we are done. Otherwise, mark
+ a reg as a pointer if we have REG or REG + CONST_INT. */
+ if (oldx == x)
+ return x;
+ else if (GET_CODE (x) == REG)
+ mark_reg_pointer (x, 1);
+ else if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 0)) == REG
+ && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ mark_reg_pointer (XEXP (x, 0), 1);
+
/* OLDX may have been the address on a temporary. Update the address
to indicate that X is now used. */
update_temp_slot_address (oldx, x);
enum machine_mode mode;
rtx x;
{
- register rtx temp, insn;
+ register rtx temp, insn, set;
if (GET_CODE (x) == REG)
return x;
temp = gen_reg_rtx (mode);
insn = emit_move_insn (temp, x);
+
/* Let optimizers know that TEMP's value never changes
- and that X can be substituted for it. */
- if (CONSTANT_P (x))
+ and that X can be substituted for it. Don't get confused
+ if INSN set something else (such as a SUBREG of TEMP). */
+ if (CONSTANT_P (x)
+ && (set = single_set (insn)) != 0
+ && SET_DEST (set) == temp)
{
rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
return temp;
}
\f
+/* Return the mode to use to store a scalar of TYPE and MODE.
+ PUNSIGNEDP points to the signedness of the type and may be adjusted
+ to show what signedness to use on extension operations.
+
+ FOR_CALL is non-zero if this call is promoting args for a call. */
+
+enum machine_mode
+promote_mode (type, mode, punsignedp, for_call)
+ tree type;
+ enum machine_mode mode;
+ int *punsignedp;
+ int for_call;
+{
+ enum tree_code code = TREE_CODE (type);
+ int unsignedp = *punsignedp;
+
+#ifdef PROMOTE_FOR_CALL_ONLY
+ if (! for_call)
+ return mode;
+#endif
+
+ switch (code)
+ {
+#ifdef PROMOTE_MODE
+ case INTEGER_TYPE: case ENUMERAL_TYPE: case BOOLEAN_TYPE:
+ case CHAR_TYPE: case REAL_TYPE: case OFFSET_TYPE:
+ PROMOTE_MODE (mode, unsignedp, type);
+ break;
+#endif
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+ case REFERENCE_TYPE:
+ case POINTER_TYPE:
+ mode = Pmode;
+ unsignedp = POINTERS_EXTEND_UNSIGNED;
+ break;
+#endif
+ }
+
+ *punsignedp = unsignedp;
+ return mode;
+}
+\f
/* Adjust the stack pointer by ADJUST (an rtx for a number of bytes).
This pops when ADJUST is positive. ADJUST need not be constant. */
}
else
{
- size = expand_divmod (0, CEIL_DIV_EXPR, Pmode, size, GEN_INT (align),
+ /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
+ but we know it can't. So add ourselves and then do
+ TRUNC_DIV_EXPR. */
+ size = expand_binop (Pmode, add_optab, size, GEN_INT (align - 1),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ size = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, size, GEN_INT (align),
NULL_RTX, 1);
size = expand_mult (Pmode, size, GEN_INT (align), NULL_RTX, 1);
}
rtx target;
int known_align;
{
+ /* If we're asking for zero bytes, it doesn't matter what we point
+ to since we can't dereference it. But return a reasonable
+ address anyway. */
+ if (size == const0_rtx)
+ return virtual_stack_dynamic_rtx;
+
+ /* Otherwise, show we're calling alloca or equivalent. */
+ current_function_calls_alloca = 1;
+
/* Ensure the size is in the proper mode. */
if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
size = convert_to_mode (Pmode, size, 1);
If we have to align, we must leave space in SIZE for the hole
that might result from the alignment operation. */
-#if defined (STACK_DYNAMIC_OFFSET) || defined(STACK_POINTER_OFFSET) || defined (ALLOCATE_OUTGOING_ARGS)
-#define MUST_ALIGN
-#endif
-
-#if ! defined (MUST_ALIGN) && (!defined(STACK_BOUNDARY) || STACK_BOUNDARY < BIGGEST_ALIGNMENT)
-#define MUST_ALIGN
+#if defined (STACK_DYNAMIC_OFFSET) || defined (STACK_POINTER_OFFSET) || ! defined (STACK_BOUNDARY)
+#define MUST_ALIGN 1
+#else
+#define MUST_ALIGN (STACK_BOUNDARY < BIGGEST_ALIGNMENT)
#endif
-#ifdef MUST_ALIGN
-
-#if 0 /* It turns out we must always make extra space, if MUST_ALIGN
- because we must always round the address up at the end,
- because we don't know whether the dynamic offset
- will mess up the desired alignment. */
- /* If we have to round the address up regardless of known_align,
- make extra space regardless, also. */
- if (known_align % BIGGEST_ALIGNMENT != 0)
-#endif
+ if (MUST_ALIGN)
{
if (GET_CODE (size) == CONST_INT)
size = GEN_INT (INTVAL (size)
NULL_RTX, 1, OPTAB_LIB_WIDEN);
}
-#endif
-
#ifdef SETJMP_VIA_SAVE_AREA
/* If setjmp restores regs from a save area in the stack frame,
avoid clobbering the reg save area. Note that the offset of
#ifdef STACK_BOUNDARY
/* If we added a variable amount to SIZE,
we can no longer assume it is aligned. */
-#if !defined (SETJMP_VIA_SAVE_AREA) && !defined (MUST_ALIGN)
- if (known_align % STACK_BOUNDARY != 0)
+#if !defined (SETJMP_VIA_SAVE_AREA)
+ if (MUST_ALIGN || known_align % STACK_BOUNDARY != 0)
#endif
size = round_push (size);
#endif
|| REGNO (target) < FIRST_PSEUDO_REGISTER)
target = gen_reg_rtx (Pmode);
- mark_reg_pointer (target);
+ mark_reg_pointer (target, known_align / BITS_PER_UNIT);
#ifndef STACK_GROWS_DOWNWARD
emit_move_insn (target, virtual_stack_dynamic_rtx);
enum machine_mode mode
= insn_operand_mode[(int) CODE_FOR_allocate_stack][0];
+ size = convert_modes (mode, ptr_mode, size, 1);
+
if (insn_operand_predicate[(int) CODE_FOR_allocate_stack][0]
&& ! ((*insn_operand_predicate[(int) CODE_FOR_allocate_stack][0])
(size, mode)))
}
else
#endif
- anti_adjust_stack (size);
+ {
+ size = convert_modes (Pmode, ptr_mode, size, 1);
+ anti_adjust_stack (size);
+ }
#ifdef STACK_GROWS_DOWNWARD
emit_move_insn (target, virtual_stack_dynamic_rtx);
#endif
-#ifdef MUST_ALIGN
-#if 0 /* Even if we know the stack pointer has enough alignment,
- there's no way to tell whether virtual_stack_dynamic_rtx shares that
- alignment, so we still need to round the address up. */
- if (known_align % BIGGEST_ALIGNMENT != 0)
-#endif
+ if (MUST_ALIGN)
{
- target = expand_divmod (0, CEIL_DIV_EXPR, Pmode, target,
+ /* CEIL_DIV_EXPR needs to worry about the addition overflowing,
+ but we know it can't. So add ourselves and then do
+ TRUNC_DIV_EXPR. */
+ target = expand_binop (Pmode, add_optab, target,
+ GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1),
+ NULL_RTX, 1, OPTAB_LIB_WIDEN);
+ target = expand_divmod (0, TRUNC_DIV_EXPR, Pmode, target,
GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT),
NULL_RTX, 1);
-
target = expand_mult (Pmode, target,
GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT),
NULL_RTX, 1);
}
-#endif
/* Some systems require a particular insn to refer to the stack
to make the pages exist. */
emit_insn (gen_probe ());
#endif
+ /* Record the new stack level for nonlocal gotos. */
+ if (nonlocal_goto_handler_slot != 0)
+ emit_stack_save (SAVE_NONLOCAL, &nonlocal_goto_stack_level, NULL_RTX);
+
return target;
}
\f
tree valtype;
tree func;
{
- return FUNCTION_VALUE (valtype, func);
+ rtx val = FUNCTION_VALUE (valtype, func);
+ if (GET_CODE (val) == REG
+ && GET_MODE (val) == BLKmode)
+ {
+ int bytes = int_size_in_bytes (valtype);
+ enum machine_mode tmpmode;
+ for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ tmpmode != MAX_MACHINE_MODE;
+ tmpmode = GET_MODE_WIDER_MODE (tmpmode))
+ {
+ /* Have we found a large enough mode? */
+ if (GET_MODE_SIZE (tmpmode) >= bytes)
+ break;
+ }
+
+ /* No suitable mode found. */
+ if (tmpmode == MAX_MACHINE_MODE)
+ abort ();
+
+ PUT_MODE (val, tmpmode);
+ }
+ return val;
}
/* Return an rtx representing the register or memory location