/* Subroutines for manipulating rtx's in semantically interesting ways.
- Copyright (C) 1987, 91, 94-97, 1998, 1999 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1991, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "hard-reg-set.h"
#include "insn-config.h"
#include "recog.h"
-#include "insn-flags.h"
-#include "insn-codes.h"
#if !defined PREFERRED_STACK_BOUNDARY && defined STACK_BOUNDARY
#define PREFERRED_STACK_BOUNDARY STACK_BOUNDARY
#endif
-static rtx break_out_memory_refs PROTO((rtx));
-static void emit_stack_probe PROTO((rtx));
+static rtx break_out_memory_refs PARAMS ((rtx));
+static void emit_stack_probe PARAMS ((rtx));
/* Truncate and perhaps sign-extend C as appropriate for MODE. */
{
int width = GET_MODE_BITSIZE (mode);
- /* We clear out all bits that don't belong in MODE, unless they and our
- sign bit are all one. So we get either a reasonable negative
- value or a reasonable unsigned value. */
+ /* Canonicalize BImode to 0 and STORE_FLAG_VALUE. */
+ if (mode == BImode)
+ return c & 1 ? STORE_FLAG_VALUE : 0;
- if (width < HOST_BITS_PER_WIDE_INT
- && ((c & ((HOST_WIDE_INT) (-1) << (width - 1)))
- != ((HOST_WIDE_INT) (-1) << (width - 1))))
- c &= ((HOST_WIDE_INT) 1 << width) - 1;
+ /* Sign-extend for the requested mode. */
- /* If this would be an entire word for the target, but is not for
- the host, then sign-extend on the host so that the number will look
- the same way on the host that it would on the target.
-
- For example, when building a 64 bit alpha hosted 32 bit sparc
- targeted compiler, then we want the 32 bit unsigned value -1 to be
- represented as a 64 bit value -1, and not as 0x00000000ffffffff.
- The later confuses the sparc backend. */
-
- if (BITS_PER_WORD < HOST_BITS_PER_WIDE_INT
- && BITS_PER_WORD == width
- && (c & ((HOST_WIDE_INT) 1 << (width - 1))))
- c |= ((HOST_WIDE_INT) (-1) << width);
+ if (width < HOST_BITS_PER_WIDE_INT)
+ {
+ HOST_WIDE_INT sign = 1;
+ sign <<= width - 1;
+ c &= (sign << 1) - 1;
+ c ^= sign;
+ c -= sign;
+ }
return c;
}
case CONST_DOUBLE:
{
- HOST_WIDE_INT l1 = CONST_DOUBLE_LOW (x);
+ unsigned HOST_WIDE_INT l1 = CONST_DOUBLE_LOW (x);
HOST_WIDE_INT h1 = CONST_DOUBLE_HIGH (x);
- HOST_WIDE_INT l2 = c;
+ unsigned HOST_WIDE_INT l2 = c;
HOST_WIDE_INT h2 = c < 0 ? ~0 : 0;
- HOST_WIDE_INT lv, hv;
+ unsigned HOST_WIDE_INT lv;
+ HOST_WIDE_INT hv;
add_double (l1, h1, l2, h2, &lv, &hv);
if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
&& CONSTANT_POOL_ADDRESS_P (XEXP (x, 0)))
{
- /* Any rtl we create here must go in a saveable obstack, since
- we might have been called from within combine. */
- push_obstacks_nochange ();
- rtl_in_saveable_obstack ();
tem
= force_const_mem (GET_MODE (x),
plus_constant (get_pool_constant (XEXP (x, 0)),
c));
- pop_obstacks ();
if (memory_address_p (GET_MODE (tem), XEXP (tem, 0)))
return tem;
}
return insn;
if (GET_CODE (insn) == JUMP_INSN)
{
- if (simplejump_p (insn))
+ if (any_uncondjump_p (insn))
next = JUMP_LABEL (insn);
else
return 0;
case CONST_DOUBLE:
return x;
+ case SUBREG:
+ if (GET_MODE (SUBREG_REG (x)) == to_mode)
+ return SUBREG_REG (x);
+ break;
+
case LABEL_REF:
temp = gen_rtx_LABEL_REF (to_mode, XEXP (x, 0));
LABEL_REF_NONLOCAL_P (temp) = LABEL_REF_NONLOCAL_P (x);
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);
+ STRING_POOL_ADDRESS_P (temp) = STRING_POOL_ADDRESS_P (x);
return temp;
case CONST:
if (oldx == x)
return x;
else if (GET_CODE (x) == REG)
- mark_reg_pointer (x, 1);
+ mark_reg_pointer (x, BITS_PER_UNIT);
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);
+ mark_reg_pointer (XEXP (x, 0), BITS_PER_UNIT);
/* OLDX may have been the address on a temporary. Update the address
to indicate that X is now used. */
return change_address (ref, GET_MODE (ref), XEXP (ref, 0));
}
\f
+/* Given REF, either a MEM or a REG, and T, either the type of X or
+ the expression corresponding to REF, set RTX_UNCHANGING_P if
+ appropriate. */
+
+void
+maybe_set_unchanging (ref, t)
+ rtx ref;
+ tree t;
+{
+ /* We can set RTX_UNCHANGING_P from TREE_READONLY for decls whose
+ initialization is only executed once, or whose initializer always
+ has the same value. Currently we simplify this to PARM_DECLs in the
+ first case, and decls with TREE_CONSTANT initializers in the second. */
+ if ((TREE_READONLY (t) && DECL_P (t)
+ && (TREE_CODE (t) == PARM_DECL
+ || DECL_INITIAL (t) == NULL_TREE
+ || TREE_CONSTANT (DECL_INITIAL (t))))
+ || TREE_CODE_CLASS (TREE_CODE (t)) == 'c')
+ RTX_UNCHANGING_P (ref) = 1;
+}
+
+/* Given REF, a MEM, and T, either the type of X or the expression
+ corresponding to REF, set the memory attributes. OBJECTP is nonzero
+ if we are making a new object of this type. */
+
+void
+set_mem_attributes (ref, t, objectp)
+ rtx ref;
+ tree t;
+ int objectp;
+{
+ tree type;
+
+ /* It can happen that type_for_mode was given a mode for which there
+ is no language-level type. In which case it returns NULL, which
+ we can see here. */
+ if (t == NULL_TREE)
+ return;
+
+ type = TYPE_P (t) ? t : TREE_TYPE (t);
+
+ /* Get the alias set from the expression or type (perhaps using a
+ front-end routine) and then copy bits from the type. */
+
+ /* It is incorrect to set RTX_UNCHANGING_P from TREE_READONLY (type)
+ here, because, in C and C++, the fact that a location is accessed
+ through a const expression does not mean that the value there can
+ never change. */
+ MEM_ALIAS_SET (ref) = get_alias_set (t);
+ MEM_VOLATILE_P (ref) = TYPE_VOLATILE (type);
+ MEM_IN_STRUCT_P (ref) = AGGREGATE_TYPE_P (type);
+
+ /* If we are making an object of this type, we know that it is a scalar if
+ the type is not an aggregate. */
+ if (objectp && ! AGGREGATE_TYPE_P (type))
+ MEM_SCALAR_P (ref) = 1;
+
+ /* If T is a type, this is all we can do. Otherwise, we may be able
+ to deduce some more information about the expression. */
+ if (TYPE_P (t))
+ return;
+
+ maybe_set_unchanging (ref, t);
+ if (TREE_THIS_VOLATILE (t))
+ MEM_VOLATILE_P (ref) = 1;
+
+ /* Now see if we can say more about whether it's an aggregate or
+ scalar. If we already know it's an aggregate, don't bother. */
+ if (MEM_IN_STRUCT_P (ref))
+ return;
+
+ /* Now remove any NOPs: they don't change what the underlying object is.
+ Likewise for SAVE_EXPR. */
+ while (TREE_CODE (t) == NOP_EXPR || TREE_CODE (t) == CONVERT_EXPR
+ || TREE_CODE (t) == NON_LVALUE_EXPR || TREE_CODE (t) == SAVE_EXPR)
+ t = TREE_OPERAND (t, 0);
+
+ /* Since we already know the type isn't an aggregate, if this is a decl,
+ it must be a scalar. Or if it is a reference into an aggregate,
+ this is part of an aggregate. Otherwise we don't know. */
+ if (DECL_P (t))
+ MEM_SCALAR_P (ref) = 1;
+ else if (TREE_CODE (t) == COMPONENT_REF || TREE_CODE (t) == ARRAY_REF
+ || TREE_CODE (t) == BIT_FIELD_REF)
+ MEM_IN_STRUCT_P (ref) = 1;
+}
+\f
/* Return a modified copy of X with its memory address copied
into a temporary register to protect it from side effects.
If X is not a MEM, it is returned unchanged (and not copied).
rtx x;
{
register rtx addr;
+
if (GET_CODE (x) != MEM)
return x;
+
addr = XEXP (x, 0);
if (rtx_unstable_p (addr))
{
- rtx temp = copy_all_regs (addr);
- rtx mem;
- if (GET_CODE (temp) != REG)
- temp = copy_to_reg (temp);
- mem = gen_rtx_MEM (GET_MODE (x), temp);
-
- /* Mark returned memref with in_struct if it's in an array or
- structure. Copy const and volatile from original memref. */
+ rtx temp = force_reg (Pmode, copy_all_regs (addr));
+ rtx mem = gen_rtx_MEM (GET_MODE (x), temp);
- RTX_UNCHANGING_P (mem) = RTX_UNCHANGING_P (x);
MEM_COPY_ATTRIBUTES (mem, x);
- if (GET_CODE (addr) == PLUS)
- MEM_SET_IN_STRUCT_P (mem, 1);
-
- /* Since the new MEM is just like the old X, it can alias only
- the things that X could. */
- MEM_ALIAS_SET (mem) = MEM_ALIAS_SET (x);
-
return mem;
}
return x;
rtx x;
{
register rtx temp;
+
if (GET_CODE (x) != MEM || GET_MODE (x) == BLKmode)
return x;
+
temp = gen_reg_rtx (GET_MODE (x));
emit_move_insn (temp, x);
return temp;
if (adjust == const0_rtx)
return;
+ /* We expect all variable sized adjustments to be multiple of
+ PREFERRED_STACK_BOUNDARY. */
+ if (GET_CODE (adjust) == CONST_INT)
+ stack_pointer_delta -= INTVAL (adjust);
+
temp = expand_binop (Pmode,
#ifdef STACK_GROWS_DOWNWARD
add_optab,
if (adjust == const0_rtx)
return;
+ /* We expect all variable sized adjustments to be multiple of
+ PREFERRED_STACK_BOUNDARY. */
+ if (GET_CODE (adjust) == CONST_INT)
+ stack_pointer_delta += INTVAL (adjust);
+
temp = expand_binop (Pmode,
#ifdef STACK_GROWS_DOWNWARD
sub_optab,
{
rtx sa = *psave;
/* The default is that we use a move insn and save in a Pmode object. */
- rtx (*fcn) PROTO ((rtx, rtx)) = gen_move_insn;
+ rtx (*fcn) PARAMS ((rtx, rtx)) = gen_move_insn;
enum machine_mode mode = STACK_SAVEAREA_MODE (save_level);
/* See if this machine has anything special to do for this kind of save. */
rtx sa;
{
/* The default is that we use a move insn. */
- rtx (*fcn) PROTO ((rtx, rtx)) = gen_move_insn;
+ rtx (*fcn) PARAMS ((rtx, rtx)) = gen_move_insn;
/* See if this machine has anything special to do for this kind of save. */
switch (save_level)
if (GET_MODE (size) != VOIDmode && GET_MODE (size) != Pmode)
size = convert_to_mode (Pmode, size, 1);
+ /* We can't attempt to minimize alignment necessary, because we don't
+ know the final value of preferred_stack_boundary yet while executing
+ this code. */
+#ifdef PREFERRED_STACK_BOUNDARY
+ cfun->preferred_stack_boundary = PREFERRED_STACK_BOUNDARY;
+#endif
+
/* We will need to ensure that the address we return is aligned to
BIGGEST_ALIGNMENT. If STACK_DYNAMIC_OFFSET is defined, we don't
always know its final value at this point in the compilation (it
#endif
if (MUST_ALIGN)
- {
- if (GET_CODE (size) == CONST_INT)
- size = GEN_INT (INTVAL (size)
- + (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1));
- else
- size = expand_binop (Pmode, add_optab, size,
- GEN_INT (BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1),
- NULL_RTX, 1, OPTAB_LIB_WIDEN);
- }
+ size
+ = force_operand (plus_constant (size,
+ BIGGEST_ALIGNMENT / BITS_PER_UNIT - 1),
+ NULL_RTX);
#ifdef SETJMP_VIA_SAVE_AREA
/* If setjmp restores regs from a save area in the stack frame,
#if !defined(PREFERRED_STACK_BOUNDARY) || !defined(MUST_ALIGN) || (PREFERRED_STACK_BOUNDARY != BIGGEST_ALIGNMENT)
/* If anyone creates a target with these characteristics, let them
know that our optimization cannot work correctly in such a case. */
- abort();
+ abort ();
#endif
if (GET_CODE (size) == CONST_INT)
{
- int new = INTVAL (size) / align * align;
+ HOST_WIDE_INT new = INTVAL (size) / align * align;
if (INTVAL (size) != new)
setjmpless_size = GEN_INT (new);
do_pending_stack_adjust ();
+ /* We ought to be called always on the toplevel and stack ought to be aligned
+ propertly. */
+#ifdef PREFERRED_STACK_BOUNDARY
+ if (stack_pointer_delta % (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT))
+ abort ();
+#endif
+
/* If needed, check that we have the required amount of stack. Take into
account what has already been checked. */
if (flag_stack_check && ! STACK_CHECK_BUILTIN)
probe_stack_range (STACK_CHECK_MAX_FRAME_SIZE + STACK_CHECK_PROTECT, size);
- /* Don't use a TARGET that isn't a pseudo. */
+ /* Don't use a TARGET that isn't a pseudo or is the wrong mode. */
if (target == 0 || GET_CODE (target) != REG
- || REGNO (target) < FIRST_PSEUDO_REGISTER)
+ || REGNO (target) < FIRST_PSEUDO_REGISTER
+ || GET_MODE (target) != Pmode)
target = gen_reg_rtx (Pmode);
- mark_reg_pointer (target, known_align / BITS_PER_UNIT);
+ mark_reg_pointer (target, known_align);
/* Perform the required allocation from the stack. Some systems do
this differently than simply incrementing/decrementing from the
if (mode == VOIDmode)
mode = Pmode;
- size = convert_modes (mode, ptr_mode, size, 1);
pred = insn_data[(int) CODE_FOR_allocate_stack].operand[1].predicate;
if (pred && ! ((*pred) (size, mode)))
size = copy_to_mode_reg (mode, size);
#ifndef STACK_GROWS_DOWNWARD
emit_move_insn (target, virtual_stack_dynamic_rtx);
#endif
- size = convert_modes (Pmode, ptr_mode, size, 1);
/* Check stack bounds if necessary. */
if (current_function_limit_stack)
REG_NOTES (note_target));
}
#endif /* SETJMP_VIA_SAVE_AREA */
+
#ifdef STACK_GROWS_DOWNWARD
emit_move_insn (target, virtual_stack_dynamic_rtx);
#endif
/* First see if the front end has set up a function for us to call to
check the stack. */
if (stack_check_libfunc != 0)
- emit_library_call (stack_check_libfunc, 0, VOIDmode, 1,
- memory_address (QImode,
- gen_rtx (STACK_GROW_OP, Pmode,
- stack_pointer_rtx,
- plus_constant (size, first))),
- ptr_mode);
+ {
+ rtx addr = memory_address (QImode,
+ gen_rtx (STACK_GROW_OP, Pmode,
+ stack_pointer_rtx,
+ plus_constant (size, first)));
+
+#ifdef POINTERS_EXTEND_UNSIGNED
+ if (GET_MODE (addr) != ptr_mode)
+ addr = convert_memory_address (ptr_mode, addr);
+#endif
+
+ emit_library_call (stack_check_libfunc, 0, VOIDmode, 1, addr,
+ ptr_mode);
+ }
/* Next see if we have an insn to check the stack. Use it if so. */
#ifdef HAVE_check_stack
|| REGNO (test_addr) < FIRST_PSEUDO_REGISTER)
test_addr = force_reg (Pmode, test_addr);
- emit_note (NULL_PTR, NOTE_INSN_LOOP_BEG);
+ emit_note (NULL, NOTE_INSN_LOOP_BEG);
emit_jump (test_lab);
emit_label (loop_lab);
emit_stack_probe (test_addr);
- emit_note (NULL_PTR, NOTE_INSN_LOOP_CONT);
+ emit_note (NULL, NOTE_INSN_LOOP_CONT);
#ifdef STACK_GROWS_DOWNWARD
#define CMP_OPCODE GTU
emit_cmp_and_jump_insns (test_addr, last_addr, CMP_OPCODE,
NULL_RTX, Pmode, 1, 0, loop_lab);
emit_jump (end_lab);
- emit_note (NULL_PTR, NOTE_INSN_LOOP_END);
+ emit_note (NULL, NOTE_INSN_LOOP_END);
emit_label (end_lab);
- /* If will be doing stupid optimization, show test_addr is still live. */
- if (obey_regdecls)
- emit_insn (gen_rtx_USE (VOIDmode, test_addr));
-
emit_stack_probe (last_addr);
}
}
int outgoing ATTRIBUTE_UNUSED;
{
rtx val;
+
#ifdef FUNCTION_OUTGOING_VALUE
if (outgoing)
val = FUNCTION_OUTGOING_VALUE (valtype, func);
else
#endif
val = FUNCTION_VALUE (valtype, func);
+
if (GET_CODE (val) == REG
&& GET_MODE (val) == BLKmode)
{
- int bytes = int_size_in_bytes (valtype);
+ unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
enum machine_mode tmpmode;
+
for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT);
tmpmode != VOIDmode;
tmpmode = GET_MODE_WIDER_MODE (tmpmode))