/* Subroutines for manipulating rtx's in semantically interesting ways.
- Copyright (C) 1987, 1991, 1994, 1995 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. */
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 (oldx == x)
return x;
else if (GET_CODE (x) == REG)
- mark_reg_pointer (x);
+ 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));
+ 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. */
break;
#endif
+#ifdef POINTERS_EXTEND_UNSIGNED
+ case REFERENCE_TYPE:
case POINTER_TYPE:
+ mode = Pmode;
+ unsignedp = POINTERS_EXTEND_UNSIGNED;
break;
+#endif
}
*punsignedp = unsignedp;
else
{
/* 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. */
+ 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),
int known_align;
{
/* If we're asking for zero bytes, it doesn't matter what we point
- to since we can't derefference it. But return a reasonable
+ to since we can't dereference it. But return a reasonable
address anyway. */
if (size == const0_rtx)
return virtual_stack_dynamic_rtx;
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) || ! defined (STACK_BOUNDARY)
+#if defined (STACK_DYNAMIC_OFFSET) || defined (STACK_POINTER_OFFSET) || ! defined (STACK_BOUNDARY)
#define MUST_ALIGN 1
#else
#define MUST_ALIGN (STACK_BOUNDARY < BIGGEST_ALIGNMENT)
|| 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);
if (MUST_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. */
+ 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);