/* Reload pseudo regs into hard regs for insns that require hard regs.
Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000 Free Software Foundation, Inc.
+ 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "tm_p.h"
#include "obstack.h"
#include "insn-config.h"
-#include "insn-flags.h"
-#include "insn-codes.h"
#include "flags.h"
#include "function.h"
#include "expr.h"
into the reload registers. */
#ifndef REGISTER_MOVE_COST
-#define REGISTER_MOVE_COST(x, y) 2
+#define REGISTER_MOVE_COST(m, x, y) 2
#endif
#ifndef LOCAL_REGNO
static int num_labels;
\f
+static void replace_pseudos_in_call_usage PARAMS((rtx *,
+ enum machine_mode,
+ rtx));
static void maybe_fix_stack_asms PARAMS ((void));
static void copy_reloads PARAMS ((struct insn_chain *));
static void calculate_needs_all_insns PARAMS ((int));
static void spill_failure PARAMS ((rtx, enum reg_class));
static void count_spilled_pseudo PARAMS ((int, int, int));
static void delete_dead_insn PARAMS ((rtx));
-static void alter_reg PARAMS ((int, int));
+static void alter_reg PARAMS ((int, int));
static void set_label_offsets PARAMS ((rtx, rtx, int));
static void check_eliminable_occurrences PARAMS ((rtx));
static void elimination_effects PARAMS ((rtx, enum machine_mode));
enum machine_mode));
static int reload_reg_free_p PARAMS ((unsigned int, int,
enum reload_type));
-static int reload_reg_free_for_value_p PARAMS ((int, int, enum reload_type,
+static int reload_reg_free_for_value_p PARAMS ((int, int, int,
+ enum reload_type,
rtx, rtx, int, int));
+static int free_for_value_p PARAMS ((int, enum machine_mode, int,
+ enum reload_type, rtx, rtx,
+ int, int));
static int reload_reg_reaches_end_p PARAMS ((unsigned int, int,
enum reload_type));
static int allocate_reload_reg PARAMS ((struct insn_chain *, int,
int));
+static int conflicts_with_override PARAMS ((rtx));
static void failed_reload PARAMS ((rtx, int));
static int set_reload_reg PARAMS ((int, int));
static void choose_reload_regs_init PARAMS ((struct insn_chain *, rtx *));
#ifdef AUTO_INC_DEC
static void add_auto_inc_notes PARAMS ((rtx, rtx));
#endif
-static rtx gen_mode_int PARAMS ((enum machine_mode,
+static void copy_eh_notes PARAMS ((rtx, rtx));
+static HOST_WIDE_INT sext_for_mode PARAMS ((enum machine_mode,
HOST_WIDE_INT));
static void failed_reload PARAMS ((rtx, int));
static int set_reload_reg PARAMS ((int, int));
}
});
}
+
+/* Replace all pseudos found in LOC with their corresponding
+ equivalences. */
+
+static void
+replace_pseudos_in_call_usage (loc, mem_mode, usage)
+ rtx *loc;
+ enum machine_mode mem_mode;
+ rtx usage;
+{
+ rtx x = *loc;
+ enum rtx_code code;
+ const char *fmt;
+ int i, j;
+
+ if (! x)
+ return;
+
+ code = GET_CODE (x);
+ if (code == REG)
+ {
+ int regno = REGNO (x);
+
+ if (regno < FIRST_PSEUDO_REGISTER)
+ return;
+
+ x = eliminate_regs (x, mem_mode, usage);
+ if (x != *loc)
+ {
+ *loc = x;
+ replace_pseudos_in_call_usage (loc, mem_mode, usage);
+ return;
+ }
+
+ if (reg_equiv_constant[regno])
+ *loc = reg_equiv_constant[regno];
+ else if (reg_equiv_mem[regno])
+ *loc = reg_equiv_mem[regno];
+ else if (reg_equiv_address[regno])
+ *loc = gen_rtx_MEM (GET_MODE (x), reg_equiv_address[regno]);
+ else if (GET_CODE (regno_reg_rtx[regno]) != REG
+ || REGNO (regno_reg_rtx[regno]) != regno)
+ *loc = regno_reg_rtx[regno];
+ else
+ abort ();
+
+ return;
+ }
+ else if (code == MEM)
+ {
+ replace_pseudos_in_call_usage (& XEXP (x, 0), GET_MODE (x), usage);
+ return;
+ }
+
+ /* Process each of our operands recursively. */
+ fmt = GET_RTX_FORMAT (code);
+ for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+ if (*fmt == 'e')
+ replace_pseudos_in_call_usage (&XEXP (x, i), mem_mode, usage);
+ else if (*fmt == 'E')
+ for (j = 0; j < XVECLEN (x, i); j++)
+ replace_pseudos_in_call_usage (& XVECEXP (x, i, j), mem_mode, usage);
+}
+
\f
/* Global variables used by reload and its subroutines. */
/* The two pointers used to track the true location of the memory used
for label offsets. */
- char *real_known_ptr = NULL_PTR;
+ char *real_known_ptr = NULL;
int (*real_at_ptr)[NUM_ELIMINABLE_REGS];
/* Make sure even insns with volatile mem refs are recognizable. */
/* Make sure that the last insn in the chain
is not something that needs reloading. */
- emit_note (NULL_PTR, NOTE_INSN_DELETED);
+ emit_note (NULL, NOTE_INSN_DELETED);
/* Enable find_equiv_reg to distinguish insns made by reload. */
reload_first_uid = get_max_uid ();
#endif
/* We don't have a stack slot for any spill reg yet. */
- bzero ((char *) spill_stack_slot, sizeof spill_stack_slot);
- bzero ((char *) spill_stack_slot_width, sizeof spill_stack_slot_width);
+ memset ((char *) spill_stack_slot, 0, sizeof spill_stack_slot);
+ memset ((char *) spill_stack_slot_width, 0, sizeof spill_stack_slot_width);
/* Initialize the save area information for caller-save, in case some
are needed. */
reg_equiv_address = (rtx *) xcalloc (max_regno, sizeof (rtx));
reg_max_ref_width = (unsigned int *) xcalloc (max_regno, sizeof (int));
reg_old_renumber = (short *) xcalloc (max_regno, sizeof (short));
- bcopy ((PTR) reg_renumber, (PTR) reg_old_renumber, max_regno * sizeof (short));
+ memcpy (reg_old_renumber, reg_renumber, max_regno * sizeof (short));
pseudo_forbidden_regs
= (HARD_REG_SET *) xmalloc (max_regno * sizeof (HARD_REG_SET));
pseudo_previous_regs
if (insns_need_reload != 0 || something_needs_elimination
|| something_needs_operands_changed)
{
- int old_frame_size = get_frame_size ();
+ HOST_WIDE_INT old_frame_size = get_frame_size ();
reload_as_needed (global);
{
rtx *pnote;
+ if (GET_CODE (insn) == CALL_INSN)
+ replace_pseudos_in_call_usage (& CALL_INSN_FUNCTION_USAGE (insn),
+ VOIDmode,
+ CALL_INSN_FUNCTION_USAGE (insn));
+
if ((GET_CODE (PATTERN (insn)) == USE
&& find_reg_note (insn, REG_EQUAL, NULL_RTX))
|| (GET_CODE (PATTERN (insn)) == CLOBBER
set_label_offsets (XEXP (tem, 0), insn, 1);
return;
+ case PARALLEL:
case ADDR_VEC:
case ADDR_DIFF_VEC:
- /* Each of the labels in the address vector must be at their initial
- offsets. We want the first field for ADDR_VEC and the second
- field for ADDR_DIFF_VEC. */
+ /* Each of the labels in the parallel or address vector must be
+ at their initial offsets. We want the first field for PARALLEL
+ and ADDR_VEC and the second field for ADDR_DIFF_VEC. */
for (i = 0; i < (unsigned) XVECLEN (x, code == ADDR_DIFF_VEC); i++)
set_label_offsets (XVECEXP (x, code == ADDR_DIFF_VEC, i),
return x;
case SUBREG:
- /* Similar to above processing, but preserve SUBREG_WORD.
+ /* Similar to above processing, but preserve SUBREG_BYTE.
Convert (subreg (mem)) to (mem) if not paradoxical.
Also, if we have a non-paradoxical (subreg (pseudo)) and the
pseudo didn't get a hard reg, we must replace this with the
else
new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
- if (new != XEXP (x, 0))
+ if (new != SUBREG_REG (x))
{
int x_size = GET_MODE_SIZE (GET_MODE (x));
int new_size = GET_MODE_SIZE (GET_MODE (new));
|| (x_size == new_size))
)
{
- int offset = SUBREG_WORD (x) * UNITS_PER_WORD;
+ int offset = SUBREG_BYTE (x);
enum machine_mode mode = GET_MODE (x);
- if (BYTES_BIG_ENDIAN)
- offset += (MIN (UNITS_PER_WORD,
- GET_MODE_SIZE (GET_MODE (new)))
- - MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode)));
-
PUT_MODE (new, mode);
XEXP (new, 0) = plus_constant (XEXP (new, 0), offset);
return new;
}
else
- return gen_rtx_SUBREG (GET_MODE (x), new, SUBREG_WORD (x));
+ return gen_rtx_SUBREG (GET_MODE (x), new, SUBREG_BYTE (x));
}
return x;
return x;
case USE:
+ /* Handle insn_list USE that a call to a pure function may generate. */
+ new = eliminate_regs (XEXP (x, 0), 0, insn);
+ if (new != XEXP (x, 0))
+ return gen_rtx_USE (GET_MODE (x), new);
+ return x;
+
case CLOBBER:
case ASM_OPERANDS:
case SET:
if (new != XEXP (x, i) && ! copied)
{
rtx new_x = rtx_alloc (code);
- bcopy ((char *) x, (char *) new_x,
- (sizeof (*new_x) - sizeof (new_x->fld)
- + sizeof (new_x->fld[0]) * GET_RTX_LENGTH (code)));
+ memcpy (new_x, x,
+ (sizeof (*new_x) - sizeof (new_x->fld)
+ + sizeof (new_x->fld[0]) * GET_RTX_LENGTH (code)));
x = new_x;
copied = 1;
}
if (! copied)
{
rtx new_x = rtx_alloc (code);
- bcopy ((char *) x, (char *) new_x,
- (sizeof (*new_x) - sizeof (new_x->fld)
- + (sizeof (new_x->fld[0])
- * GET_RTX_LENGTH (code))));
+ memcpy (new_x, x,
+ (sizeof (*new_x) - sizeof (new_x->fld)
+ + (sizeof (new_x->fld[0])
+ * GET_RTX_LENGTH (code))));
x = new_x;
copied = 1;
}
currently support: a single set with the source being a PLUS of an
eliminable register and a constant. */
if (old_set
+ && GET_CODE (SET_DEST (old_set)) == REG
&& GET_CODE (SET_SRC (old_set)) == PLUS
&& GET_CODE (XEXP (SET_SRC (old_set), 0)) == REG
&& GET_CODE (XEXP (SET_SRC (old_set), 1)) == CONST_INT
if (offset == 0)
{
- /* We assume here that we don't need a PARALLEL of
- any CLOBBERs for this assignment. There's not
- much we can do if we do need it. */
+ int num_clobbers;
+ /* We assume here that if we need a PARALLEL with
+ CLOBBERs for this assignment, we can do with the
+ MATCH_SCRATCHes that add_clobbers allocates.
+ There's not much we can do if that doesn't work. */
PATTERN (insn) = gen_rtx_SET (VOIDmode,
SET_DEST (old_set),
ep->to_rtx);
- INSN_CODE (insn) = recog (PATTERN (insn), insn, 0);
+ num_clobbers = 0;
+ INSN_CODE (insn) = recog (PATTERN (insn), insn, &num_clobbers);
+ if (num_clobbers)
+ {
+ rtvec vec = rtvec_alloc (num_clobbers + 1);
+
+ vec->elem[0] = PATTERN (insn);
+ PATTERN (insn) = gen_rtx_PARALLEL (VOIDmode, vec);
+ add_clobbers (PATTERN (insn), INSN_CODE (insn));
+ }
if (INSN_CODE (insn) < 0)
abort ();
}
set_initial_label_offsets ()
{
rtx x;
- bzero ((char *) &offsets_known_at[get_first_label_num ()], num_labels);
+ memset ((char *) &offsets_known_at[get_first_label_num ()], 0, num_labels);
for (x = forced_labels; x; x = XEXP (x, 1))
if (XEXP (x, 0))
/* Retry global register allocation if possible. */
if (global)
{
- bzero ((char *) pseudo_forbidden_regs, max_regno * sizeof (HARD_REG_SET));
+ memset ((char *) pseudo_forbidden_regs, 0, max_regno * sizeof (HARD_REG_SET));
/* For every insn that needs reloads, set the registers used as spill
regs in pseudo_forbidden_regs for every pseudo live across the
insn. */
#endif
rtx x;
- bzero ((char *) spill_reg_rtx, sizeof spill_reg_rtx);
- bzero ((char *) spill_reg_store, sizeof spill_reg_store);
+ memset ((char *) spill_reg_rtx, 0, sizeof spill_reg_rtx);
+ memset ((char *) spill_reg_store, 0, sizeof spill_reg_store);
reg_last_reload_reg = (rtx *) xcalloc (max_regno, sizeof (rtx));
reg_has_output_reload = (char *) xmalloc (max_regno);
CLEAR_HARD_REG_SET (reg_reloaded_valid);
rtx's for those pseudo regs. */
else
{
- bzero (reg_has_output_reload, max_regno);
+ memset (reg_has_output_reload, 0, max_regno);
CLEAR_HARD_REG_SET (reg_is_output_reload);
find_reloads (insn, 1, spill_indirect_levels, live_known,
into the insn's body (or perhaps into the bodies of other
load and store insn that we just made for reloading
and that we moved the structure into). */
- subst_reloads ();
+ subst_reloads (insn);
/* If this was an ASM, make sure that all the reload insns
we have generated are valid. If not, give an error
unsigned int nr;
int offset = 0;
- /* note_stores does give us subregs of hard regs. */
+ /* note_stores does give us subregs of hard regs,
+ subreg_regno_offset will abort if it is not a hard reg. */
while (GET_CODE (x) == SUBREG)
{
- offset += SUBREG_WORD (x);
+ offset += subreg_regno_offset (REGNO (SUBREG_REG (x)),
+ GET_MODE (SUBREG_REG (x)),
+ SUBREG_BYTE (x),
+ GET_MODE (x));
x = SUBREG_REG (x);
}
or -1 if we did not need a register for this reload. */
int reload_spill_index[MAX_RELOADS];
-/* Return 1 if the value in reload reg REGNO, as used by a reload
- needed for the part of the insn specified by OPNUM and TYPE,
- may be used to load VALUE into it.
-
- Other read-only reloads with the same value do not conflict
- unless OUT is non-zero and these other reloads have to live while
- output reloads live.
- If OUT is CONST0_RTX, this is a special case: it means that the
- test should not be for using register REGNO as reload register, but
- for copying from register REGNO into the reload register.
-
- RELOADNUM is the number of the reload we want to load this value for;
- a reload does not conflict with itself.
-
- When IGNORE_ADDRESS_RELOADS is set, we can not have conflicts with
- reloads that load an address for the very reload we are considering.
+/* Subroutine of free_for_value_p, used to check a single register.
+ START_REGNO is the starting regno of the full reload register
+ (possibly comprising multiple hard registers) that we are considering. */
- The caller has to make sure that there is no conflict with the return
- register. */
static int
-reload_reg_free_for_value_p (regno, opnum, type, value, out, reloadnum,
- ignore_address_reloads)
- int regno;
+reload_reg_free_for_value_p (start_regno, regno, opnum, type, value, out,
+ reloadnum, ignore_address_reloads)
+ int start_regno, regno;
int opnum;
enum reload_type type;
rtx value, out;
<= HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)) - (unsigned)1)
&& i != reloadnum)
{
- if (! rld[i].in || ! rtx_equal_p (rld[i].in, value)
+ rtx other_input = rld[i].in;
+
+ /* If the other reload loads the same input value, that
+ will not cause a conflict only if it's loading it into
+ the same register. */
+ if (true_regnum (reg) != start_regno)
+ other_input = NULL_RTX;
+ if (! other_input || ! rtx_equal_p (other_input, value)
|| rld[i].out || out)
{
int time2;
case RELOAD_OTHER:
/* If there is no conflict in the input part, handle this
like an output reload. */
- if (! rld[i].in || rtx_equal_p (rld[i].in, value))
+ if (! rld[i].in || rtx_equal_p (other_input, value))
{
time2 = MAX_RECOG_OPERANDS * 4 + 4;
/* Earlyclobbered outputs must conflict with inputs. */
}
if ((time1 >= time2
&& (! rld[i].in || rld[i].out
- || ! rtx_equal_p (rld[i].in, value)))
+ || ! rtx_equal_p (other_input, value)))
|| (out && rld[reloadnum].out_reg
&& time2 >= MAX_RECOG_OPERANDS * 4 + 3))
return 0;
return 1;
}
+/* Return 1 if the value in reload reg REGNO, as used by a reload
+ needed for the part of the insn specified by OPNUM and TYPE,
+ may be used to load VALUE into it.
+
+ MODE is the mode in which the register is used, this is needed to
+ determine how many hard regs to test.
+
+ Other read-only reloads with the same value do not conflict
+ unless OUT is non-zero and these other reloads have to live while
+ output reloads live.
+ If OUT is CONST0_RTX, this is a special case: it means that the
+ test should not be for using register REGNO as reload register, but
+ for copying from register REGNO into the reload register.
+
+ RELOADNUM is the number of the reload we want to load this value for;
+ a reload does not conflict with itself.
+
+ When IGNORE_ADDRESS_RELOADS is set, we can not have conflicts with
+ reloads that load an address for the very reload we are considering.
+
+ The caller has to make sure that there is no conflict with the return
+ register. */
+
+static int
+free_for_value_p (regno, mode, opnum, type, value, out, reloadnum,
+ ignore_address_reloads)
+ int regno;
+ enum machine_mode mode;
+ int opnum;
+ enum reload_type type;
+ rtx value, out;
+ int reloadnum;
+ int ignore_address_reloads;
+{
+ int nregs = HARD_REGNO_NREGS (regno, mode);
+ while (nregs-- > 0)
+ if (! reload_reg_free_for_value_p (regno, regno + nregs, opnum, type,
+ value, out, reloadnum,
+ ignore_address_reloads))
+ return 0;
+ return 1;
+}
+
+/* Determine whether the reload reg X overlaps any rtx'es used for
+ overriding inheritance. Return nonzero if so. */
+
+static int
+conflicts_with_override (x)
+ rtx x;
+{
+ int i;
+ for (i = 0; i < n_reloads; i++)
+ if (reload_override_in[i]
+ && reg_overlap_mentioned_p (x, reload_override_in[i]))
+ return 1;
+ return 0;
+}
+\f
/* Give an error message saying we failed to find a reload for INSN,
and clear out reload R. */
static void
/* We check reload_reg_used to make sure we
don't clobber the return register. */
&& ! TEST_HARD_REG_BIT (reload_reg_used, regnum)
- && reload_reg_free_for_value_p (regnum,
- rld[r].opnum,
- rld[r].when_needed,
- rld[r].in,
- rld[r].out, r, 1)))
+ && free_for_value_p (regnum, rld[r].mode, rld[r].opnum,
+ rld[r].when_needed, rld[r].in,
+ rld[r].out, r, 1)))
&& TEST_HARD_REG_BIT (reg_class_contents[class], regnum)
&& HARD_REGNO_MODE_OK (regnum, rld[r].mode)
/* Look first for regs to share, then for unshared. But
for (i = 0; i < n_reloads; i++)
rld[i].reg_rtx = save_reload_reg_rtx[i];
- bzero (reload_inherited, MAX_RELOADS);
- bzero ((char *) reload_inheritance_insn, MAX_RELOADS * sizeof (rtx));
- bzero ((char *) reload_override_in, MAX_RELOADS * sizeof (rtx));
+ memset (reload_inherited, 0, MAX_RELOADS);
+ memset ((char *) reload_inheritance_insn, 0, MAX_RELOADS * sizeof (rtx));
+ memset ((char *) reload_override_in, 0, MAX_RELOADS * sizeof (rtx));
CLEAR_HARD_REG_SET (reload_reg_used);
CLEAR_HARD_REG_SET (reload_reg_used_at_all);
if (inheritance)
{
- int word = 0;
+ int byte = 0;
register int regno = -1;
enum machine_mode mode = VOIDmode;
else if (GET_CODE (rld[r].in_reg) == SUBREG
&& GET_CODE (SUBREG_REG (rld[r].in_reg)) == REG)
{
- word = SUBREG_WORD (rld[r].in_reg);
+ byte = SUBREG_BYTE (rld[r].in_reg);
regno = REGNO (SUBREG_REG (rld[r].in_reg));
if (regno < FIRST_PSEUDO_REGISTER)
- regno += word;
+ regno = subreg_regno (rld[r].in_reg);
mode = GET_MODE (rld[r].in_reg);
}
#ifdef AUTO_INC_DEC
that can invalidate an inherited reload of part of a pseudoreg. */
else if (GET_CODE (rld[r].in) == SUBREG
&& GET_CODE (SUBREG_REG (rld[r].in)) == REG)
- regno = REGNO (SUBREG_REG (rld[r].in)) + SUBREG_WORD (rld[r].in);
+ regno = subreg_regno (rld[r].in);
#endif
if (regno >= 0 && reg_last_reload_reg[regno] != 0)
rtx last_reg = reg_last_reload_reg[regno];
enum machine_mode need_mode;
- i = REGNO (last_reg) + word;
+ i = REGNO (last_reg);
+ i += subreg_regno_offset (i, GET_MODE (last_reg), byte, mode);
last_class = REGNO_REG_CLASS (i);
- if (word == 0)
+ if (byte == 0)
need_mode = mode;
else
need_mode
- = smallest_mode_for_size (GET_MODE_SIZE (mode)
- + word * UNITS_PER_WORD,
+ = smallest_mode_for_size (GET_MODE_SIZE (mode) + byte,
GET_MODE_CLASS (mode));
if (
register, we might use it for reload_override_in,
if copying it to the desired class is cheap
enough. */
- || ((REGISTER_MOVE_COST (last_class, class)
+ || ((REGISTER_MOVE_COST (mode, last_class, class)
< MEMORY_MOVE_COST (mode, class, 1))
#ifdef SECONDARY_INPUT_RELOAD_CLASS
&& (SECONDARY_INPUT_RELOAD_CLASS (class, mode,
&& (rld[r].nregs == max_group_size
|| ! TEST_HARD_REG_BIT (reg_class_contents[(int) group_class],
i))
- && reload_reg_free_for_value_p (i, rld[r].opnum,
- rld[r].when_needed,
- rld[r].in,
- const0_rtx, r, 1))
+ && free_for_value_p (i, rld[r].mode, rld[r].opnum,
+ rld[r].when_needed, rld[r].in,
+ const0_rtx, r, 1))
{
/* If a group is needed, verify that all the subsequent
registers still have their values intact. */
break;
if (i1 != n_earlyclobbers
- || ! (reload_reg_free_for_value_p
- (i, rld[r].opnum, rld[r].when_needed,
- rld[r].in, rld[r].out, r, 1))
+ || ! (free_for_value_p (i, rld[r].mode,
+ rld[r].opnum,
+ rld[r].when_needed, rld[r].in,
+ rld[r].out, r, 1))
/* Don't use it if we'd clobber a pseudo reg. */
|| (TEST_HARD_REG_BIT (reg_used_in_insn, i)
&& rld[r].out
{
register rtx equiv
= find_equiv_reg (search_equiv, insn, rld[r].class,
- -1, NULL_PTR, 0, rld[r].mode);
+ -1, NULL, 0, rld[r].mode);
int regno = 0;
if (equiv != 0)
Make a new REG since this might be used in an
address and not all machines support SUBREGs
there. */
- regno = REGNO (SUBREG_REG (equiv)) + SUBREG_WORD (equiv);
+ regno = subreg_regno (equiv);
equiv = gen_rtx_REG (rld[r].mode, regno);
}
else
and of the desired class. */
if (equiv != 0
&& ((TEST_HARD_REG_BIT (reload_reg_used_at_all, regno)
- && ! reload_reg_free_for_value_p (regno, rld[r].opnum,
- rld[r].when_needed,
- rld[r].in,
- rld[r].out, r, 1))
+ && ! free_for_value_p (regno, rld[r].mode,
+ rld[r].opnum, rld[r].when_needed,
+ rld[r].in, rld[r].out, r, 1))
|| ! TEST_HARD_REG_BIT (reg_class_contents[(int) rld[r].class],
regno)))
equiv = 0;
In particular, we then can't use EQUIV for a
RELOAD_FOR_OUTPUT_ADDRESS reload. */
- if (equiv != 0 && regno_clobbered_p (regno, insn, rld[r].mode))
+ if (equiv != 0)
{
- switch (rld[r].when_needed)
- {
- case RELOAD_FOR_OTHER_ADDRESS:
- case RELOAD_FOR_INPADDR_ADDRESS:
- case RELOAD_FOR_INPUT_ADDRESS:
- case RELOAD_FOR_OPADDR_ADDR:
- break;
- case RELOAD_OTHER:
- case RELOAD_FOR_INPUT:
- case RELOAD_FOR_OPERAND_ADDRESS:
- if (! rld[r].optional)
- reload_override_in[r] = equiv;
- /* Fall through. */
- default:
- equiv = 0;
- break;
- }
+ if (regno_clobbered_p (regno, insn, rld[r].mode, 0))
+ switch (rld[r].when_needed)
+ {
+ case RELOAD_FOR_OTHER_ADDRESS:
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ case RELOAD_FOR_INPUT_ADDRESS:
+ case RELOAD_FOR_OPADDR_ADDR:
+ break;
+ case RELOAD_OTHER:
+ case RELOAD_FOR_INPUT:
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ if (! rld[r].optional)
+ reload_override_in[r] = equiv;
+ /* Fall through. */
+ default:
+ equiv = 0;
+ break;
+ }
+ else if (regno_clobbered_p (regno, insn, rld[r].mode, 1))
+ switch (rld[r].when_needed)
+ {
+ case RELOAD_FOR_OTHER_ADDRESS:
+ case RELOAD_FOR_INPADDR_ADDRESS:
+ case RELOAD_FOR_INPUT_ADDRESS:
+ case RELOAD_FOR_OPADDR_ADDR:
+ case RELOAD_FOR_OPERAND_ADDRESS:
+ case RELOAD_FOR_INPUT:
+ break;
+ case RELOAD_OTHER:
+ if (! rld[r].optional)
+ reload_override_in[r] = equiv;
+ /* Fall through. */
+ default:
+ equiv = 0;
+ break;
+ }
}
/* If we found an equivalent reg, say no code need be generated
check_reg = reload_override_in[r];
else
continue;
- if (! reload_reg_free_for_value_p (true_regnum (check_reg),
- rld[r].opnum,
- rld[r].when_needed,
- rld[r].in,
- (reload_inherited[r]
- ? rld[r].out : const0_rtx),
- r, 1))
+ if (! free_for_value_p (true_regnum (check_reg), rld[r].mode,
+ rld[r].opnum, rld[r].when_needed, rld[r].in,
+ (reload_inherited[r]
+ ? rld[r].out : const0_rtx),
+ r, 1))
{
if (pass)
continue;
oldequiv
= find_equiv_reg (old, insn,
rld[rl->secondary_in_reload].class,
- -1, NULL_PTR, 0, mode);
+ -1, NULL, 0, mode);
#endif
/* If reloading from memory, see if there is a register
|| (GET_CODE (old) == REG
&& REGNO (old) >= FIRST_PSEUDO_REGISTER
&& reg_renumber[REGNO (old)] < 0)))
- oldequiv = find_equiv_reg (old, insn, ALL_REGS,
- -1, NULL_PTR, 0, mode);
+ oldequiv = find_equiv_reg (old, insn, ALL_REGS, -1, NULL, 0, mode);
if (oldequiv)
{
/* Don't use OLDEQUIV if any other reload changes it at an
earlier stage of this insn or at this stage. */
- if (! reload_reg_free_for_value_p (regno, rl->opnum,
- rl->when_needed,
- rl->in, const0_rtx, j,
- 0))
+ if (! free_for_value_p (regno, rl->mode, rl->opnum, rl->when_needed,
+ rl->in, const0_rtx, j, 0))
oldequiv = 0;
/* If it is no cheaper to copy from OLDEQUIV into the
if (oldequiv != 0
&& ((REGNO_REG_CLASS (regno) != rl->class
- && (REGISTER_MOVE_COST (REGNO_REG_CLASS (regno),
+ && (REGISTER_MOVE_COST (mode, REGNO_REG_CLASS (regno),
rl->class)
>= MEMORY_MOVE_COST (mode, rl->class, 1)))
#ifdef SECONDARY_INPUT_RELOAD_CLASS
oldequiv = SUBREG_REG (oldequiv);
if (GET_MODE (oldequiv) != VOIDmode
&& mode != GET_MODE (oldequiv))
- oldequiv = gen_rtx_SUBREG (mode, oldequiv, 0);
+ oldequiv = gen_lowpart_SUBREG (mode, oldequiv);
/* Switch to the right place to emit the reload insns. */
switch (rl->when_needed)
&& dead_or_set_p (insn, old)
/* This is unsafe if some other reload
uses the same reg first. */
- && reload_reg_free_for_value_p (REGNO (reloadreg),
- rl->opnum,
- rl->when_needed,
- old, rl->out,
- j, 0))
+ && ! conflicts_with_override (reloadreg)
+ && free_for_value_p (REGNO (reloadreg), rl->mode, rl->opnum,
+ rl->when_needed, old, rl->out, j, 0))
{
rtx temp = PREV_INSN (insn);
while (temp && GET_CODE (temp) == NOTE)
&& ((reg_equiv_memory_loc
[REGNO (SUBREG_REG (oldequiv))] != 0)
|| (reg_equiv_constant
- [REGNO (SUBREG_REG (oldequiv))] != 0))))
+ [REGNO (SUBREG_REG (oldequiv))] != 0)))
+ || (CONSTANT_P (oldequiv)
+ && PREFERRED_RELOAD_CLASS (oldequiv,
+ REGNO_REG_CLASS (REGNO (reloadreg))) == NO_REGS))
real_oldequiv = rl->in;
gen_reload (reloadreg, real_oldequiv, rl->opnum,
rl->when_needed);
}
+ if (flag_non_call_exceptions)
+ copy_eh_notes (insn, get_insns ());
+
/* End this sequence. */
*where = get_insns ();
end_sequence ();
-
+
/* Update reload_override_in so that delete_address_reloads_1
can see the actual register usage. */
if (oldequiv_reg)
/* Copy primary reload reg to secondary reload reg.
(Note that these have been swapped above, then
- secondary reload reg to OLD using our insn. */
+ secondary reload reg to OLD using our insn.) */
/* If REAL_OLD is a paradoxical SUBREG, remove it
and try to put the opposite SUBREG on
|| !(set = single_set (insn))
|| rtx_equal_p (old, SET_DEST (set))
|| !reg_mentioned_p (old, SET_SRC (set))
- || !regno_clobbered_p (REGNO (old), insn, rl->mode))
+ || !regno_clobbered_p (REGNO (old), insn, rl->mode, 0))
gen_reload (old, reloadreg, rl->opnum,
rl->when_needed);
}
else
output_reload_insns[rl->opnum] = get_insns ();
+ if (flag_non_call_exceptions)
+ copy_eh_notes (insn, get_insns ());
+
end_sequence ();
}
if (GET_CODE (body) == SET)
{
int count = 0;
- if (reload_cse_noop_set_p (body))
+
+ /* Simplify even if we may think it is a no-op.
+ We may think a memory load of a value smaller than WORD_SIZE
+ is redundant because we haven't taken into account possible
+ implicit extension. reload_cse_simplify_set() will bring
+ this out, so it's safer to simplify before we delete. */
+ count += reload_cse_simplify_set (body, insn);
+
+ if (!count && reload_cse_noop_set_p (body))
{
rtx value = SET_DEST (body);
if (! REG_FUNCTION_VALUE_P (SET_DEST (body)))
return;
}
- /* It's not a no-op, but we can try to simplify it. */
- count += reload_cse_simplify_set (body, insn);
-
if (count > 0)
apply_change_group ();
else
int old_cost;
cselib_val *val;
struct elt_loc_list *l;
+#ifdef LOAD_EXTEND_OP
+ enum rtx_code extend_op = NIL;
+#endif
dreg = true_regnum (SET_DEST (set));
if (dreg < 0)
dclass = REGNO_REG_CLASS (dreg);
+#ifdef LOAD_EXTEND_OP
+ /* When replacing a memory with a register, we need to honor assumptions
+ that combine made wrt the contents of sign bits. We'll do this by
+ generating an extend instruction instead of a reg->reg copy. Thus
+ the destination must be a register that we can widen. */
+ if (GET_CODE (src) == MEM
+ && GET_MODE_BITSIZE (GET_MODE (src)) < BITS_PER_WORD
+ && (extend_op = LOAD_EXTEND_OP (GET_MODE (src))) != NIL
+ && GET_CODE (SET_DEST (set)) != REG)
+ return 0;
+#endif
+
/* If memory loads are cheaper than register copies, don't change them. */
if (GET_CODE (src) == MEM)
old_cost = MEMORY_MOVE_COST (GET_MODE (src), dclass, 1);
else if (CONSTANT_P (src))
old_cost = rtx_cost (src, SET);
else if (GET_CODE (src) == REG)
- old_cost = REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (src)), dclass);
+ old_cost = REGISTER_MOVE_COST (GET_MODE (src),
+ REGNO_REG_CLASS (REGNO (src)), dclass);
else
/* ??? */
old_cost = rtx_cost (src, SET);
- val = cselib_lookup (src, VOIDmode, 0);
+ val = cselib_lookup (src, GET_MODE (SET_DEST (set)), 0);
if (! val)
return 0;
for (l = val->locs; l; l = l->next)
{
+ rtx this_rtx = l->loc;
int this_cost;
- if (CONSTANT_P (l->loc) && ! references_value_p (l->loc, 0))
- this_cost = rtx_cost (l->loc, SET);
- else if (GET_CODE (l->loc) == REG)
- this_cost = REGISTER_MOVE_COST (REGNO_REG_CLASS (REGNO (l->loc)),
- dclass);
+
+ if (CONSTANT_P (this_rtx) && ! references_value_p (this_rtx, 0))
+ {
+#ifdef LOAD_EXTEND_OP
+ if (extend_op != NIL)
+ {
+ HOST_WIDE_INT this_val;
+
+ /* ??? I'm lazy and don't wish to handle CONST_DOUBLE. Other
+ constants, such as SYMBOL_REF, cannot be extended. */
+ if (GET_CODE (this_rtx) != CONST_INT)
+ continue;
+
+ this_val = INTVAL (this_rtx);
+ switch (extend_op)
+ {
+ case ZERO_EXTEND:
+ this_val &= GET_MODE_MASK (GET_MODE (src));
+ break;
+ case SIGN_EXTEND:
+ /* ??? In theory we're already extended. */
+ if (this_val == trunc_int_for_mode (this_val, GET_MODE (src)))
+ break;
+ default:
+ abort ();
+ }
+ this_rtx = GEN_INT (this_val);
+ }
+#endif
+ this_cost = rtx_cost (this_rtx, SET);
+ }
+ else if (GET_CODE (this_rtx) == REG)
+ {
+#ifdef LOAD_EXTEND_OP
+ if (extend_op != NIL)
+ {
+ this_rtx = gen_rtx_fmt_e (extend_op, word_mode, this_rtx);
+ this_cost = rtx_cost (this_rtx, SET);
+ }
+ else
+#endif
+ this_cost = REGISTER_MOVE_COST (GET_MODE (this_rtx),
+ REGNO_REG_CLASS (REGNO (this_rtx)),
+ dclass);
+ }
else
continue;
- /* If equal costs, prefer registers over anything else. That tends to
- lead to smaller instructions on some machines. */
- if ((this_cost < old_cost
- || (this_cost == old_cost
- && GET_CODE (l->loc) == REG
- && GET_CODE (SET_SRC (set)) != REG))
- && validate_change (insn, &SET_SRC (set), copy_rtx (l->loc), 1))
- old_cost = this_cost, did_change = 1;
+
+ /* If equal costs, prefer registers over anything else. That
+ tends to lead to smaller instructions on some machines. */
+ if (this_cost < old_cost
+ || (this_cost == old_cost
+ && GET_CODE (this_rtx) == REG
+ && GET_CODE (SET_SRC (set)) != REG))
+ {
+#ifdef LOAD_EXTEND_OP
+ if (GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) < BITS_PER_WORD
+ && extend_op != NIL)
+ {
+ rtx wide_dest = gen_rtx_REG (word_mode, REGNO (SET_DEST (set)));
+ ORIGINAL_REGNO (wide_dest) = ORIGINAL_REGNO (SET_DEST (set));
+ validate_change (insn, &SET_DEST (set), wide_dest, 1);
+ }
+#endif
+
+ validate_change (insn, &SET_SRC (set), copy_rtx (this_rtx), 1);
+ old_cost = this_cost, did_change = 1;
+ }
}
return did_change;
alternative_reject = (int *) alloca (recog_data.n_alternatives * sizeof (int));
alternative_nregs = (int *) alloca (recog_data.n_alternatives * sizeof (int));
alternative_order = (int *) alloca (recog_data.n_alternatives * sizeof (int));
- bzero ((char *)alternative_reject, recog_data.n_alternatives * sizeof (int));
- bzero ((char *)alternative_nregs, recog_data.n_alternatives * sizeof (int));
+ memset ((char *)alternative_reject, 0, recog_data.n_alternatives * sizeof (int));
+ memset ((char *)alternative_nregs, 0, recog_data.n_alternatives * sizeof (int));
/* For each operand, find out which regs are equivalent. */
for (i = 0; i < recog_data.n_operands; i++)
CLEAR_HARD_REG_SET (equiv_regs[i]);
/* cselib blows up on CODE_LABELs. Trying to fix that doesn't seem
- right, so avoid the problem here. */
- if (GET_CODE (recog_data.operand[i]) == CODE_LABEL)
+ right, so avoid the problem here. Likewise if we have a constant
+ and the insn pattern doesn't tell us the mode we need. */
+ if (GET_CODE (recog_data.operand[i]) == CODE_LABEL
+ || (CONSTANT_P (recog_data.operand[i])
+ && recog_data.operand_mode[i] == VOIDmode))
continue;
v = cselib_lookup (recog_data.operand[i], recog_data.operand_mode[i], 0);
reload_combine ()
{
rtx insn, set;
- int first_index_reg = 1, last_index_reg = 0;
+ int first_index_reg = -1, last_index_reg;
int i;
unsigned int r;
int last_label_ruid;
for (r = 0; r < FIRST_PSEUDO_REGISTER; r++)
if (TEST_HARD_REG_BIT (reg_class_contents[INDEX_REG_CLASS], r))
{
- if (! first_index_reg)
+ if (first_index_reg == -1)
first_index_reg = r;
last_index_reg = r;
}
/* If no index register is available, we can quit now. */
- if (first_index_reg > last_index_reg)
+ if (first_index_reg == -1)
return;
/* Set up LABEL_LIVE and EVER_LIVE_AT_START. The register lifetime
for (link = CALL_INSN_FUNCTION_USAGE (insn); link;
link = XEXP (link, 1))
- if (GET_CODE (XEXP (XEXP (link, 0), 0)) == REG)
- {
- unsigned int regno = REGNO (XEXP (XEXP (link, 0), 0));
+ {
+ rtx usage_rtx = XEXP (XEXP (link, 0), 0);
+ if (GET_CODE (usage_rtx) == REG)
+ {
+ int i;
+ unsigned int start_reg = REGNO (usage_rtx);
+ unsigned int num_regs =
+ HARD_REGNO_NREGS (start_reg, GET_MODE (usage_rtx));
+ unsigned int end_reg = start_reg + num_regs - 1;
+ for (i = start_reg; i <= end_reg; i++)
+ if (GET_CODE (XEXP (link, 0)) == CLOBBER)
+ {
+ reg_state[i].use_index = RELOAD_COMBINE_MAX_USES;
+ reg_state[i].store_ruid = reload_combine_ruid;
+ }
+ else
+ reg_state[i].use_index = -1;
+ }
+ }
- if (GET_CODE (XEXP (link, 0)) == CLOBBER)
- {
- reg_state[regno].use_index = RELOAD_COMBINE_MAX_USES;
- reg_state[regno].store_ruid = reload_combine_ruid;
- }
- else
- reg_state[regno].use_index = -1;
- }
}
-
else if (GET_CODE (insn) == JUMP_INSN
&& GET_CODE (PATTERN (insn)) != RETURN)
{
if (GET_CODE (dst) == SUBREG)
{
- regno = SUBREG_WORD (dst);
+ regno = subreg_regno_offset (REGNO (SUBREG_REG (dst)),
+ GET_MODE (SUBREG_REG (dst)),
+ SUBREG_BYTE (dst),
+ GET_MODE (dst));
dst = SUBREG_REG (dst);
}
if (GET_CODE (dst) != REG)
}
}
\f
-/* See if we can reduce the cost of a constant by replacing a move with
- an add. */
+/* See if we can reduce the cost of a constant by replacing a move
+ with an add. We track situations in which a register is set to a
+ constant or to a register plus a constant. */
/* We cannot do our optimization across labels. Invalidating all the
information about register contents we have would be costly, so we
- use last_label_luid (local variable of reload_cse_move2add) to note
- where the label is and then later disable any optimization that would
- cross it.
+ use move2add_last_label_luid to note where the label is and then
+ later disable any optimization that would cross it.
reg_offset[n] / reg_base_reg[n] / reg_mode[n] are only valid if
- reg_set_luid[n] is larger than last_label_luid[n] . */
+ reg_set_luid[n] is greater than last_label_luid[n] . */
static int reg_set_luid[FIRST_PSEUDO_REGISTER];
-/* reg_offset[n] has to be CONST_INT for it and reg_base_reg[n] /
- reg_mode[n] to be valid.
- If reg_offset[n] is a CONST_INT and reg_base_reg[n] is negative, register n
- has been set to reg_offset[n] in mode reg_mode[n] .
- If reg_offset[n] is a CONST_INT and reg_base_reg[n] is non-negative,
- register n has been set to the sum of reg_offset[n] and register
- reg_base_reg[n], calculated in mode reg_mode[n] . */
-static rtx reg_offset[FIRST_PSEUDO_REGISTER];
+/* If reg_base_reg[n] is negative, register n has been set to
+ reg_offset[n] in mode reg_mode[n] .
+ If reg_base_reg[n] is non-negative, register n has been set to the
+ sum of reg_offset[n] and the value of register reg_base_reg[n]
+ before reg_set_luid[n], calculated in mode reg_mode[n] . */
+static HOST_WIDE_INT reg_offset[FIRST_PSEUDO_REGISTER];
static int reg_base_reg[FIRST_PSEUDO_REGISTER];
static enum machine_mode reg_mode[FIRST_PSEUDO_REGISTER];
reload_cse_move2add and move2add_note_store. */
static int move2add_luid;
+/* move2add_last_label_luid is set whenever a label is found. Labels
+ invalidate all previously collected reg_offset data. */
+static int move2add_last_label_luid;
+
/* Generate a CONST_INT and force it in the range of MODE. */
-static rtx
-gen_mode_int (mode, value)
+static HOST_WIDE_INT
+sext_for_mode (mode, value)
enum machine_mode mode;
HOST_WIDE_INT value;
{
&& (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
cval |= (HOST_WIDE_INT) -1 << width;
- return GEN_INT (cval);
+ return cval;
}
+/* ??? We don't know how zero / sign extension is handled, hence we
+ can't go from a narrower to a wider mode. */
+#define MODES_OK_FOR_MOVE2ADD(OUTMODE, INMODE) \
+ (GET_MODE_SIZE (OUTMODE) == GET_MODE_SIZE (INMODE) \
+ || (GET_MODE_SIZE (OUTMODE) <= GET_MODE_SIZE (INMODE) \
+ && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (OUTMODE), \
+ GET_MODE_BITSIZE (INMODE))))
+
static void
reload_cse_move2add (first)
rtx first;
{
int i;
rtx insn;
- int last_label_luid;
for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
reg_set_luid[i] = 0;
- last_label_luid = 0;
- move2add_luid = 1;
+ move2add_last_label_luid = 0;
+ move2add_luid = 2;
for (insn = first; insn; insn = NEXT_INSN (insn), move2add_luid++)
{
rtx pat, note;
if (GET_CODE (insn) == CODE_LABEL)
- last_label_luid = move2add_luid;
+ {
+ move2add_last_label_luid = move2add_luid;
+ /* We're going to increment move2add_luid twice after a
+ label, so that we can use move2add_last_label_luid + 1 as
+ the luid for constants. */
+ move2add_luid++;
+ continue;
+ }
if (! INSN_P (insn))
continue;
pat = PATTERN (insn);
/* Check if we have valid information on the contents of this
register in the mode of REG. */
- /* ??? We don't know how zero / sign extension is handled, hence
- we can't go from a narrower to a wider mode. */
- if (reg_set_luid[regno] > last_label_luid
- && ((GET_MODE_SIZE (GET_MODE (reg))
- == GET_MODE_SIZE (reg_mode[regno]))
- || ((GET_MODE_SIZE (GET_MODE (reg))
- <= GET_MODE_SIZE (reg_mode[regno]))
- && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (reg)),
- GET_MODE_BITSIZE (reg_mode[regno]))))
- && GET_CODE (reg_offset[regno]) == CONST_INT)
+ if (reg_set_luid[regno] > move2add_last_label_luid
+ && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg), reg_mode[regno]))
{
/* Try to transform (set (REGX) (CONST_INT A))
...
if (GET_CODE (src) == CONST_INT && reg_base_reg[regno] < 0)
{
int success = 0;
- rtx new_src
- = gen_mode_int (GET_MODE (reg),
- INTVAL (src) - INTVAL (reg_offset[regno]));
+ rtx new_src = GEN_INT (sext_for_mode (GET_MODE (reg),
+ INTVAL (src)
+ - reg_offset[regno]));
/* (set (reg) (plus (reg) (const_int 0))) is not canonical;
use (set (reg) (reg)) instead.
We don't delete this insn, nor do we convert it into a
gen_add2_insn (reg, new_src), 0);
reg_set_luid[regno] = move2add_luid;
reg_mode[regno] = GET_MODE (reg);
- reg_offset[regno] = src;
+ reg_offset[regno] = INTVAL (src);
continue;
}
...
(set (REGX) (plus (REGX) (CONST_INT B-A))) */
else if (GET_CODE (src) == REG
- && reg_base_reg[regno] == (int) REGNO (src)
- && reg_set_luid[regno] > reg_set_luid[REGNO (src)])
+ && reg_set_luid[regno] == reg_set_luid[REGNO (src)]
+ && reg_base_reg[regno] == reg_base_reg[REGNO (src)]
+ && MODES_OK_FOR_MOVE2ADD (GET_MODE (reg),
+ reg_mode[REGNO (src)]))
{
rtx next = next_nonnote_insn (insn);
rtx set = NULL_RTX;
if (next)
set = single_set (next);
- if (next
- && set
+ if (set
&& SET_DEST (set) == reg
&& GET_CODE (SET_SRC (set)) == PLUS
&& XEXP (SET_SRC (set), 0) == reg
&& GET_CODE (XEXP (SET_SRC (set), 1)) == CONST_INT)
{
rtx src3 = XEXP (SET_SRC (set), 1);
- rtx new_src
- = gen_mode_int (GET_MODE (reg),
- INTVAL (src3)
- - INTVAL (reg_offset[regno]));
+ HOST_WIDE_INT added_offset = INTVAL (src3);
+ HOST_WIDE_INT base_offset = reg_offset[REGNO (src)];
+ HOST_WIDE_INT regno_offset = reg_offset[regno];
+ rtx new_src = GEN_INT (sext_for_mode (GET_MODE (reg),
+ added_offset
+ + base_offset
+ - regno_offset));
int success = 0;
if (new_src == const0_rtx)
NOTE_SOURCE_FILE (insn) = 0;
}
insn = next;
- reg_set_luid[regno] = move2add_luid;
reg_mode[regno] = GET_MODE (reg);
- reg_offset[regno] = src3;
+ reg_offset[regno] = sext_for_mode (GET_MODE (reg),
+ added_offset
+ + base_offset);
continue;
}
}
if (REG_NOTE_KIND (note) == REG_INC
&& GET_CODE (XEXP (note, 0)) == REG)
{
- /* Indicate that this register has been recently written to,
- but the exact contents are not available. */
+ /* Reset the information about this register. */
int regno = REGNO (XEXP (note, 0));
if (regno < FIRST_PSEUDO_REGISTER)
- {
- reg_set_luid[regno] = move2add_luid;
- reg_offset[regno] = note;
- }
+ reg_set_luid[regno] = 0;
}
}
note_stores (PATTERN (insn), move2add_note_store, NULL);
for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
{
if (call_used_regs[i])
- {
- reg_set_luid[i] = move2add_luid;
- reg_offset[i] = insn; /* Invalidate contents. */
- }
+ /* Reset the information about this register. */
+ reg_set_luid[i] = 0;
}
}
}
if (GET_CODE (dst) == SUBREG)
{
- regno = SUBREG_WORD (dst);
+ regno = subreg_regno_offset (REGNO (SUBREG_REG (dst)),
+ GET_MODE (SUBREG_REG (dst)),
+ SUBREG_BYTE (dst),
+ GET_MODE (dst));
dst = SUBREG_REG (dst);
}
dst = XEXP (dst, 0);
if (GET_CODE (dst) == PRE_INC || GET_CODE (dst) == POST_DEC
|| GET_CODE (dst) == PRE_DEC || GET_CODE (dst) == POST_DEC)
- {
- regno = REGNO (XEXP (dst, 0));
- reg_set_luid[regno] = move2add_luid;
- reg_offset[regno] = dst;
- }
+ reg_set_luid[REGNO (XEXP (dst, 0))] = 0;
return;
- }
+ }
if (GET_CODE (dst) != REG)
return;
&& GET_CODE (SET_DEST (set)) != STRICT_LOW_PART)
{
rtx src = SET_SRC (set);
+ rtx base_reg;
+ HOST_WIDE_INT offset;
+ int base_regno;
+ /* This may be different from mode, if SET_DEST (set) is a
+ SUBREG. */
+ enum machine_mode dst_mode = GET_MODE (dst);
- reg_mode[regno] = mode;
switch (GET_CODE (src))
{
case PLUS:
- {
- rtx src0 = XEXP (src, 0);
-
- if (GET_CODE (src0) == REG)
- {
- if (REGNO (src0) != regno
- || reg_offset[regno] != const0_rtx)
- {
- reg_base_reg[regno] = REGNO (src0);
- reg_set_luid[regno] = move2add_luid;
- }
+ if (GET_CODE (XEXP (src, 0)) == REG)
+ {
+ base_reg = XEXP (src, 0);
+
+ if (GET_CODE (XEXP (src, 1)) == CONST_INT)
+ offset = INTVAL (XEXP (src, 1));
+ else if (GET_CODE (XEXP (src, 1)) == REG
+ && (reg_set_luid[REGNO (XEXP (src, 1))]
+ > move2add_last_label_luid)
+ && (MODES_OK_FOR_MOVE2ADD
+ (dst_mode, reg_mode[REGNO (XEXP (src, 1))])))
+ {
+ if (reg_base_reg[REGNO (XEXP (src, 1))] < 0)
+ offset = reg_offset[REGNO (XEXP (src, 1))];
+ /* Maybe the first register is known to be a
+ constant. */
+ else if (reg_set_luid[REGNO (base_reg)]
+ > move2add_last_label_luid
+ && (MODES_OK_FOR_MOVE2ADD
+ (dst_mode, reg_mode[REGNO (XEXP (src, 1))]))
+ && reg_base_reg[REGNO (base_reg)] < 0)
+ {
+ offset = reg_offset[REGNO (base_reg)];
+ base_reg = XEXP (src, 1);
+ }
+ else
+ goto invalidate;
+ }
+ else
+ goto invalidate;
- reg_offset[regno] = XEXP (src, 1);
- break;
- }
+ break;
+ }
- reg_set_luid[regno] = move2add_luid;
- reg_offset[regno] = set; /* Invalidate contents. */
- break;
- }
+ goto invalidate;
case REG:
- reg_base_reg[regno] = REGNO (SET_SRC (set));
- reg_offset[regno] = const0_rtx;
- reg_set_luid[regno] = move2add_luid;
+ base_reg = src;
+ offset = 0;
break;
- default:
+ case CONST_INT:
+ /* Start tracking the register as a constant. */
reg_base_reg[regno] = -1;
- reg_offset[regno] = SET_SRC (set);
- reg_set_luid[regno] = move2add_luid;
- break;
+ reg_offset[regno] = INTVAL (SET_SRC (set));
+ /* We assign the same luid to all registers set to constants. */
+ reg_set_luid[regno] = move2add_last_label_luid + 1;
+ reg_mode[regno] = mode;
+ return;
+
+ default:
+ invalidate:
+ /* Invalidate the contents of the register. */
+ reg_set_luid[regno] = 0;
+ return;
+ }
+
+ base_regno = REGNO (base_reg);
+ /* If information about the base register is not valid, set it
+ up as a new base register, pretending its value is known
+ starting from the current insn. */
+ if (reg_set_luid[base_regno] <= move2add_last_label_luid)
+ {
+ reg_base_reg[base_regno] = base_regno;
+ reg_offset[base_regno] = 0;
+ reg_set_luid[base_regno] = move2add_luid;
+ reg_mode[base_regno] = mode;
}
+ else if (! MODES_OK_FOR_MOVE2ADD (dst_mode,
+ reg_mode[base_regno]))
+ goto invalidate;
+
+ reg_mode[regno] = mode;
+
+ /* Copy base information from our base register. */
+ reg_set_luid[regno] = reg_set_luid[base_regno];
+ reg_base_reg[regno] = reg_base_reg[base_regno];
+
+ /* Compute the sum of the offsets or constants. */
+ reg_offset[regno] = sext_for_mode (dst_mode,
+ offset
+ + reg_offset[base_regno]);
}
else
{
unsigned int endregno = regno + HARD_REGNO_NREGS (regno, mode);
for (i = regno; i < endregno; i++)
- {
- /* Indicate that this register has been recently written to,
- but the exact contents are not available. */
- reg_set_luid[i] = move2add_luid;
- reg_offset[i] = dst;
- }
+ /* Reset the information about this register. */
+ reg_set_luid[i] = 0;
}
}
}
}
#endif
+
+/* Copy EH notes from an insn to its reloads. */
+static void
+copy_eh_notes (insn, x)
+ rtx insn;
+ rtx x;
+{
+ rtx eh_note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
+ if (eh_note)
+ {
+ for (; x != 0; x = NEXT_INSN (x))
+ {
+ if (may_trap_p (PATTERN (x)))
+ REG_NOTES (x)
+ = gen_rtx_EXPR_LIST (REG_EH_REGION, XEXP (eh_note, 0),
+ REG_NOTES (x));
+ }
+ }
+}
+