/* Emit RTL for the GCC expander.
Copyright (C) 1987, 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
Free Software Foundation, Inc.
This file is part of GCC.
#include "langhooks.h"
#include "tree-pass.h"
#include "df.h"
+#include "params.h"
+#include "target.h"
/* Commonly used modes. */
enum machine_mode double_mode; /* Mode whose width is DOUBLE_TYPE_SIZE. */
enum machine_mode ptr_mode; /* Mode whose width is POINTER_SIZE. */
+/* Datastructures maintained for currently processed function in RTL form. */
+
+struct rtl_data x_rtl;
+
+/* Indexed by pseudo register number, gives the rtx for that pseudo.
+ Allocated in parallel with regno_pointer_align.
+ FIXME: We could put it into emit_status struct, but gengtype is not able to deal
+ with length attribute nested in top level structures. */
+
+rtx * regno_reg_rtx;
/* This is *not* reset after each function. It gives each CODE_LABEL
in the entire compilation a unique label number. */
static GTY(()) int label_num = 1;
-/* Nonzero means do not generate NOTEs for source line numbers. */
-
-static int no_line_numbers;
-
/* Commonly used rtx's, so that we only need space for one copy.
These are initialized once for the entire compilation.
All of these are unique; no other rtx-object will be equal to any
REAL_VALUE_TYPE dconst0;
REAL_VALUE_TYPE dconst1;
REAL_VALUE_TYPE dconst2;
-REAL_VALUE_TYPE dconst3;
-REAL_VALUE_TYPE dconst10;
REAL_VALUE_TYPE dconstm1;
-REAL_VALUE_TYPE dconstm2;
REAL_VALUE_TYPE dconsthalf;
-REAL_VALUE_TYPE dconstthird;
-REAL_VALUE_TYPE dconstsqrt2;
-REAL_VALUE_TYPE dconste;
/* Record fixed-point constant 0 and 1. */
FIXED_VALUE_TYPE fconst0[MAX_FCONST0];
In an inline procedure, the stack and frame pointer rtxs may not be
used for anything else. */
-rtx static_chain_rtx; /* (REG:Pmode STATIC_CHAIN_REGNUM) */
-rtx static_chain_incoming_rtx; /* (REG:Pmode STATIC_CHAIN_INCOMING_REGNUM) */
rtx pic_offset_table_rtx; /* (REG:Pmode PIC_OFFSET_TABLE_REGNUM) */
/* This is used to implement __builtin_return_address for some machines.
static GTY ((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
htab_t const_double_htab;
-#define first_insn (cfun->emit->x_first_insn)
-#define last_insn (cfun->emit->x_last_insn)
-#define cur_insn_uid (cfun->emit->x_cur_insn_uid)
-#define last_location (cfun->emit->x_last_location)
-#define first_label_num (cfun->emit->x_first_label_num)
+/* A hash table storing all CONST_FIXEDs. */
+static GTY ((if_marked ("ggc_marked_p"), param_is (struct rtx_def)))
+ htab_t const_fixed_htab;
+
+#define first_insn (crtl->emit.x_first_insn)
+#define last_insn (crtl->emit.x_last_insn)
+#define cur_insn_uid (crtl->emit.x_cur_insn_uid)
+#define cur_debug_insn_uid (crtl->emit.x_cur_debug_insn_uid)
+#define last_location (crtl->emit.x_last_location)
+#define first_label_num (crtl->emit.x_first_label_num)
static rtx make_call_insn_raw (rtx);
static rtx change_address_1 (rtx, enum machine_mode, rtx, int);
static hashval_t const_double_htab_hash (const void *);
static int const_double_htab_eq (const void *, const void *);
static rtx lookup_const_double (rtx);
+static hashval_t const_fixed_htab_hash (const void *);
+static int const_fixed_htab_eq (const void *, const void *);
+static rtx lookup_const_fixed (rtx);
static hashval_t mem_attrs_htab_hash (const void *);
static int mem_attrs_htab_eq (const void *, const void *);
static mem_attrs *get_mem_attrs (alias_set_type, tree, rtx, rtx, unsigned int,
- enum machine_mode);
+ addr_space_t, enum machine_mode);
static hashval_t reg_attrs_htab_hash (const void *);
static int reg_attrs_htab_eq (const void *, const void *);
static reg_attrs *get_reg_attrs (tree, int);
-static tree component_ref_for_mem_expr (tree);
static rtx gen_const_vector (enum machine_mode, int);
static void copy_rtx_if_shared_1 (rtx *orig);
CONST_DOUBLE_REAL_VALUE (b));
}
+/* Returns a hash code for X (which is really a CONST_FIXED). */
+
+static hashval_t
+const_fixed_htab_hash (const void *x)
+{
+ const_rtx const value = (const_rtx) x;
+ hashval_t h;
+
+ h = fixed_hash (CONST_FIXED_VALUE (value));
+ /* MODE is used in the comparison, so it should be in the hash. */
+ h ^= GET_MODE (value);
+ return h;
+}
+
+/* Returns nonzero if the value represented by X (really a ...)
+ is the same as that represented by Y (really a ...). */
+
+static int
+const_fixed_htab_eq (const void *x, const void *y)
+{
+ const_rtx const a = (const_rtx) x, b = (const_rtx) y;
+
+ if (GET_MODE (a) != GET_MODE (b))
+ return 0;
+ return fixed_identical (CONST_FIXED_VALUE (a), CONST_FIXED_VALUE (b));
+}
+
/* Returns a hash code for X (which is a really a mem_attrs *). */
static hashval_t
const mem_attrs *const p = (const mem_attrs *) x;
return (p->alias ^ (p->align * 1000)
+ ^ (p->addrspace * 4000)
^ ((p->offset ? INTVAL (p->offset) : 0) * 50000)
^ ((p->size ? INTVAL (p->size) : 0) * 2500000)
^ (size_t) iterative_hash_expr (p->expr, 0));
return (p->alias == q->alias && p->offset == q->offset
&& p->size == q->size && p->align == q->align
+ && p->addrspace == q->addrspace
&& (p->expr == q->expr
|| (p->expr != NULL_TREE && q->expr != NULL_TREE
&& operand_equal_p (p->expr, q->expr, 0))));
static mem_attrs *
get_mem_attrs (alias_set_type alias, tree expr, rtx offset, rtx size,
- unsigned int align, enum machine_mode mode)
+ unsigned int align, addr_space_t addrspace, enum machine_mode mode)
{
mem_attrs attrs;
void **slot;
/* If everything is the default, we can just return zero.
This must match what the corresponding MEM_* macros return when the
field is not present. */
- if (alias == 0 && expr == 0 && offset == 0
+ if (alias == 0 && expr == 0 && offset == 0 && addrspace == 0
&& (size == 0
|| (mode != BLKmode && GET_MODE_SIZE (mode) == INTVAL (size)))
&& (STRICT_ALIGNMENT && mode != BLKmode
attrs.offset = offset;
attrs.size = size;
attrs.align = align;
+ attrs.addrspace = addrspace;
slot = htab_find_slot (mem_attrs_htab, &attrs, INSERT);
if (*slot == 0)
memcpy (*slot, &attrs, sizeof (mem_attrs));
}
- return *slot;
+ return (mem_attrs *) *slot;
}
/* Returns a hash code for X (which is a really a reg_attrs *). */
memcpy (*slot, &attrs, sizeof (reg_attrs));
}
- return *slot;
+ return (reg_attrs *) *slot;
}
return lookup_const_double (real);
}
+/* Determine whether FIXED, a CONST_FIXED, already exists in the
+ hash table. If so, return its counterpart; otherwise add it
+ to the hash table and return it. */
+
+static rtx
+lookup_const_fixed (rtx fixed)
+{
+ void **slot = htab_find_slot (const_fixed_htab, fixed, INSERT);
+ if (*slot == 0)
+ *slot = fixed;
+
+ return (rtx) *slot;
+}
+
+/* Return a CONST_FIXED rtx for a fixed-point value specified by
+ VALUE in mode MODE. */
+
+rtx
+const_fixed_from_fixed_value (FIXED_VALUE_TYPE value, enum machine_mode mode)
+{
+ rtx fixed = rtx_alloc (CONST_FIXED);
+ PUT_MODE (fixed, mode);
+
+ fixed->u.fv = value;
+
+ return lookup_const_fixed (fixed);
+}
+
/* Return a CONST_DOUBLE or CONST_INT for a value specified as a pair
of ints: I0 is the low-order word and I1 is the high-order word.
Do not use this routine for non-integer modes; convert to
gen_int_mode.
2) GET_MODE_BITSIZE (mode) == 2 * HOST_BITS_PER_WIDE_INT, but the value of
the integer fits into HOST_WIDE_INT anyway (i.e., i1 consists only
- from copies of the sign bit, and sign of i0 and i1 are the same), then
+ from copies of the sign bit, and sign of i0 and i1 are the same), then
we return a CONST_INT for i0.
3) Otherwise, we create a CONST_DOUBLE for i0 and i1. */
if (mode != VOIDmode)
{
rtx mem = gen_rtx_MEM (mode, addr);
MEM_NOTRAP_P (mem) = 1;
- if (!current_function_calls_alloca)
+ if (!cfun->calls_alloca)
set_mem_alias_set (mem, get_frame_alias_set ());
return mem;
}
bool
validate_subreg (enum machine_mode omode, enum machine_mode imode,
- rtx reg, unsigned int offset)
+ const_rtx reg, unsigned int offset)
{
unsigned int isize = GET_MODE_SIZE (imode);
unsigned int osize = GET_MODE_SIZE (omode);
subreg_lowpart_offset (mode, inmode));
}
\f
-/* gen_rtvec (n, [rt1, ..., rtn])
-**
-** This routine creates an rtvec and stores within it the
-** pointers to rtx's which are its arguments.
-*/
-/*VARARGS1*/
+/* Create an rtvec and stores within it the RTXen passed in the arguments. */
+
rtvec
gen_rtvec (int n, ...)
{
- int i, save_n;
- rtx *vector;
+ int i;
+ rtvec rt_val;
va_list p;
va_start (p, n);
+ /* Don't allocate an empty rtvec... */
if (n == 0)
- return NULL_RTVEC; /* Don't allocate an empty rtvec... */
+ return NULL_RTVEC;
- vector = alloca (n * sizeof (rtx));
+ rt_val = rtvec_alloc (n);
for (i = 0; i < n; i++)
- vector[i] = va_arg (p, rtx);
+ rt_val->elem[i] = va_arg (p, rtx);
- /* The definition of VA_* in K&R C causes `n' to go out of scope. */
- save_n = n;
va_end (p);
-
- return gen_rtvec_v (save_n, vector);
+ return rt_val;
}
rtvec
int i;
rtvec rt_val;
+ /* Don't allocate an empty rtvec... */
if (n == 0)
- return NULL_RTVEC; /* Don't allocate an empty rtvec... */
+ return NULL_RTVEC;
- rt_val = rtvec_alloc (n); /* Allocate an rtvec... */
+ rt_val = rtvec_alloc (n);
for (i = 0; i < n; i++)
rt_val->elem[i] = *argp++;
return rt_val;
}
\f
+/* Return the number of bytes between the start of an OUTER_MODE
+ in-memory value and the start of an INNER_MODE in-memory value,
+ given that the former is a lowpart of the latter. It may be a
+ paradoxical lowpart, in which case the offset will be negative
+ on big-endian targets. */
+
+int
+byte_lowpart_offset (enum machine_mode outer_mode,
+ enum machine_mode inner_mode)
+{
+ if (GET_MODE_SIZE (outer_mode) < GET_MODE_SIZE (inner_mode))
+ return subreg_lowpart_offset (outer_mode, inner_mode);
+ else
+ return -subreg_lowpart_offset (inner_mode, outer_mode);
+}
+\f
/* Generate a REG rtx for a new pseudo register of mode MODE.
This pseudo is assigned the next sequential register number. */
rtx
gen_reg_rtx (enum machine_mode mode)
{
- struct function *f = cfun;
rtx val;
+ unsigned int align = GET_MODE_ALIGNMENT (mode);
gcc_assert (can_create_pseudo_p ());
+ /* If a virtual register with bigger mode alignment is generated,
+ increase stack alignment estimation because it might be spilled
+ to stack later. */
+ if (SUPPORTS_STACK_ALIGNMENT
+ && crtl->stack_alignment_estimated < align
+ && !crtl->stack_realign_processed)
+ {
+ unsigned int min_align = MINIMUM_ALIGNMENT (NULL, mode, align);
+ if (crtl->stack_alignment_estimated < min_align)
+ crtl->stack_alignment_estimated = min_align;
+ }
+
if (generating_concat_p
&& (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
|| GET_MODE_CLASS (mode) == MODE_COMPLEX_INT))
/* Make sure regno_pointer_align, and regno_reg_rtx are large
enough to have an element for this pseudo reg number. */
- if (reg_rtx_no == f->emit->regno_pointer_align_length)
+ if (reg_rtx_no == crtl->emit.regno_pointer_align_length)
{
- int old_size = f->emit->regno_pointer_align_length;
- char *new;
+ int old_size = crtl->emit.regno_pointer_align_length;
+ char *tmp;
rtx *new1;
- new = ggc_realloc (f->emit->regno_pointer_align, old_size * 2);
- memset (new + old_size, 0, old_size);
- f->emit->regno_pointer_align = (unsigned char *) new;
+ tmp = XRESIZEVEC (char, crtl->emit.regno_pointer_align, old_size * 2);
+ memset (tmp + old_size, 0, old_size);
+ crtl->emit.regno_pointer_align = (unsigned char *) tmp;
- new1 = ggc_realloc (f->emit->x_regno_reg_rtx,
- old_size * 2 * sizeof (rtx));
+ new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, old_size * 2);
memset (new1 + old_size, 0, old_size * sizeof (rtx));
regno_reg_rtx = new1;
- f->emit->regno_pointer_align_length = old_size * 2;
+ crtl->emit.regno_pointer_align_length = old_size * 2;
}
val = gen_raw_REG (mode, reg_rtx_no);
return val;
}
-/* Update NEW with the same attributes as REG, but offsetted by OFFSET.
- Do the big endian correction if needed. */
+/* Update NEW with the same attributes as REG, but with OFFSET added
+ to the REG_OFFSET. */
static void
-update_reg_offset (rtx new, rtx reg, int offset)
+update_reg_offset (rtx new_rtx, rtx reg, int offset)
{
- tree decl;
- HOST_WIDE_INT var_size;
-
- /* PR middle-end/14084
- The problem appears when a variable is stored in a larger register
- and later it is used in the original mode or some mode in between
- or some part of variable is accessed.
-
- On little endian machines there is no problem because
- the REG_OFFSET of the start of the variable is the same when
- accessed in any mode (it is 0).
-
- However, this is not true on big endian machines.
- The offset of the start of the variable is different when accessed
- in different modes.
- When we are taking a part of the REG we have to change the OFFSET
- from offset WRT size of mode of REG to offset WRT size of variable.
-
- If we would not do the big endian correction the resulting REG_OFFSET
- would be larger than the size of the DECL.
-
- Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
-
- REG.mode MODE DECL size old offset new offset description
- DI SI 4 4 0 int32 in SImode
- DI SI 1 4 0 char in SImode
- DI QI 1 7 0 char in QImode
- DI QI 4 5 1 1st element in QImode
- of char[4]
- DI HI 4 6 2 1st element in HImode
- of int16[2]
-
- If the size of DECL is equal or greater than the size of REG
- we can't do this correction because the register holds the
- whole variable or a part of the variable and thus the REG_OFFSET
- is already correct. */
-
- decl = REG_EXPR (reg);
- if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
- && decl != NULL
- && offset > 0
- && GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (GET_MODE (new))
- && ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
- && var_size < GET_MODE_SIZE (GET_MODE (reg))))
- {
- int offset_le;
-
- /* Convert machine endian to little endian WRT size of mode of REG. */
- if (WORDS_BIG_ENDIAN)
- offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
- / UNITS_PER_WORD) * UNITS_PER_WORD;
- else
- offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
-
- if (BYTES_BIG_ENDIAN)
- offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
- % UNITS_PER_WORD);
- else
- offset_le += offset % UNITS_PER_WORD;
-
- if (offset_le >= var_size)
- {
- /* MODE is wider than the variable so the new reg will cover
- the whole variable so the resulting OFFSET should be 0. */
- offset = 0;
- }
- else
- {
- /* Convert little endian to machine endian WRT size of variable. */
- if (WORDS_BIG_ENDIAN)
- offset = ((var_size - 1 - offset_le)
- / UNITS_PER_WORD) * UNITS_PER_WORD;
- else
- offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
-
- if (BYTES_BIG_ENDIAN)
- offset += ((var_size - 1 - offset_le)
- % UNITS_PER_WORD);
- else
- offset += offset_le % UNITS_PER_WORD;
- }
- }
-
- REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
+ REG_ATTRS (new_rtx) = get_reg_attrs (REG_EXPR (reg),
REG_OFFSET (reg) + offset);
}
-/* Generate a register with same attributes as REG, but offsetted by
- OFFSET. */
+/* Generate a register with same attributes as REG, but with OFFSET
+ added to the REG_OFFSET. */
rtx
gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno,
int offset)
{
- rtx new = gen_rtx_REG (mode, regno);
+ rtx new_rtx = gen_rtx_REG (mode, regno);
- update_reg_offset (new, reg, offset);
- return new;
+ update_reg_offset (new_rtx, reg, offset);
+ return new_rtx;
}
/* Generate a new pseudo-register with the same attributes as REG, but
- offsetted by OFFSET. */
+ with OFFSET added to the REG_OFFSET. */
rtx
gen_reg_rtx_offset (rtx reg, enum machine_mode mode, int offset)
{
- rtx new = gen_reg_rtx (mode);
+ rtx new_rtx = gen_reg_rtx (mode);
+
+ update_reg_offset (new_rtx, reg, offset);
+ return new_rtx;
+}
- update_reg_offset (new, reg, offset);
- return new;
+/* Adjust REG in-place so that it has mode MODE. It is assumed that the
+ new register is a (possibly paradoxical) lowpart of the old one. */
+
+void
+adjust_reg_mode (rtx reg, enum machine_mode mode)
+{
+ update_reg_offset (reg, reg, byte_lowpart_offset (mode, GET_MODE (reg)));
+ PUT_MODE (reg, mode);
}
-/* Set the decl for MEM to DECL. */
+/* Copy REG's attributes from X, if X has any attributes. If REG and X
+ have different modes, REG is a (possibly paradoxical) lowpart of X. */
void
-set_reg_attrs_from_mem (rtx reg, rtx mem)
+set_reg_attrs_from_value (rtx reg, rtx x)
+{
+ int offset;
+
+ /* Hard registers can be reused for multiple purposes within the same
+ function, so setting REG_ATTRS, REG_POINTER and REG_POINTER_ALIGN
+ on them is wrong. */
+ if (HARD_REGISTER_P (reg))
+ return;
+
+ offset = byte_lowpart_offset (GET_MODE (reg), GET_MODE (x));
+ if (MEM_P (x))
+ {
+ if (MEM_OFFSET (x) && CONST_INT_P (MEM_OFFSET (x)))
+ REG_ATTRS (reg)
+ = get_reg_attrs (MEM_EXPR (x), INTVAL (MEM_OFFSET (x)) + offset);
+ if (MEM_POINTER (x))
+ mark_reg_pointer (reg, 0);
+ }
+ else if (REG_P (x))
+ {
+ if (REG_ATTRS (x))
+ update_reg_offset (reg, x, offset);
+ if (REG_POINTER (x))
+ mark_reg_pointer (reg, REGNO_POINTER_ALIGN (REGNO (x)));
+ }
+}
+
+/* Generate a REG rtx for a new pseudo register, copying the mode
+ and attributes from X. */
+
+rtx
+gen_reg_rtx_and_attrs (rtx x)
{
- if (MEM_OFFSET (mem) && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
- REG_ATTRS (reg)
- = get_reg_attrs (MEM_EXPR (mem), INTVAL (MEM_OFFSET (mem)));
+ rtx reg = gen_reg_rtx (GET_MODE (x));
+ set_reg_attrs_from_value (reg, x);
+ return reg;
}
/* Set the register attributes for registers contained in PARM_RTX.
set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
{
if (REG_P (parm_rtx))
- set_reg_attrs_from_mem (parm_rtx, mem);
+ set_reg_attrs_from_value (parm_rtx, mem);
else if (GET_CODE (parm_rtx) == PARALLEL)
{
/* Check for a NULL entry in the first slot, used to indicate that the
}
}
-/* Assign the RTX X to declaration T. */
+/* Set the REG_ATTRS for registers in value X, given that X represents
+ decl T. */
+
void
-set_decl_rtl (tree t, rtx x)
+set_reg_attrs_for_decl_rtl (tree t, rtx x)
{
- DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
-
- if (!x)
- return;
- /* For register, we maintain the reverse information too. */
- if (REG_P (x))
- REG_ATTRS (x) = get_reg_attrs (t, 0);
- else if (GET_CODE (x) == SUBREG)
- REG_ATTRS (SUBREG_REG (x))
- = get_reg_attrs (t, -SUBREG_BYTE (x));
- if (GET_CODE (x) == CONCAT)
- {
- if (REG_P (XEXP (x, 0)))
- REG_ATTRS (XEXP (x, 0)) = get_reg_attrs (t, 0);
- if (REG_P (XEXP (x, 1)))
- REG_ATTRS (XEXP (x, 1))
- = get_reg_attrs (t, GET_MODE_UNIT_SIZE (GET_MODE (XEXP (x, 0))));
- }
- if (GET_CODE (x) == PARALLEL)
+ if (GET_CODE (x) == SUBREG)
{
- int i;
- for (i = 0; i < XVECLEN (x, 0); i++)
- {
- rtx y = XVECEXP (x, 0, i);
- if (REG_P (XEXP (y, 0)))
- REG_ATTRS (XEXP (y, 0)) = get_reg_attrs (t, INTVAL (XEXP (y, 1)));
- }
+ gcc_assert (subreg_lowpart_p (x));
+ x = SUBREG_REG (x);
}
-}
-
-/* Assign the RTX X to parameter declaration T. */
-void
-set_decl_incoming_rtl (tree t, rtx x)
-{
- DECL_INCOMING_RTL (t) = x;
-
- if (!x)
- return;
- /* For register, we maintain the reverse information too. */
if (REG_P (x))
- REG_ATTRS (x) = get_reg_attrs (t, 0);
- else if (GET_CODE (x) == SUBREG)
- REG_ATTRS (SUBREG_REG (x))
- = get_reg_attrs (t, -SUBREG_BYTE (x));
+ REG_ATTRS (x)
+ = get_reg_attrs (t, byte_lowpart_offset (GET_MODE (x),
+ DECL_MODE (t)));
if (GET_CODE (x) == CONCAT)
{
if (REG_P (XEXP (x, 0)))
}
}
+/* Assign the RTX X to declaration T. */
+
+void
+set_decl_rtl (tree t, rtx x)
+{
+ DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
+ if (x)
+ set_reg_attrs_for_decl_rtl (t, x);
+}
+
+/* Assign the RTX X to parameter declaration T. BY_REFERENCE_P is true
+ if the ABI requires the parameter to be passed by reference. */
+
+void
+set_decl_incoming_rtl (tree t, rtx x, bool by_reference_p)
+{
+ DECL_INCOMING_RTL (t) = x;
+ if (x && !by_reference_p)
+ set_reg_attrs_for_decl_rtl (t, x);
+}
+
/* Identify REG (which may be a CONCAT) as a user register. */
void
/* If the rtx for label was created during the expansion of a nested
function, then first_label_num won't include this label number.
- Fix this now so that array indicies work later. */
+ Fix this now so that array indices work later. */
void
maybe_set_first_label_num (rtx x)
/* Unfortunately, this routine doesn't take a parameter for the mode of X,
so we have to make one up. Yuk. */
innermode = GET_MODE (x);
- if (GET_CODE (x) == CONST_INT
+ if (CONST_INT_P (x)
&& msize * BITS_PER_UNIT <= HOST_BITS_PER_WIDE_INT)
innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0);
else if (innermode == VOIDmode)
innermode = mode_for_size (HOST_BITS_PER_WIDE_INT * 2, MODE_INT, 0);
-
+
xsize = GET_MODE_SIZE (innermode);
gcc_assert (innermode != VOIDmode && innermode != BLKmode);
}
else if (GET_CODE (x) == SUBREG || REG_P (x)
|| GET_CODE (x) == CONCAT || GET_CODE (x) == CONST_VECTOR
- || GET_CODE (x) == CONST_DOUBLE || GET_CODE (x) == CONST_INT)
+ || GET_CODE (x) == CONST_DOUBLE || CONST_INT_P (x))
return simplify_gen_subreg (mode, x, innermode, offset);
/* Otherwise, we can't do this. */
result = simplify_gen_subreg (mode, x, GET_MODE (x),
subreg_highpart_offset (mode, GET_MODE (x)));
gcc_assert (result);
-
+
/* simplify_gen_subreg is not guaranteed to return a valid operand for
the target if we have a MEM. gen_highpart must return a valid operand,
emitting code if necessary to do so. */
result = validize_mem (result);
gcc_assert (result);
}
-
+
return result;
}
subreg_highpart_offset (outermode, innermode));
}
-/* Return offset in bytes to get OUTERMODE low part
- of the value in mode INNERMODE stored in memory in target format. */
+/* Return the SUBREG_BYTE for an OUTERMODE lowpart of an INNERMODE value. */
unsigned int
subreg_lowpart_offset (enum machine_mode outermode, enum machine_mode innermode)
/* Form a new MEM at the requested address. */
if (MEM_P (op))
{
- rtx new = adjust_address_nv (op, word_mode, offset * UNITS_PER_WORD);
+ rtx new_rtx = adjust_address_nv (op, word_mode, offset * UNITS_PER_WORD);
if (! validate_address)
- return new;
+ return new_rtx;
else if (reload_completed)
{
- if (! strict_memory_address_p (word_mode, XEXP (new, 0)))
+ if (! strict_memory_address_addr_space_p (word_mode,
+ XEXP (new_rtx, 0),
+ MEM_ADDR_SPACE (op)))
return 0;
}
else
- return replace_equiv_address (new, XEXP (new, 0));
+ return replace_equiv_address (new_rtx, XEXP (new_rtx, 0));
}
/* Rest can be handled by simplify_subreg. */
return result;
}
\f
-/* Within a MEM_EXPR, we care about either (1) a component ref of a decl,
- or (2) a component ref of something variable. Represent the later with
- a NULL expression. */
-
-static tree
-component_ref_for_mem_expr (tree ref)
-{
- tree inner = TREE_OPERAND (ref, 0);
-
- if (TREE_CODE (inner) == COMPONENT_REF)
- inner = component_ref_for_mem_expr (inner);
- else
- {
- /* Now remove any conversions: they don't change what the underlying
- object is. Likewise for SAVE_EXPR. */
- while (TREE_CODE (inner) == NOP_EXPR || TREE_CODE (inner) == CONVERT_EXPR
- || TREE_CODE (inner) == NON_LVALUE_EXPR
- || TREE_CODE (inner) == VIEW_CONVERT_EXPR
- || TREE_CODE (inner) == SAVE_EXPR)
- inner = TREE_OPERAND (inner, 0);
-
- if (! DECL_P (inner))
- inner = NULL_TREE;
- }
-
- if (inner == TREE_OPERAND (ref, 0))
- return ref;
- else
- return build3 (COMPONENT_REF, TREE_TYPE (ref), inner,
- TREE_OPERAND (ref, 1), NULL_TREE);
-}
-
/* Returns 1 if both MEM_EXPR can be considered equal
and 0 otherwise. */
if (TREE_CODE (expr1) != TREE_CODE (expr2))
return 0;
- if (TREE_CODE (expr1) == COMPONENT_REF)
- return
- mem_expr_equal_p (TREE_OPERAND (expr1, 0),
- TREE_OPERAND (expr2, 0))
- && mem_expr_equal_p (TREE_OPERAND (expr1, 1), /* field decl */
- TREE_OPERAND (expr2, 1));
-
- if (INDIRECT_REF_P (expr1))
- return mem_expr_equal_p (TREE_OPERAND (expr1, 0),
- TREE_OPERAND (expr2, 0));
-
- /* ARRAY_REFs, ARRAY_RANGE_REFs and BIT_FIELD_REFs should already
- have been resolved here. */
- gcc_assert (DECL_P (expr1));
-
- /* Decls with different pointers can't be equal. */
- return 0;
+ return operand_equal_p (expr1, expr2, 0);
+}
+
+/* Return OFFSET if XEXP (MEM, 0) - OFFSET is known to be ALIGN
+ bits aligned for 0 <= OFFSET < ALIGN / BITS_PER_UNIT, or
+ -1 if not known. */
+
+int
+get_mem_align_offset (rtx mem, unsigned int align)
+{
+ tree expr;
+ unsigned HOST_WIDE_INT offset;
+
+ /* This function can't use
+ if (!MEM_EXPR (mem) || !MEM_OFFSET (mem)
+ || !CONST_INT_P (MEM_OFFSET (mem))
+ || (get_object_alignment (MEM_EXPR (mem), MEM_ALIGN (mem), align)
+ < align))
+ return -1;
+ else
+ return (- INTVAL (MEM_OFFSET (mem))) & (align / BITS_PER_UNIT - 1);
+ for two reasons:
+ - COMPONENT_REFs in MEM_EXPR can have NULL first operand,
+ for <variable>. get_inner_reference doesn't handle it and
+ even if it did, the alignment in that case needs to be determined
+ from DECL_FIELD_CONTEXT's TYPE_ALIGN.
+ - it would do suboptimal job for COMPONENT_REFs, even if MEM_EXPR
+ isn't sufficiently aligned, the object it is in might be. */
+ gcc_assert (MEM_P (mem));
+ expr = MEM_EXPR (mem);
+ if (expr == NULL_TREE
+ || MEM_OFFSET (mem) == NULL_RTX
+ || !CONST_INT_P (MEM_OFFSET (mem)))
+ return -1;
+
+ offset = INTVAL (MEM_OFFSET (mem));
+ if (DECL_P (expr))
+ {
+ if (DECL_ALIGN (expr) < align)
+ return -1;
+ }
+ else if (INDIRECT_REF_P (expr))
+ {
+ if (TYPE_ALIGN (TREE_TYPE (expr)) < (unsigned int) align)
+ return -1;
+ }
+ else if (TREE_CODE (expr) == COMPONENT_REF)
+ {
+ while (1)
+ {
+ tree inner = TREE_OPERAND (expr, 0);
+ tree field = TREE_OPERAND (expr, 1);
+ tree byte_offset = component_ref_field_offset (expr);
+ tree bit_offset = DECL_FIELD_BIT_OFFSET (field);
+
+ if (!byte_offset
+ || !host_integerp (byte_offset, 1)
+ || !host_integerp (bit_offset, 1))
+ return -1;
+
+ offset += tree_low_cst (byte_offset, 1);
+ offset += tree_low_cst (bit_offset, 1) / BITS_PER_UNIT;
+
+ if (inner == NULL_TREE)
+ {
+ if (TYPE_ALIGN (DECL_FIELD_CONTEXT (field))
+ < (unsigned int) align)
+ return -1;
+ break;
+ }
+ else if (DECL_P (inner))
+ {
+ if (DECL_ALIGN (inner) < align)
+ return -1;
+ break;
+ }
+ else if (TREE_CODE (inner) != COMPONENT_REF)
+ return -1;
+ expr = inner;
+ }
+ }
+ else
+ return -1;
+
+ return offset & ((align / BITS_PER_UNIT) - 1);
}
-/* Given REF, a MEM, and T, either the type of X or the expression
+/* 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. BITPOS is nonzero if
there is an offset outstanding on T that will be applied later. */
/* We can set the alignment from the type if we are making an object,
this is an INDIRECT_REF, or if TYPE_ALIGN_OK. */
- if (objectp || TREE_CODE (t) == INDIRECT_REF
- || TREE_CODE (t) == ALIGN_INDIRECT_REF
+ if (objectp || TREE_CODE (t) == INDIRECT_REF
+ || TREE_CODE (t) == ALIGN_INDIRECT_REF
|| TYPE_ALIGN_OK (type))
align = MAX (align, TYPE_ALIGN (type));
- else
+ else
if (TREE_CODE (t) == MISALIGNED_INDIRECT_REF)
{
if (integer_zerop (TREE_OPERAND (t, 1)))
if (! TYPE_P (t))
{
tree base;
+ bool align_computed = false;
if (TREE_THIS_VOLATILE (t))
MEM_VOLATILE_P (ref) = 1;
/* Now remove any conversions: 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
+ while (CONVERT_EXPR_P (t)
|| TREE_CODE (t) == VIEW_CONVERT_EXPR
|| TREE_CODE (t) == SAVE_EXPR)
t = TREE_OPERAND (t, 0);
&& host_integerp (DECL_SIZE_UNIT (t), 1)
? GEN_INT (tree_low_cst (DECL_SIZE_UNIT (t), 1)) : 0);
align = DECL_ALIGN (t);
+ align_computed = true;
}
/* If this is a constant, we know the alignment. */
#ifdef CONSTANT_ALIGNMENT
align = CONSTANT_ALIGNMENT (t, align);
#endif
+ align_computed = true;
}
/* If this is a field reference and not a bit-field, record it. */
- /* ??? There is some information that can be gleened from bit-fields,
+ /* ??? There is some information that can be gleaned from bit-fields,
such as the word offset in the structure that might be modified.
But skip it for now. */
else if (TREE_CODE (t) == COMPONENT_REF
&& ! DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
{
- expr = component_ref_for_mem_expr (t);
+ expr = t;
offset = const0_rtx;
apply_bitpos = bitpos;
/* ??? Any reason the field size would be different than
align = DECL_ALIGN (t2);
if (aoff && (unsigned HOST_WIDE_INT) aoff < align)
align = aoff;
+ align_computed = true;
offset = GEN_INT (ioff);
apply_bitpos = bitpos;
}
}
else if (TREE_CODE (t2) == COMPONENT_REF)
{
- expr = component_ref_for_mem_expr (t2);
+ expr = t2;
+ offset = NULL;
if (host_integerp (off_tree, 1))
{
offset = GEN_INT (tree_low_cst (off_tree, 1));
expr = t;
offset = NULL;
}
+
+ if (!align_computed && !INDIRECT_REF_P (t))
+ {
+ unsigned int obj_align
+ = get_object_alignment (t, align, BIGGEST_ALIGNMENT);
+ align = MAX (align, obj_align);
+ }
}
/* If we modified OFFSET based on T, then subtract the outstanding
if (TREE_CODE (t) == ALIGN_INDIRECT_REF)
{
- /* Force EXPR and OFFSE to NULL, since we don't know exactly what
+ /* Force EXPR and OFFSET to NULL, since we don't know exactly what
we're overlapping. */
offset = NULL;
expr = NULL;
/* Now set the attributes we computed above. */
MEM_ATTRS (ref)
- = get_mem_attrs (alias, expr, offset, size, align, GET_MODE (ref));
+ = get_mem_attrs (alias, expr, offset, size, align,
+ TYPE_ADDR_SPACE (type), GET_MODE (ref));
/* If this is already known to be a scalar or aggregate, we are done. */
if (MEM_IN_STRUCT_P (ref) || MEM_SCALAR_P (ref))
set_mem_attributes_minus_bitpos (ref, t, objectp, 0);
}
-/* Set the decl for MEM to DECL. */
-
-void
-set_mem_attrs_from_reg (rtx mem, rtx reg)
-{
- MEM_ATTRS (mem)
- = get_mem_attrs (MEM_ALIAS_SET (mem), REG_EXPR (reg),
- GEN_INT (REG_OFFSET (reg)),
- MEM_SIZE (mem), MEM_ALIGN (mem), GET_MODE (mem));
-}
-
/* Set the alias set of MEM to SET. */
void
MEM_ATTRS (mem) = get_mem_attrs (set, MEM_EXPR (mem), MEM_OFFSET (mem),
MEM_SIZE (mem), MEM_ALIGN (mem),
- GET_MODE (mem));
+ MEM_ADDR_SPACE (mem), GET_MODE (mem));
+}
+
+/* Set the address space of MEM to ADDRSPACE (target-defined). */
+
+void
+set_mem_addr_space (rtx mem, addr_space_t addrspace)
+{
+ MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),
+ MEM_OFFSET (mem), MEM_SIZE (mem),
+ MEM_ALIGN (mem), addrspace, GET_MODE (mem));
}
/* Set the alignment of MEM to ALIGN bits. */
{
MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),
MEM_OFFSET (mem), MEM_SIZE (mem), align,
- GET_MODE (mem));
+ MEM_ADDR_SPACE (mem), GET_MODE (mem));
}
/* Set the expr for MEM to EXPR. */
{
MEM_ATTRS (mem)
= get_mem_attrs (MEM_ALIAS_SET (mem), expr, MEM_OFFSET (mem),
- MEM_SIZE (mem), MEM_ALIGN (mem), GET_MODE (mem));
+ MEM_SIZE (mem), MEM_ALIGN (mem),
+ MEM_ADDR_SPACE (mem), GET_MODE (mem));
}
/* Set the offset of MEM to OFFSET. */
{
MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),
offset, MEM_SIZE (mem), MEM_ALIGN (mem),
- GET_MODE (mem));
+ MEM_ADDR_SPACE (mem), GET_MODE (mem));
}
/* Set the size of MEM to SIZE. */
{
MEM_ATTRS (mem) = get_mem_attrs (MEM_ALIAS_SET (mem), MEM_EXPR (mem),
MEM_OFFSET (mem), size, MEM_ALIGN (mem),
- GET_MODE (mem));
+ MEM_ADDR_SPACE (mem), GET_MODE (mem));
}
\f
/* Return a memory reference like MEMREF, but with its mode changed to MODE
static rtx
change_address_1 (rtx memref, enum machine_mode mode, rtx addr, int validate)
{
- rtx new;
+ addr_space_t as;
+ rtx new_rtx;
gcc_assert (MEM_P (memref));
+ as = MEM_ADDR_SPACE (memref);
if (mode == VOIDmode)
mode = GET_MODE (memref);
if (addr == 0)
addr = XEXP (memref, 0);
if (mode == GET_MODE (memref) && addr == XEXP (memref, 0)
- && (!validate || memory_address_p (mode, addr)))
+ && (!validate || memory_address_addr_space_p (mode, addr, as)))
return memref;
if (validate)
{
if (reload_in_progress || reload_completed)
- gcc_assert (memory_address_p (mode, addr));
+ gcc_assert (memory_address_addr_space_p (mode, addr, as));
else
- addr = memory_address (mode, addr);
+ addr = memory_address_addr_space (mode, addr, as);
}
if (rtx_equal_p (addr, XEXP (memref, 0)) && mode == GET_MODE (memref))
return memref;
- new = gen_rtx_MEM (mode, addr);
- MEM_COPY_ATTRIBUTES (new, memref);
- return new;
+ new_rtx = gen_rtx_MEM (mode, addr);
+ MEM_COPY_ATTRIBUTES (new_rtx, memref);
+ return new_rtx;
}
/* Like change_address_1 with VALIDATE nonzero, but we are not saying in what
rtx
change_address (rtx memref, enum machine_mode mode, rtx addr)
{
- rtx new = change_address_1 (memref, mode, addr, 1), size;
- enum machine_mode mmode = GET_MODE (new);
+ rtx new_rtx = change_address_1 (memref, mode, addr, 1), size;
+ enum machine_mode mmode = GET_MODE (new_rtx);
unsigned int align;
size = mmode == BLKmode ? 0 : GEN_INT (GET_MODE_SIZE (mmode));
align = mmode == BLKmode ? BITS_PER_UNIT : GET_MODE_ALIGNMENT (mmode);
/* If there are no changes, just return the original memory reference. */
- if (new == memref)
+ if (new_rtx == memref)
{
if (MEM_ATTRS (memref) == 0
|| (MEM_EXPR (memref) == NULL
&& MEM_OFFSET (memref) == NULL
&& MEM_SIZE (memref) == size
&& MEM_ALIGN (memref) == align))
- return new;
+ return new_rtx;
- new = gen_rtx_MEM (mmode, XEXP (memref, 0));
- MEM_COPY_ATTRIBUTES (new, memref);
+ new_rtx = gen_rtx_MEM (mmode, XEXP (memref, 0));
+ MEM_COPY_ATTRIBUTES (new_rtx, memref);
}
- MEM_ATTRS (new)
- = get_mem_attrs (MEM_ALIAS_SET (memref), 0, 0, size, align, mmode);
+ MEM_ATTRS (new_rtx)
+ = get_mem_attrs (MEM_ALIAS_SET (memref), 0, 0, size, align,
+ MEM_ADDR_SPACE (memref), mmode);
- return new;
+ return new_rtx;
}
/* Return a memory reference like MEMREF, but with its mode changed
int validate, int adjust)
{
rtx addr = XEXP (memref, 0);
- rtx new;
+ rtx new_rtx;
rtx memoffset = MEM_OFFSET (memref);
rtx size = 0;
unsigned int memalign = MEM_ALIGN (memref);
+ addr_space_t as = MEM_ADDR_SPACE (memref);
+ enum machine_mode address_mode = targetm.addr_space.address_mode (as);
+ int pbits;
/* If there are no changes, just return the original memory reference. */
if (mode == GET_MODE (memref) && !offset
- && (!validate || memory_address_p (mode, addr)))
+ && (!validate || memory_address_addr_space_p (mode, addr, as)))
return memref;
/* ??? Prefer to create garbage instead of creating shared rtl.
(plus (plus reg reg) const_int) -- so do this always. */
addr = copy_rtx (addr);
+ /* Convert a possibly large offset to a signed value within the
+ range of the target address space. */
+ pbits = GET_MODE_BITSIZE (address_mode);
+ if (HOST_BITS_PER_WIDE_INT > pbits)
+ {
+ int shift = HOST_BITS_PER_WIDE_INT - pbits;
+ offset = (((HOST_WIDE_INT) ((unsigned HOST_WIDE_INT) offset << shift))
+ >> shift);
+ }
+
if (adjust)
{
/* If MEMREF is a LO_SUM and the offset is within the alignment of the
&& offset >= 0
&& (unsigned HOST_WIDE_INT) offset
< GET_MODE_ALIGNMENT (GET_MODE (memref)) / BITS_PER_UNIT)
- addr = gen_rtx_LO_SUM (Pmode, XEXP (addr, 0),
+ addr = gen_rtx_LO_SUM (address_mode, XEXP (addr, 0),
plus_constant (XEXP (addr, 1), offset));
else
addr = plus_constant (addr, offset);
}
- new = change_address_1 (memref, mode, addr, validate);
+ new_rtx = change_address_1 (memref, mode, addr, validate);
+
+ /* If the address is a REG, change_address_1 rightfully returns memref,
+ but this would destroy memref's MEM_ATTRS. */
+ if (new_rtx == memref && offset != 0)
+ new_rtx = copy_rtx (new_rtx);
/* Compute the new values of the memory attributes due to this adjustment.
We add the offsets and update the alignment. */
(unsigned HOST_WIDE_INT) (offset & -offset) * BITS_PER_UNIT);
/* We can compute the size in a number of ways. */
- if (GET_MODE (new) != BLKmode)
- size = GEN_INT (GET_MODE_SIZE (GET_MODE (new)));
+ if (GET_MODE (new_rtx) != BLKmode)
+ size = GEN_INT (GET_MODE_SIZE (GET_MODE (new_rtx)));
else if (MEM_SIZE (memref))
size = plus_constant (MEM_SIZE (memref), -offset);
- MEM_ATTRS (new) = get_mem_attrs (MEM_ALIAS_SET (memref), MEM_EXPR (memref),
- memoffset, size, memalign, GET_MODE (new));
+ MEM_ATTRS (new_rtx) = get_mem_attrs (MEM_ALIAS_SET (memref), MEM_EXPR (memref),
+ memoffset, size, memalign, as,
+ GET_MODE (new_rtx));
/* At some point, we should validate that this offset is within the object,
if all the appropriate values are known. */
- return new;
+ return new_rtx;
}
/* Return a memory reference like MEMREF, but with its mode changed
to MODE and its address changed to ADDR, which is assumed to be
- MEMREF offseted by OFFSET bytes. If VALIDATE is
+ MEMREF offset by OFFSET bytes. If VALIDATE is
nonzero, the memory address is forced to be valid. */
rtx
rtx
offset_address (rtx memref, rtx offset, unsigned HOST_WIDE_INT pow2)
{
- rtx new, addr = XEXP (memref, 0);
+ rtx new_rtx, addr = XEXP (memref, 0);
+ addr_space_t as = MEM_ADDR_SPACE (memref);
+ enum machine_mode address_mode = targetm.addr_space.address_mode (as);
- new = simplify_gen_binary (PLUS, Pmode, addr, offset);
+ new_rtx = simplify_gen_binary (PLUS, address_mode, addr, offset);
/* At this point we don't know _why_ the address is invalid. It
could have secondary memory references, multiplies or anything.
being able to recognize the magic around pic_offset_table_rtx.
This stuff is fragile, and is yet another example of why it is
bad to expose PIC machinery too early. */
- if (! memory_address_p (GET_MODE (memref), new)
+ if (! memory_address_addr_space_p (GET_MODE (memref), new_rtx, as)
&& GET_CODE (addr) == PLUS
&& XEXP (addr, 0) == pic_offset_table_rtx)
{
addr = force_reg (GET_MODE (addr), addr);
- new = simplify_gen_binary (PLUS, Pmode, addr, offset);
+ new_rtx = simplify_gen_binary (PLUS, address_mode, addr, offset);
}
- update_temp_slot_address (XEXP (memref, 0), new);
- new = change_address_1 (memref, VOIDmode, new, 1);
+ update_temp_slot_address (XEXP (memref, 0), new_rtx);
+ new_rtx = change_address_1 (memref, VOIDmode, new_rtx, 1);
/* If there are no changes, just return the original memory reference. */
- if (new == memref)
- return new;
+ if (new_rtx == memref)
+ return new_rtx;
/* Update the alignment to reflect the offset. Reset the offset, which
we don't know. */
- MEM_ATTRS (new)
+ MEM_ATTRS (new_rtx)
= get_mem_attrs (MEM_ALIAS_SET (memref), MEM_EXPR (memref), 0, 0,
MIN (MEM_ALIGN (memref), pow2 * BITS_PER_UNIT),
- GET_MODE (new));
- return new;
+ as, GET_MODE (new_rtx));
+ return new_rtx;
}
/* Return a memory reference like MEMREF, but with its address changed to
rtx
widen_memory_access (rtx memref, enum machine_mode mode, HOST_WIDE_INT offset)
{
- rtx new = adjust_address_1 (memref, mode, offset, 1, 1);
- tree expr = MEM_EXPR (new);
- rtx memoffset = MEM_OFFSET (new);
+ rtx new_rtx = adjust_address_1 (memref, mode, offset, 1, 1);
+ tree expr = MEM_EXPR (new_rtx);
+ rtx memoffset = MEM_OFFSET (new_rtx);
unsigned int size = GET_MODE_SIZE (mode);
/* If there are no changes, just return the original memory reference. */
- if (new == memref)
- return new;
+ if (new_rtx == memref)
+ return new_rtx;
/* If we don't know what offset we were at within the expression, then
we can't know if we've overstepped the bounds. */
/* The widened memory may alias other stuff, so zap the alias set. */
/* ??? Maybe use get_alias_set on any remaining expression. */
- MEM_ATTRS (new) = get_mem_attrs (0, expr, memoffset, GEN_INT (size),
- MEM_ALIGN (new), mode);
+ MEM_ATTRS (new_rtx) = get_mem_attrs (0, expr, memoffset, GEN_INT (size),
+ MEM_ALIGN (new_rtx),
+ MEM_ADDR_SPACE (new_rtx), mode);
+
+ return new_rtx;
+}
+\f
+/* A fake decl that is used as the MEM_EXPR of spill slots. */
+static GTY(()) tree spill_slot_decl;
+
+tree
+get_spill_slot_decl (bool force_build_p)
+{
+ tree d = spill_slot_decl;
+ rtx rd;
+
+ if (d || !force_build_p)
+ return d;
+
+ d = build_decl (DECL_SOURCE_LOCATION (current_function_decl),
+ VAR_DECL, get_identifier ("%sfp"), void_type_node);
+ DECL_ARTIFICIAL (d) = 1;
+ DECL_IGNORED_P (d) = 1;
+ TREE_USED (d) = 1;
+ TREE_THIS_NOTRAP (d) = 1;
+ spill_slot_decl = d;
+
+ rd = gen_rtx_MEM (BLKmode, frame_pointer_rtx);
+ MEM_NOTRAP_P (rd) = 1;
+ MEM_ATTRS (rd) = get_mem_attrs (new_alias_set (), d, const0_rtx,
+ NULL_RTX, 0, ADDR_SPACE_GENERIC, BLKmode);
+ SET_DECL_RTL (d, rd);
+
+ return d;
+}
+
+/* Given MEM, a result from assign_stack_local, fill in the memory
+ attributes as appropriate for a register allocator spill slot.
+ These slots are not aliasable by other memory. We arrange for
+ them all to use a single MEM_EXPR, so that the aliasing code can
+ work properly in the case of shared spill slots. */
+
+void
+set_mem_attrs_for_spill (rtx mem)
+{
+ alias_set_type alias;
+ rtx addr, offset;
+ tree expr;
+
+ expr = get_spill_slot_decl (true);
+ alias = MEM_ALIAS_SET (DECL_RTL (expr));
+
+ /* We expect the incoming memory to be of the form:
+ (mem:MODE (plus (reg sfp) (const_int offset)))
+ with perhaps the plus missing for offset = 0. */
+ addr = XEXP (mem, 0);
+ offset = const0_rtx;
+ if (GET_CODE (addr) == PLUS
+ && CONST_INT_P (XEXP (addr, 1)))
+ offset = XEXP (addr, 1);
- return new;
+ MEM_ATTRS (mem) = get_mem_attrs (alias, expr, offset,
+ MEM_SIZE (mem), MEM_ALIGN (mem),
+ ADDR_SPACE_GENERIC, GET_MODE (mem));
+ MEM_NOTRAP_P (mem) = 1;
}
\f
/* Return a newly created CODE_LABEL rtx with a unique label number. */
last_insn = last;
cur_insn_uid = 0;
- for (insn = first; insn; insn = NEXT_INSN (insn))
- cur_insn_uid = MAX (cur_insn_uid, INSN_UID (insn));
+ if (MIN_NONDEBUG_INSN_UID || MAY_HAVE_DEBUG_INSNS)
+ {
+ int debug_count = 0;
+
+ cur_insn_uid = MIN_NONDEBUG_INSN_UID - 1;
+ cur_debug_insn_uid = 0;
+
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ if (INSN_UID (insn) < MIN_NONDEBUG_INSN_UID)
+ cur_debug_insn_uid = MAX (cur_debug_insn_uid, INSN_UID (insn));
+ else
+ {
+ cur_insn_uid = MAX (cur_insn_uid, INSN_UID (insn));
+ if (DEBUG_INSN_P (insn))
+ debug_count++;
+ }
+
+ if (debug_count)
+ cur_debug_insn_uid = MIN_NONDEBUG_INSN_UID + debug_count;
+ else
+ cur_debug_insn_uid++;
+ }
+ else
+ for (insn = first; insn; insn = NEXT_INSN (insn))
+ cur_insn_uid = MAX (cur_insn_uid, INSN_UID (insn));
cur_insn_uid++;
}
return 0;
}
-struct tree_opt_pass pass_unshare_all_rtl =
+struct rtl_opt_pass pass_unshare_all_rtl =
{
+ {
+ RTL_PASS,
"unshare", /* name */
NULL, /* gate */
unshare_all_rtl, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
- 0, /* tv_id */
+ TV_NONE, /* tv_id */
0, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
+ TODO_dump_func | TODO_verify_rtl_sharing /* todo_flags_finish */
+ }
};
switch (code)
{
case REG:
+ case DEBUG_EXPR:
+ case VALUE:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
}
#endif
gcc_assert (!RTX_FLAG (x, used));
-
+
RTX_FLAG (x, used) = 1;
/* Now scan the subexpressions recursively. */
set_used_flags (DECL_RTL (t));
/* Now process sub-blocks. */
- for (t = BLOCK_SUBBLOCKS (blk); t; t = TREE_CHAIN (t))
+ for (t = BLOCK_SUBBLOCKS (blk); t; t = BLOCK_CHAIN (t))
set_used_decls (t);
}
switch (code)
{
case REG:
+ case DEBUG_EXPR:
+ case VALUE:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
return;
break;
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
format_ptr = GET_RTX_FORMAT (code);
length = GET_RTX_LENGTH (code);
last_ptr = NULL;
-
+
for (i = 0; i < length; i++)
{
switch (*format_ptr++)
{
int j;
int len = XVECLEN (x, i);
-
+
/* Copy the vector iff I copied the rtx and the length
is nonzero. */
if (copied && len > 0)
XVEC (x, i) = gen_rtvec_v (len, XVEC (x, i)->elem);
-
+
/* Call recursively on all inside the vector. */
for (j = 0; j < len; j++)
{
switch (code)
{
case REG:
+ case DEBUG_EXPR:
+ case VALUE:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case CODE_LABEL:
case CC0:
return;
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
format_ptr = GET_RTX_FORMAT (code);
length = GET_RTX_LENGTH (code);
-
+
for (i = 0; i < length; i++)
{
switch (*format_ptr++)
switch (code)
{
case REG:
+ case DEBUG_EXPR:
+ case VALUE:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case CODE_LABEL:
case CC0:
return;
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
{
return cur_insn_uid;
}
+
+/* Return the number of actual (non-debug) insns emitted in this
+ function. */
+
+int
+get_max_insn_count (void)
+{
+ int n = cur_insn_uid;
+
+ /* The table size must be stable across -g, to avoid codegen
+ differences due to debug insns, and not be affected by
+ -fmin-insn-uid, to avoid excessive table size and to simplify
+ debugging of -fcompare-debug failures. */
+ if (cur_debug_insn_uid > MIN_NONDEBUG_INSN_UID)
+ n -= cur_debug_insn_uid;
+ else
+ n -= MIN_NONDEBUG_INSN_UID;
+
+ return n;
+}
+
\f
/* Return the next insn. If it is a SEQUENCE, return the first insn
of the sequence. */
return insn;
}
-/* Return the previous insn before INSN that is not a NOTE. This routine does
- not look inside SEQUENCEs. */
+/* Return the next insn after INSN that is not a NOTE, but stop the
+ search before we enter another basic block. This routine does not
+ look inside SEQUENCEs. */
rtx
-prev_nonnote_insn (rtx insn)
+next_nonnote_insn_bb (rtx insn)
{
while (insn)
{
- insn = PREV_INSN (insn);
+ insn = NEXT_INSN (insn);
if (insn == 0 || !NOTE_P (insn))
break;
+ if (NOTE_INSN_BASIC_BLOCK_P (insn))
+ return NULL_RTX;
}
return insn;
}
-/* Return the next INSN, CALL_INSN or JUMP_INSN after INSN;
- or 0, if there is none. This routine does not look inside
- SEQUENCEs. */
+/* Return the previous insn before INSN that is not a NOTE. This routine does
+ not look inside SEQUENCEs. */
rtx
-next_real_insn (rtx insn)
+prev_nonnote_insn (rtx insn)
{
while (insn)
{
- insn = NEXT_INSN (insn);
- if (insn == 0 || INSN_P (insn))
+ insn = PREV_INSN (insn);
+ if (insn == 0 || !NOTE_P (insn))
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the previous insn before INSN that is not a NOTE, but stop
+ the search before we enter another basic block. This routine does
+ not look inside SEQUENCEs. */
+
+rtx
+prev_nonnote_insn_bb (rtx insn)
+{
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || !NOTE_P (insn))
+ break;
+ if (NOTE_INSN_BASIC_BLOCK_P (insn))
+ return NULL_RTX;
+ }
+
+ return insn;
+}
+
+/* Return the next insn after INSN that is not a DEBUG_INSN. This
+ routine does not look inside SEQUENCEs. */
+
+rtx
+next_nondebug_insn (rtx insn)
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || !DEBUG_INSN_P (insn))
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the previous insn before INSN that is not a DEBUG_INSN.
+ This routine does not look inside SEQUENCEs. */
+
+rtx
+prev_nondebug_insn (rtx insn)
+{
+ while (insn)
+ {
+ insn = PREV_INSN (insn);
+ if (insn == 0 || !DEBUG_INSN_P (insn))
+ break;
+ }
+
+ return insn;
+}
+
+/* Return the next INSN, CALL_INSN or JUMP_INSN after INSN;
+ or 0, if there is none. This routine does not look inside
+ SEQUENCEs. */
+
+rtx
+next_real_insn (rtx insn)
+{
+ while (insn)
+ {
+ insn = NEXT_INSN (insn);
+ if (insn == 0 || INSN_P (insn))
break;
}
}
/* Find the next insn after INSN that really does something. This routine
- does not look inside SEQUENCEs. Until reload has completed, this is the
- same as next_real_insn. */
+ does not look inside SEQUENCEs. After reload this also skips over
+ standalone USE and CLOBBER insn. */
int
active_insn_p (const_rtx insn)
}
/* Find the last insn before INSN that really does something. This routine
- does not look inside SEQUENCEs. Until reload has completed, this is the
- same as prev_real_insn. */
+ does not look inside SEQUENCEs. After reload this also skips over
+ standalone USE and CLOBBER insn. */
rtx
prev_active_insn (rtx insn)
if (NONJUMP_INSN_P (user) && GET_CODE (PATTERN (user)) == SEQUENCE)
user = XVECEXP (PATTERN (user), 0, 0);
- REG_NOTES (user) = gen_rtx_INSN_LIST (REG_CC_SETTER, insn,
- REG_NOTES (user));
- REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_CC_USER, user, REG_NOTES (insn));
+ add_reg_note (user, REG_CC_SETTER, insn);
+ add_reg_note (insn, REG_CC_USER, user);
}
/* Return the next insn that uses CC0 after INSN, which is assumed to
find_auto_inc (rtx *xp, void *data)
{
rtx x = *xp;
- rtx reg = data;
+ rtx reg = (rtx) data;
if (GET_RTX_CLASS (GET_CODE (x)) != RTX_AUTOINC)
return 0;
rtx before = PREV_INSN (trial);
rtx after = NEXT_INSN (trial);
int has_barrier = 0;
- rtx tem;
- rtx note, seq;
+ rtx note, seq, tem;
int probability;
rtx insn_last, insn;
int njumps = 0;
+ /* We're not good at redistributing frame information. */
+ if (RTX_FRAME_RELATED_P (trial))
+ return trial;
+
if (any_condjump_p (trial)
&& (note = find_reg_note (trial, REG_BR_PROB, 0)))
split_branch_probability = INTVAL (XEXP (note, 0));
is responsible for this step using
split_branch_probability variable. */
gcc_assert (njumps == 1);
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_BR_PROB,
- GEN_INT (probability),
- REG_NOTES (insn));
+ add_reg_note (insn, REG_BR_PROB, GEN_INT (probability));
}
}
}
p = &XEXP (*p, 1);
*p = CALL_INSN_FUNCTION_USAGE (trial);
SIBLING_CALL_P (insn) = SIBLING_CALL_P (trial);
+
+ /* Update the debug information for the CALL_INSN. */
+ if (flag_enable_icf_debug)
+ (*debug_hooks->copy_call_info) (trial, insn);
}
}
switch (REG_NOTE_KIND (note))
{
case REG_EH_REGION:
- for (insn = insn_last; insn != NULL_RTX; insn = PREV_INSN (insn))
- {
- if (CALL_P (insn)
- || (flag_non_call_exceptions && INSN_P (insn)
- && may_trap_p (PATTERN (insn))))
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_EH_REGION,
- XEXP (note, 0),
- REG_NOTES (insn));
- }
+ copy_reg_eh_region_note_backward (note, insn_last, NULL);
break;
case REG_NORETURN:
for (insn = insn_last; insn != NULL_RTX; insn = PREV_INSN (insn))
{
if (CALL_P (insn))
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
- XEXP (note, 0),
- REG_NOTES (insn));
+ add_reg_note (insn, REG_NOTE_KIND (note), XEXP (note, 0));
}
break;
for (insn = insn_last; insn != NULL_RTX; insn = PREV_INSN (insn))
{
if (JUMP_P (insn))
- REG_NOTES (insn)
- = gen_rtx_EXPR_LIST (REG_NOTE_KIND (note),
- XEXP (note, 0),
- REG_NOTES (insn));
+ add_reg_note (insn, REG_NOTE_KIND (note), XEXP (note, 0));
}
break;
rtx reg = XEXP (note, 0);
if (!FIND_REG_INC_NOTE (insn, reg)
&& for_each_rtx (&PATTERN (insn), find_auto_inc, reg) > 0)
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_INC, reg,
- REG_NOTES (insn));
+ add_reg_note (insn, REG_INC, reg);
}
break;
#endif
/* If there are LABELS inside the split insns increment the
usage count so we don't delete the label. */
- if (NONJUMP_INSN_P (trial))
+ if (INSN_P (trial))
{
insn = insn_last;
while (insn != NULL_RTX)
{
+ /* JUMP_P insns have already been "marked" above. */
if (NONJUMP_INSN_P (insn))
mark_label_nuses (PATTERN (insn));
return insn;
}
+/* Like `make_insn_raw' but make a DEBUG_INSN instead of an insn. */
+
+rtx
+make_debug_insn_raw (rtx pattern)
+{
+ rtx insn;
+
+ insn = rtx_alloc (DEBUG_INSN);
+ INSN_UID (insn) = cur_debug_insn_uid++;
+ if (cur_debug_insn_uid > MIN_NONDEBUG_INSN_UID)
+ INSN_UID (insn) = cur_insn_uid++;
+
+ PATTERN (insn) = pattern;
+ INSN_CODE (insn) = -1;
+ REG_NOTES (insn) = NULL;
+ INSN_LOCATOR (insn) = curr_insn_locator ();
+ BLOCK_FOR_INSN (insn) = NULL;
+
+ return insn;
+}
+
/* Like `make_insn_raw' but make a JUMP_INSN instead of an insn. */
rtx
gcc_assert (stack);
}
- if (!bb
+ if (!bb
&& !BARRIER_P (before)
&& !BARRIER_P (insn))
bb = BLOCK_FOR_INSN (before);
/* Replace insn with an deleted instruction note. */
-void set_insn_deleted (rtx insn)
+void
+set_insn_deleted (rtx insn)
{
df_insn_delete (BLOCK_FOR_INSN (insn), INSN_UID (insn));
PUT_CODE (insn, NOTE);
for (x = from; x != NEXT_INSN (to); x = NEXT_INSN (x))
if (!BARRIER_P (x))
- {
- set_block_for_insn (x, bb);
- df_insn_change_bb (x);
- }
+ df_insn_change_bb (x, bb);
}
}
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
return last;
}
+/* Make an instruction with body X and code DEBUG_INSN
+ and output it before the instruction BEFORE. */
+
+rtx
+emit_debug_insn_before_noloc (rtx x, rtx before)
+{
+ rtx last = NULL_RTX, insn;
+
+ gcc_assert (before);
+
+ switch (GET_CODE (x))
+ {
+ case DEBUG_INSN:
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case CODE_LABEL:
+ case BARRIER:
+ case NOTE:
+ insn = x;
+ while (insn)
+ {
+ rtx next = NEXT_INSN (insn);
+ add_insn_before (insn, before, NULL);
+ last = insn;
+ insn = next;
+ }
+ break;
+
+#ifdef ENABLE_RTL_CHECKING
+ case SEQUENCE:
+ gcc_unreachable ();
+ break;
+#endif
+
+ default:
+ last = make_debug_insn_raw (x);
+ add_insn_before (last, before, NULL);
+ break;
+ }
+
+ return last;
+}
+
/* Make an insn of code BARRIER
and output it before the insn BEFORE. */
if (after == last_insn)
last_insn = last;
+
return last;
}
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
return last;
}
+/* Make an instruction with body X and code CALL_INSN
+ and output it after the instruction AFTER. */
+
+rtx
+emit_debug_insn_after_noloc (rtx x, rtx after)
+{
+ rtx last;
+
+ gcc_assert (after);
+
+ switch (GET_CODE (x))
+ {
+ case DEBUG_INSN:
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case CODE_LABEL:
+ case BARRIER:
+ case NOTE:
+ last = emit_insn_after_1 (x, after, NULL);
+ break;
+
+#ifdef ENABLE_RTL_CHECKING
+ case SEQUENCE:
+ gcc_unreachable ();
+ break;
+#endif
+
+ default:
+ last = make_debug_insn_raw (x);
+ add_insn_after (last, after, NULL);
+ break;
+ }
+
+ return last;
+}
+
/* Make an insn of code BARRIER
and output it after the insn AFTER. */
rtx
emit_insn_after (rtx pattern, rtx after)
{
- if (INSN_P (after))
- return emit_insn_after_setloc (pattern, after, INSN_LOCATOR (after));
+ rtx prev = after;
+
+ while (DEBUG_INSN_P (prev))
+ prev = PREV_INSN (prev);
+
+ if (INSN_P (prev))
+ return emit_insn_after_setloc (pattern, after, INSN_LOCATOR (prev));
else
return emit_insn_after_noloc (pattern, after, NULL);
}
rtx
emit_jump_insn_after (rtx pattern, rtx after)
{
- if (INSN_P (after))
- return emit_jump_insn_after_setloc (pattern, after, INSN_LOCATOR (after));
+ rtx prev = after;
+
+ while (DEBUG_INSN_P (prev))
+ prev = PREV_INSN (prev);
+
+ if (INSN_P (prev))
+ return emit_jump_insn_after_setloc (pattern, after, INSN_LOCATOR (prev));
else
return emit_jump_insn_after_noloc (pattern, after);
}
rtx
emit_call_insn_after (rtx pattern, rtx after)
{
- if (INSN_P (after))
- return emit_call_insn_after_setloc (pattern, after, INSN_LOCATOR (after));
+ rtx prev = after;
+
+ while (DEBUG_INSN_P (prev))
+ prev = PREV_INSN (prev);
+
+ if (INSN_P (prev))
+ return emit_call_insn_after_setloc (pattern, after, INSN_LOCATOR (prev));
else
return emit_call_insn_after_noloc (pattern, after);
}
+/* Like emit_debug_insn_after_noloc, but set INSN_LOCATOR according to SCOPE. */
+rtx
+emit_debug_insn_after_setloc (rtx pattern, rtx after, int loc)
+{
+ rtx last = emit_debug_insn_after_noloc (pattern, after);
+
+ if (pattern == NULL_RTX || !loc)
+ return last;
+
+ after = NEXT_INSN (after);
+ while (1)
+ {
+ if (active_insn_p (after) && !INSN_LOCATOR (after))
+ INSN_LOCATOR (after) = loc;
+ if (after == last)
+ break;
+ after = NEXT_INSN (after);
+ }
+ return last;
+}
+
+/* Like emit_debug_insn_after_noloc, but set INSN_LOCATOR according to AFTER. */
+rtx
+emit_debug_insn_after (rtx pattern, rtx after)
+{
+ if (INSN_P (after))
+ return emit_debug_insn_after_setloc (pattern, after, INSN_LOCATOR (after));
+ else
+ return emit_debug_insn_after_noloc (pattern, after);
+}
+
/* Like emit_insn_before_noloc, but set INSN_LOCATOR according to SCOPE. */
rtx
emit_insn_before_setloc (rtx pattern, rtx before, int loc)
rtx
emit_insn_before (rtx pattern, rtx before)
{
- if (INSN_P (before))
- return emit_insn_before_setloc (pattern, before, INSN_LOCATOR (before));
+ rtx next = before;
+
+ while (DEBUG_INSN_P (next))
+ next = PREV_INSN (next);
+
+ if (INSN_P (next))
+ return emit_insn_before_setloc (pattern, before, INSN_LOCATOR (next));
else
return emit_insn_before_noloc (pattern, before, NULL);
}
rtx
emit_jump_insn_before (rtx pattern, rtx before)
{
- if (INSN_P (before))
- return emit_jump_insn_before_setloc (pattern, before, INSN_LOCATOR (before));
+ rtx next = before;
+
+ while (DEBUG_INSN_P (next))
+ next = PREV_INSN (next);
+
+ if (INSN_P (next))
+ return emit_jump_insn_before_setloc (pattern, before, INSN_LOCATOR (next));
else
return emit_jump_insn_before_noloc (pattern, before);
}
rtx
emit_call_insn_before (rtx pattern, rtx before)
{
- if (INSN_P (before))
- return emit_call_insn_before_setloc (pattern, before, INSN_LOCATOR (before));
+ rtx next = before;
+
+ while (DEBUG_INSN_P (next))
+ next = PREV_INSN (next);
+
+ if (INSN_P (next))
+ return emit_call_insn_before_setloc (pattern, before, INSN_LOCATOR (next));
else
return emit_call_insn_before_noloc (pattern, before);
}
+
+/* like emit_insn_before_noloc, but set insn_locator according to scope. */
+rtx
+emit_debug_insn_before_setloc (rtx pattern, rtx before, int loc)
+{
+ rtx first = PREV_INSN (before);
+ rtx last = emit_debug_insn_before_noloc (pattern, before);
+
+ if (pattern == NULL_RTX)
+ return last;
+
+ first = NEXT_INSN (first);
+ while (1)
+ {
+ if (active_insn_p (first) && !INSN_LOCATOR (first))
+ INSN_LOCATOR (first) = loc;
+ if (first == last)
+ break;
+ first = NEXT_INSN (first);
+ }
+ return last;
+}
+
+/* like emit_debug_insn_before_noloc,
+ but set insn_locator according to before. */
+rtx
+emit_debug_insn_before (rtx pattern, rtx before)
+{
+ if (INSN_P (before))
+ return emit_debug_insn_before_setloc (pattern, before, INSN_LOCATOR (before));
+ else
+ return emit_debug_insn_before_noloc (pattern, before);
+}
\f
/* Take X and emit it at the end of the doubly-linked
INSN list.
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
return last;
}
+/* Make an insn of code DEBUG_INSN with pattern X
+ and add it to the end of the doubly-linked list. */
+
+rtx
+emit_debug_insn (rtx x)
+{
+ rtx last = last_insn;
+ rtx insn;
+
+ if (x == NULL_RTX)
+ return last;
+
+ switch (GET_CODE (x))
+ {
+ case DEBUG_INSN:
+ case INSN:
+ case JUMP_INSN:
+ case CALL_INSN:
+ case CODE_LABEL:
+ case BARRIER:
+ case NOTE:
+ insn = x;
+ while (insn)
+ {
+ rtx next = NEXT_INSN (insn);
+ add_insn (insn);
+ last = insn;
+ insn = next;
+ }
+ break;
+
+#ifdef ENABLE_RTL_CHECKING
+ case SEQUENCE:
+ gcc_unreachable ();
+ break;
+#endif
+
+ default:
+ last = make_debug_insn_raw (x);
+ add_insn (last);
+ break;
+ }
+
+ return last;
+}
+
/* Make an insn of code JUMP_INSN with pattern X
and add it to the end of the doubly-linked list. */
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
switch (GET_CODE (x))
{
+ case DEBUG_INSN:
case INSN:
case JUMP_INSN:
case CALL_INSN:
emit_note_copy (rtx orig)
{
rtx note;
-
+
note = rtx_alloc (NOTE);
-
+
INSN_UID (note) = cur_insn_uid++;
NOTE_DATA (note) = NOTE_DATA (orig);
NOTE_KIND (note) = NOTE_KIND (orig);
BLOCK_FOR_INSN (note) = NULL;
add_insn (note);
-
+
return note;
}
return note;
}
+/* Emit a clobber of lvalue X. */
+
+rtx
+emit_clobber (rtx x)
+{
+ /* CONCATs should not appear in the insn stream. */
+ if (GET_CODE (x) == CONCAT)
+ {
+ emit_clobber (XEXP (x, 0));
+ return emit_clobber (XEXP (x, 1));
+ }
+ return emit_insn (gen_rtx_CLOBBER (VOIDmode, x));
+}
+
+/* Return a sequence of insns to clobber lvalue X. */
+
+rtx
+gen_clobber (rtx x)
+{
+ rtx seq;
+
+ start_sequence ();
+ emit_clobber (x);
+ seq = get_insns ();
+ end_sequence ();
+ return seq;
+}
+
+/* Emit a use of rvalue X. */
+
+rtx
+emit_use (rtx x)
+{
+ /* CONCATs should not appear in the insn stream. */
+ if (GET_CODE (x) == CONCAT)
+ {
+ emit_use (XEXP (x, 0));
+ return emit_use (XEXP (x, 1));
+ }
+ return emit_insn (gen_rtx_USE (VOIDmode, x));
+}
+
+/* Return a sequence of insns to use rvalue X. */
+
+rtx
+gen_use (rtx x)
+{
+ rtx seq;
+
+ start_sequence ();
+ emit_use (x);
+ seq = get_insns ();
+ end_sequence ();
+ return seq;
+}
+
/* Cause next statement to emit a line note even if the line number
has not changed. */
void
force_next_line_note (void)
{
-#ifdef USE_MAPPED_LOCATION
last_location = -1;
-#else
- last_location.line = -1;
-#endif
}
/* Place a note of KIND on insn INSN with DATUM as the datum. If a
set_unique_reg_note (rtx insn, enum reg_note kind, rtx datum)
{
rtx note = find_reg_note (insn, kind, NULL_RTX);
- rtx new_note = NULL;
switch (kind)
{
break;
}
- new_note = gen_rtx_EXPR_LIST (kind, datum, REG_NOTES (insn));
- REG_NOTES (insn) = new_note;
+ add_reg_note (insn, kind, datum);
switch (kind)
{
}
case CALL_INSN:
return emit_call_insn (x);
+ case DEBUG_INSN:
+ return emit_debug_insn (x);
default:
gcc_unreachable ();
}
free_sequence_stack = tem->next;
}
else
- tem = ggc_alloc (sizeof (struct sequence_stack));
+ tem = GGC_NEW (struct sequence_stack);
tem->next = seq_stack;
tem->first = first_insn;
/* Put the various virtual registers into REGNO_REG_RTX. */
static void
-init_virtual_regs (struct emit_status *es)
+init_virtual_regs (void)
{
- rtx *ptr = es->x_regno_reg_rtx;
- ptr[VIRTUAL_INCOMING_ARGS_REGNUM] = virtual_incoming_args_rtx;
- ptr[VIRTUAL_STACK_VARS_REGNUM] = virtual_stack_vars_rtx;
- ptr[VIRTUAL_STACK_DYNAMIC_REGNUM] = virtual_stack_dynamic_rtx;
- ptr[VIRTUAL_OUTGOING_ARGS_REGNUM] = virtual_outgoing_args_rtx;
- ptr[VIRTUAL_CFA_REGNUM] = virtual_cfa_rtx;
+ regno_reg_rtx[VIRTUAL_INCOMING_ARGS_REGNUM] = virtual_incoming_args_rtx;
+ regno_reg_rtx[VIRTUAL_STACK_VARS_REGNUM] = virtual_stack_vars_rtx;
+ regno_reg_rtx[VIRTUAL_STACK_DYNAMIC_REGNUM] = virtual_stack_dynamic_rtx;
+ regno_reg_rtx[VIRTUAL_OUTGOING_ARGS_REGNUM] = virtual_outgoing_args_rtx;
+ regno_reg_rtx[VIRTUAL_CFA_REGNUM] = virtual_cfa_rtx;
}
\f
RTX_CODE code;
const char *format_ptr;
+ if (orig == NULL)
+ return NULL;
+
code = GET_CODE (orig);
switch (code)
case REG:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_FIXED:
case CONST_VECTOR:
case SYMBOL_REF:
case CODE_LABEL:
void
init_emit (void)
{
- struct function *f = cfun;
-
- f->emit = ggc_alloc (sizeof (struct emit_status));
first_insn = NULL;
last_insn = NULL;
- cur_insn_uid = 1;
+ if (MIN_NONDEBUG_INSN_UID)
+ cur_insn_uid = MIN_NONDEBUG_INSN_UID;
+ else
+ cur_insn_uid = 1;
+ cur_debug_insn_uid = 1;
reg_rtx_no = LAST_VIRTUAL_REGISTER + 1;
last_location = UNKNOWN_LOCATION;
first_label_num = label_num;
/* Init the tables that describe all the pseudo regs. */
- f->emit->regno_pointer_align_length = LAST_VIRTUAL_REGISTER + 101;
+ crtl->emit.regno_pointer_align_length = LAST_VIRTUAL_REGISTER + 101;
- f->emit->regno_pointer_align
- = ggc_alloc_cleared (f->emit->regno_pointer_align_length
- * sizeof (unsigned char));
+ crtl->emit.regno_pointer_align
+ = XCNEWVEC (unsigned char, crtl->emit.regno_pointer_align_length);
regno_reg_rtx
- = ggc_alloc (f->emit->regno_pointer_align_length * sizeof (rtx));
+ = GGC_NEWVEC (rtx, crtl->emit.regno_pointer_align_length);
/* Put copies of all the hard registers into regno_reg_rtx. */
memcpy (regno_reg_rtx,
FIRST_PSEUDO_REGISTER * sizeof (rtx));
/* Put copies of all the virtual register rtx into regno_reg_rtx. */
- init_virtual_regs (f->emit);
+ init_virtual_regs ();
/* Indicate that the virtual registers and stack locations are
all pointers. */
return gen_rtx_raw_CONST_VECTOR (mode, v);
}
-/* Create some permanent unique rtl objects shared between all functions.
- LINE_NUMBERS is nonzero if line numbers are to be generated. */
+/* Initialise global register information required by all functions. */
void
-init_emit_once (int line_numbers)
+init_emit_regs (void)
{
int i;
- enum machine_mode mode;
- enum machine_mode double_mode;
+
+ /* Reset register attributes */
+ htab_empty (reg_attrs_htab);
/* We need reg_raw_mode, so initialize the modes now. */
- init_reg_modes_once ();
+ init_reg_modes_target ();
+
+ /* Assign register numbers to the globally defined register rtx. */
+ pc_rtx = gen_rtx_PC (VOIDmode);
+ cc0_rtx = gen_rtx_CC0 (VOIDmode);
+ stack_pointer_rtx = gen_raw_REG (Pmode, STACK_POINTER_REGNUM);
+ frame_pointer_rtx = gen_raw_REG (Pmode, FRAME_POINTER_REGNUM);
+ hard_frame_pointer_rtx = gen_raw_REG (Pmode, HARD_FRAME_POINTER_REGNUM);
+ arg_pointer_rtx = gen_raw_REG (Pmode, ARG_POINTER_REGNUM);
+ virtual_incoming_args_rtx =
+ gen_raw_REG (Pmode, VIRTUAL_INCOMING_ARGS_REGNUM);
+ virtual_stack_vars_rtx =
+ gen_raw_REG (Pmode, VIRTUAL_STACK_VARS_REGNUM);
+ virtual_stack_dynamic_rtx =
+ gen_raw_REG (Pmode, VIRTUAL_STACK_DYNAMIC_REGNUM);
+ virtual_outgoing_args_rtx =
+ gen_raw_REG (Pmode, VIRTUAL_OUTGOING_ARGS_REGNUM);
+ virtual_cfa_rtx = gen_raw_REG (Pmode, VIRTUAL_CFA_REGNUM);
+
+ /* Initialize RTL for commonly used hard registers. These are
+ copied into regno_reg_rtx as we begin to compile each function. */
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ static_regno_reg_rtx[i] = gen_raw_REG (reg_raw_mode[i], i);
+
+#ifdef RETURN_ADDRESS_POINTER_REGNUM
+ return_address_pointer_rtx
+ = gen_raw_REG (Pmode, RETURN_ADDRESS_POINTER_REGNUM);
+#endif
+
+ if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
+ pic_offset_table_rtx = gen_raw_REG (Pmode, PIC_OFFSET_TABLE_REGNUM);
+ else
+ pic_offset_table_rtx = NULL_RTX;
+}
+
+/* Create some permanent unique rtl objects shared between all functions. */
- /* Initialize the CONST_INT, CONST_DOUBLE, and memory attribute hash
- tables. */
+void
+init_emit_once (void)
+{
+ int i;
+ enum machine_mode mode;
+ enum machine_mode double_mode;
+
+ /* Initialize the CONST_INT, CONST_DOUBLE, CONST_FIXED, and memory attribute
+ hash tables. */
const_int_htab = htab_create_ggc (37, const_int_htab_hash,
const_int_htab_eq, NULL);
const_double_htab = htab_create_ggc (37, const_double_htab_hash,
const_double_htab_eq, NULL);
+ const_fixed_htab = htab_create_ggc (37, const_fixed_htab_hash,
+ const_fixed_htab_eq, NULL);
+
mem_attrs_htab = htab_create_ggc (37, mem_attrs_htab_hash,
mem_attrs_htab_eq, NULL);
reg_attrs_htab = htab_create_ggc (37, reg_attrs_htab_hash,
reg_attrs_htab_eq, NULL);
- no_line_numbers = ! line_numbers;
-
/* Compute the word and byte modes. */
byte_mode = VOIDmode;
ptr_mode = mode_for_size (POINTER_SIZE, GET_MODE_CLASS (Pmode), 0);
- /* Assign register numbers to the globally defined register rtx.
- This must be done at runtime because the register number field
- is in a union and some compilers can't initialize unions. */
-
- pc_rtx = gen_rtx_PC (VOIDmode);
- cc0_rtx = gen_rtx_CC0 (VOIDmode);
- stack_pointer_rtx = gen_raw_REG (Pmode, STACK_POINTER_REGNUM);
- frame_pointer_rtx = gen_raw_REG (Pmode, FRAME_POINTER_REGNUM);
- if (hard_frame_pointer_rtx == 0)
- hard_frame_pointer_rtx = gen_raw_REG (Pmode,
- HARD_FRAME_POINTER_REGNUM);
- if (arg_pointer_rtx == 0)
- arg_pointer_rtx = gen_raw_REG (Pmode, ARG_POINTER_REGNUM);
- virtual_incoming_args_rtx =
- gen_raw_REG (Pmode, VIRTUAL_INCOMING_ARGS_REGNUM);
- virtual_stack_vars_rtx =
- gen_raw_REG (Pmode, VIRTUAL_STACK_VARS_REGNUM);
- virtual_stack_dynamic_rtx =
- gen_raw_REG (Pmode, VIRTUAL_STACK_DYNAMIC_REGNUM);
- virtual_outgoing_args_rtx =
- gen_raw_REG (Pmode, VIRTUAL_OUTGOING_ARGS_REGNUM);
- virtual_cfa_rtx = gen_raw_REG (Pmode, VIRTUAL_CFA_REGNUM);
-
- /* Initialize RTL for commonly used hard registers. These are
- copied into regno_reg_rtx as we begin to compile each function. */
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- static_regno_reg_rtx[i] = gen_raw_REG (reg_raw_mode[i], i);
-
#ifdef INIT_EXPANDERS
/* This is to initialize {init|mark|free}_machine_status before the first
call to push_function_context_to. This is needed by the Chill front
REAL_VALUE_FROM_INT (dconst0, 0, 0, double_mode);
REAL_VALUE_FROM_INT (dconst1, 1, 0, double_mode);
REAL_VALUE_FROM_INT (dconst2, 2, 0, double_mode);
- REAL_VALUE_FROM_INT (dconst3, 3, 0, double_mode);
- REAL_VALUE_FROM_INT (dconst10, 10, 0, double_mode);
- REAL_VALUE_FROM_INT (dconstm1, -1, -1, double_mode);
- REAL_VALUE_FROM_INT (dconstm2, -2, -1, double_mode);
+
+ dconstm1 = dconst1;
+ dconstm1.sign = 1;
dconsthalf = dconst1;
SET_REAL_EXP (&dconsthalf, REAL_EXP (&dconsthalf) - 1);
- real_arithmetic (&dconstthird, RDIV_EXPR, &dconst1, &dconst3);
-
- /* Initialize mathematical constants for constant folding builtins.
- These constants need to be given to at least 160 bits precision. */
- real_from_string (&dconstsqrt2,
- "1.4142135623730950488016887242096980785696718753769480731766797379907");
- real_from_string (&dconste,
- "2.7182818284590452353602874713526624977572470936999595749669676277241");
-
for (i = 0; i < (int) ARRAY_SIZE (const_tiny_rtx); i++)
{
- REAL_VALUE_TYPE *r =
+ const REAL_VALUE_TYPE *const r =
(i == 0 ? &dconst0 : i == 1 ? &dconst1 : &dconst2);
for (mode = GET_CLASS_NARROWEST_MODE (MODE_FLOAT);
FCONST0(mode).data.high = 0;
FCONST0(mode).data.low = 0;
FCONST0(mode).mode = mode;
+ const_tiny_rtx[0][(int) mode] = CONST_FIXED_FROM_FIXED_VALUE (
+ FCONST0 (mode), mode);
}
for (mode = GET_CLASS_NARROWEST_MODE (MODE_UFRACT);
FCONST0(mode).data.high = 0;
FCONST0(mode).data.low = 0;
FCONST0(mode).mode = mode;
+ const_tiny_rtx[0][(int) mode] = CONST_FIXED_FROM_FIXED_VALUE (
+ FCONST0 (mode), mode);
}
for (mode = GET_CLASS_NARROWEST_MODE (MODE_ACCUM);
FCONST0(mode).data.high = 0;
FCONST0(mode).data.low = 0;
FCONST0(mode).mode = mode;
+ const_tiny_rtx[0][(int) mode] = CONST_FIXED_FROM_FIXED_VALUE (
+ FCONST0 (mode), mode);
/* We store the value 1. */
FCONST1(mode).data.high = 0;
&FCONST1(mode).data.low,
&FCONST1(mode).data.high,
SIGNED_FIXED_POINT_MODE_P (mode));
+ const_tiny_rtx[1][(int) mode] = CONST_FIXED_FROM_FIXED_VALUE (
+ FCONST1 (mode), mode);
}
for (mode = GET_CLASS_NARROWEST_MODE (MODE_UACCUM);
FCONST0(mode).data.high = 0;
FCONST0(mode).data.low = 0;
FCONST0(mode).mode = mode;
+ const_tiny_rtx[0][(int) mode] = CONST_FIXED_FROM_FIXED_VALUE (
+ FCONST0 (mode), mode);
/* We store the value 1. */
FCONST1(mode).data.high = 0;
&FCONST1(mode).data.low,
&FCONST1(mode).data.high,
SIGNED_FIXED_POINT_MODE_P (mode));
+ const_tiny_rtx[1][(int) mode] = CONST_FIXED_FROM_FIXED_VALUE (
+ FCONST1 (mode), mode);
+ }
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_FRACT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ const_tiny_rtx[0][(int) mode] = gen_const_vector (mode, 0);
+ }
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_UFRACT);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ const_tiny_rtx[0][(int) mode] = gen_const_vector (mode, 0);
+ }
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_ACCUM);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ const_tiny_rtx[0][(int) mode] = gen_const_vector (mode, 0);
+ const_tiny_rtx[1][(int) mode] = gen_const_vector (mode, 1);
+ }
+
+ for (mode = GET_CLASS_NARROWEST_MODE (MODE_VECTOR_UACCUM);
+ mode != VOIDmode;
+ mode = GET_MODE_WIDER_MODE (mode))
+ {
+ const_tiny_rtx[0][(int) mode] = gen_const_vector (mode, 0);
+ const_tiny_rtx[1][(int) mode] = gen_const_vector (mode, 1);
}
for (i = (int) CCmode; i < (int) MAX_MACHINE_MODE; ++i)
const_tiny_rtx[0][(int) BImode] = const0_rtx;
if (STORE_FLAG_VALUE == 1)
const_tiny_rtx[1][(int) BImode] = const1_rtx;
-
-#ifdef RETURN_ADDRESS_POINTER_REGNUM
- return_address_pointer_rtx
- = gen_raw_REG (Pmode, RETURN_ADDRESS_POINTER_REGNUM);
-#endif
-
-#ifdef STATIC_CHAIN_REGNUM
- static_chain_rtx = gen_rtx_REG (Pmode, STATIC_CHAIN_REGNUM);
-
-#ifdef STATIC_CHAIN_INCOMING_REGNUM
- if (STATIC_CHAIN_INCOMING_REGNUM != STATIC_CHAIN_REGNUM)
- static_chain_incoming_rtx
- = gen_rtx_REG (Pmode, STATIC_CHAIN_INCOMING_REGNUM);
- else
-#endif
- static_chain_incoming_rtx = static_chain_rtx;
-#endif
-
-#ifdef STATIC_CHAIN
- static_chain_rtx = STATIC_CHAIN;
-
-#ifdef STATIC_CHAIN_INCOMING
- static_chain_incoming_rtx = STATIC_CHAIN_INCOMING;
-#else
- static_chain_incoming_rtx = static_chain_rtx;
-#endif
-#endif
-
- if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
- pic_offset_table_rtx = gen_raw_REG (Pmode, PIC_OFFSET_TABLE_REGNUM);
}
\f
/* Produce exact duplicate of insn INSN after AFTER.
rtx
emit_copy_of_insn_after (rtx insn, rtx after)
{
- rtx new;
- rtx note1, note2, link;
+ rtx new_rtx, link;
switch (GET_CODE (insn))
{
case INSN:
- new = emit_insn_after (copy_insn (PATTERN (insn)), after);
+ new_rtx = emit_insn_after (copy_insn (PATTERN (insn)), after);
break;
case JUMP_INSN:
- new = emit_jump_insn_after (copy_insn (PATTERN (insn)), after);
+ new_rtx = emit_jump_insn_after (copy_insn (PATTERN (insn)), after);
+ break;
+
+ case DEBUG_INSN:
+ new_rtx = emit_debug_insn_after (copy_insn (PATTERN (insn)), after);
break;
case CALL_INSN:
- new = emit_call_insn_after (copy_insn (PATTERN (insn)), after);
+ new_rtx = emit_call_insn_after (copy_insn (PATTERN (insn)), after);
if (CALL_INSN_FUNCTION_USAGE (insn))
- CALL_INSN_FUNCTION_USAGE (new)
+ CALL_INSN_FUNCTION_USAGE (new_rtx)
= copy_insn (CALL_INSN_FUNCTION_USAGE (insn));
- SIBLING_CALL_P (new) = SIBLING_CALL_P (insn);
- CONST_OR_PURE_CALL_P (new) = CONST_OR_PURE_CALL_P (insn);
+ SIBLING_CALL_P (new_rtx) = SIBLING_CALL_P (insn);
+ RTL_CONST_CALL_P (new_rtx) = RTL_CONST_CALL_P (insn);
+ RTL_PURE_CALL_P (new_rtx) = RTL_PURE_CALL_P (insn);
+ RTL_LOOPING_CONST_OR_PURE_CALL_P (new_rtx)
+ = RTL_LOOPING_CONST_OR_PURE_CALL_P (insn);
break;
default:
}
/* Update LABEL_NUSES. */
- mark_jump_label (PATTERN (new), new, 0);
+ mark_jump_label (PATTERN (new_rtx), new_rtx, 0);
- INSN_LOCATOR (new) = INSN_LOCATOR (insn);
+ INSN_LOCATOR (new_rtx) = INSN_LOCATOR (insn);
/* If the old insn is frame related, then so is the new one. This is
primarily needed for IA-64 unwind info which marks epilogue insns,
which may be duplicated by the basic block reordering code. */
- RTX_FRAME_RELATED_P (new) = RTX_FRAME_RELATED_P (insn);
+ RTX_FRAME_RELATED_P (new_rtx) = RTX_FRAME_RELATED_P (insn);
- /* Copy all REG_NOTES except REG_LABEL since mark_jump_label will
- make them. */
+ /* Copy all REG_NOTES except REG_LABEL_OPERAND since mark_jump_label
+ will make them. REG_LABEL_TARGETs are created there too, but are
+ supposed to be sticky, so we copy them. */
for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
- if (REG_NOTE_KIND (link) != REG_LABEL)
+ if (REG_NOTE_KIND (link) != REG_LABEL_OPERAND)
{
if (GET_CODE (link) == EXPR_LIST)
- REG_NOTES (new)
- = gen_rtx_EXPR_LIST (REG_NOTE_KIND (link),
- copy_insn_1 (XEXP (link, 0)), REG_NOTES (new));
+ add_reg_note (new_rtx, REG_NOTE_KIND (link),
+ copy_insn_1 (XEXP (link, 0)));
else
- REG_NOTES (new)
- = gen_rtx_INSN_LIST (REG_NOTE_KIND (link),
- XEXP (link, 0), REG_NOTES (new));
+ add_reg_note (new_rtx, REG_NOTE_KIND (link), XEXP (link, 0));
}
- /* Fix the libcall sequences. */
- if ((note1 = find_reg_note (new, REG_RETVAL, NULL_RTX)) != NULL)
- {
- rtx p = new;
- while ((note2 = find_reg_note (p, REG_LIBCALL, NULL_RTX)) == NULL)
- p = PREV_INSN (p);
- XEXP (note1, 0) = p;
- XEXP (note2, 0) = new;
- }
- INSN_CODE (new) = INSN_CODE (insn);
- return new;
+ INSN_CODE (new_rtx) = INSN_CODE (insn);
+ return new_rtx;
}
static GTY((deletable)) rtx hard_reg_clobbers [NUM_MACHINE_MODES][FIRST_PSEUDO_REGISTER];