/* 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, 2001, 2002 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
This file is part of GCC.
int from; /* Register number to be eliminated. */
int to; /* Register number used as replacement. */
int initial_offset; /* Initial difference between values. */
- int can_eliminate; /* Non-zero if this elimination can be done. */
+ int can_eliminate; /* Nonzero if this elimination can be done. */
int can_eliminate_previous; /* Value of CAN_ELIMINATE in previous scan over
insns made by reload. */
int offset; /* Current offset between the two regs. */
/* For each label, we record the offset of each elimination. If we reach
a label by more than one path and an offset differs, we cannot do the
- elimination. This information is indexed by the number of the label.
- The first table is an array of flags that records whether we have yet
- encountered a label and the second table is an array of arrays, one
- entry in the latter array for each elimination. */
-
+ elimination. This information is indexed by the difference of the
+ number of the label and the first label number. We can't offset the
+ pointer itself as this can cause problems on machines with segmented
+ memory. The first table is an array of flags that records whether we
+ have yet encountered a label and the second table is an array of arrays,
+ one entry in the latter array for each elimination. */
+
+static int first_label_num;
static char *offsets_known_at;
static int (*offsets_at)[NUM_ELIMINABLE_REGS];
static void add_auto_inc_notes PARAMS ((rtx, rtx));
#endif
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));
static void reload_cse_simplify PARAMS ((rtx, rtx));
struct elim_table *ep;
basic_block bb;
- /* The two pointers used to track the true location of the memory used
- for label offsets. */
- char *real_known_ptr = NULL;
- int (*real_at_ptr)[NUM_ELIMINABLE_REGS];
-
/* Make sure even insns with volatile mem refs are recognizable. */
init_recog ();
init_elim_table ();
- num_labels = max_label_num () - get_first_label_num ();
+ first_label_num = get_first_label_num ();
+ num_labels = max_label_num () - first_label_num;
/* Allocate the tables used to store offset information at labels. */
/* We used to use alloca here, but the size of what it would try to
allocate would occasionally cause it to exceed the stack limit and
cause a core dump. */
- real_known_ptr = xmalloc (num_labels);
- real_at_ptr
+ offsets_known_at = xmalloc (num_labels);
+ offsets_at
= (int (*)[NUM_ELIMINABLE_REGS])
xmalloc (num_labels * NUM_ELIMINABLE_REGS * sizeof (int));
- offsets_known_at = real_known_ptr - get_first_label_num ();
- offsets_at
- = (int (*)[NUM_ELIMINABLE_REGS]) (real_at_ptr - get_first_label_num ());
-
/* Alter each pseudo-reg rtx to contain its hard reg number.
Assign stack slots to the pseudos that lack hard regs or equivalents.
Do not touch virtual registers. */
free (reg_equiv_memory_loc);
reg_equiv_memory_loc = 0;
- if (real_known_ptr)
- free (real_known_ptr);
- if (real_at_ptr)
- free (real_at_ptr);
+ if (offsets_known_at)
+ free (offsets_known_at);
+ if (offsets_at)
+ free (offsets_at);
free (reg_equiv_mem);
free (reg_equiv_init);
for (;;)
{
- char c = *p++;
+ char c = *p;
if (c == '\0' || c == ',' || c == '#')
{
class, and reset the class. */
IOR_HARD_REG_SET (allowed, reg_class_contents[cls]);
cls = NO_REGS;
+ p++;
if (c == '#')
do {
c = *p++;
break;
default:
- if (EXTRA_ADDRESS_CONSTRAINT (c))
+ if (EXTRA_ADDRESS_CONSTRAINT (c, p))
cls = (int) reg_class_subunion[cls]
[(int) MODE_BASE_REG_CLASS (VOIDmode)];
else
cls = (int) reg_class_subunion[cls]
- [(int) REG_CLASS_FROM_LETTER (c)];
+ [(int) REG_CLASS_FROM_CONSTRAINT (c, p)];
}
+ p += CONSTRAINT_LEN (c, p);
}
}
/* Those of the registers which are clobbered, but allowed by the
/* If we have a decl for the original register, set it for the
memory. If this is a shared MEM, make a copy. */
- if (REGNO_DECL (i))
+ if (REG_EXPR (regno_reg_rtx[i])
+ && TREE_CODE_CLASS (TREE_CODE (REG_EXPR (regno_reg_rtx[i]))) == 'd')
{
- rtx decl = DECL_RTL_IF_SET (REGNO_DECL (i));
+ rtx decl = DECL_RTL_IF_SET (REG_EXPR (regno_reg_rtx[i]));
/* We can do this only for the DECLs home pseudo, not for
any copies of it, since otherwise when the stack slot
if (from_reg != -1 && spill_stack_slot[from_reg] == x)
x = copy_rtx (x);
- set_mem_expr (x, REGNO_DECL (i));
+ set_mem_attrs_from_reg (x, regno_reg_rtx[i]);
}
}
we guessed wrong, we will suppress an elimination that might have
been possible had we been able to guess correctly. */
- if (! offsets_known_at[CODE_LABEL_NUMBER (x)])
+ if (! offsets_known_at[CODE_LABEL_NUMBER (x) - first_label_num])
{
for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
- offsets_at[CODE_LABEL_NUMBER (x)][i]
+ offsets_at[CODE_LABEL_NUMBER (x) - first_label_num][i]
= (initial_p ? reg_eliminate[i].initial_offset
: reg_eliminate[i].offset);
- offsets_known_at[CODE_LABEL_NUMBER (x)] = 1;
+ offsets_known_at[CODE_LABEL_NUMBER (x) - first_label_num] = 1;
}
/* Otherwise, if this is the definition of a label and it is
where the offsets disagree. */
for (i = 0; i < NUM_ELIMINABLE_REGS; i++)
- if (offsets_at[CODE_LABEL_NUMBER (x)][i]
+ if (offsets_at[CODE_LABEL_NUMBER (x) - first_label_num][i]
!= (initial_p ? reg_eliminate[i].initial_offset
: reg_eliminate[i].offset))
reg_eliminate[i].can_eliminate = 0;
case ABS:
case SQRT:
case FFS:
+ case CLZ:
+ case CTZ:
+ case POPCOUNT:
+ case PARITY:
new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
if (new != XEXP (x, 0))
return gen_rtx_fmt_e (code, GET_MODE (x), new);
case ABS:
case SQRT:
case FFS:
+ case CLZ:
+ case CTZ:
+ case POPCOUNT:
+ case PARITY:
elimination_effects (XEXP (x, 0), mem_mode);
return;
set_initial_label_offsets ()
{
rtx x;
- memset ((char *) &offsets_known_at[get_first_label_num ()], 0, num_labels);
+ memset (offsets_known_at, 0, num_labels);
for (x = forced_labels; x; x = XEXP (x, 1))
if (XEXP (x, 0))
num_not_at_initial_offset = 0;
for (i = 0, ep = reg_eliminate; i < NUM_ELIMINABLE_REGS; ep++, i++)
{
- ep->offset = ep->previous_offset = offsets_at[label_nr][i];
+ ep->offset = ep->previous_offset
+ = offsets_at[label_nr - first_label_num][i];
if (ep->can_eliminate && ep->offset != ep->initial_offset)
num_not_at_initial_offset++;
}
for (chain = reload_insn_chain; chain; chain = chain->next)
{
- rtx prev;
+ rtx prev = 0;
rtx insn = chain->insn;
rtx old_next = NEXT_INSN (insn);
if (asm_noperands (PATTERN (insn)) >= 0)
for (p = NEXT_INSN (prev); p != next; p = NEXT_INSN (p))
if (p != insn && INSN_P (p)
+ && GET_CODE (PATTERN (p)) != USE
&& (recog_memoized (p) < 0
|| (extract_insn (p), ! constrain_operands (1))))
{
REGNO (rld[i].reg_rtx))
/* Make sure it is the inc/dec pseudo, and not
some other (e.g. output operand) pseudo. */
- && (reg_reloaded_contents[REGNO (rld[i].reg_rtx)]
+ && ((unsigned) reg_reloaded_contents[REGNO (rld[i].reg_rtx)]
== REGNO (XEXP (in_reg, 0))))
{
REGNO (rld[i].reg_rtx))
/* Make sure it is the inc/dec pseudo, and not
some other (e.g. output operand) pseudo. */
- && (reg_reloaded_contents[REGNO (rld[i].reg_rtx)]
+ && ((unsigned) reg_reloaded_contents[REGNO (rld[i].reg_rtx)]
== REGNO (XEXP (in_reg, 0))))
{
SET_HARD_REG_BIT (reg_is_output_reload,
#ifdef CANNOT_CHANGE_MODE_CLASS
(!REG_CANNOT_CHANGE_MODE_P (i, GET_MODE (last_reg),
need_mode)
- ||
+ &&
#endif
(GET_MODE_SIZE (GET_MODE (last_reg))
>= GET_MODE_SIZE (need_mode))
if (reload_override_in[j])
rld[j].in = reload_override_in[j];
- /* If this reload won't be done because it has been cancelled or is
+ /* If this reload won't be done because it has been canceled or is
optional and not inherited, clear reload_reg_rtx so other
routines (such as subst_reloads) don't get confused. */
for (j = 0; j < n_reloads; j++)
or memory. */
if (oldequiv != 0
- && ((REGNO_REG_CLASS (regno) != rl->class
+ && (((enum reg_class) REGNO_REG_CLASS (regno) != rl->class
&& (REGISTER_MOVE_COST (mode, REGNO_REG_CLASS (regno),
rl->class)
>= MEMORY_MOVE_COST (mode, rl->class, 1)))
must always be a REG here. */
if (GET_MODE (reloadreg) != mode)
- reloadreg = gen_rtx_REG (mode, REGNO (reloadreg));
+ reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
while (GET_CODE (oldequiv) == SUBREG && GET_MODE (oldequiv) != mode)
oldequiv = SUBREG_REG (oldequiv);
if (GET_MODE (oldequiv) != VOIDmode
oldequiv = old, real_oldequiv = real_old;
else
second_reload_reg
- = gen_rtx_REG (new_mode,
- REGNO (second_reload_reg));
+ = reload_adjust_reg_for_mode (second_reload_reg,
+ new_mode);
}
}
}
}
if (GET_MODE (reloadreg) != mode)
- reloadreg = gen_rtx_REG (mode, REGNO (reloadreg));
+ reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
#ifdef SECONDARY_OUTPUT_RELOAD_CLASS
= rld[secondary_reload].secondary_out_icode;
if (GET_MODE (reloadreg) != mode)
- reloadreg = gen_rtx_REG (mode, REGNO (reloadreg));
+ reloadreg = reload_adjust_reg_for_mode (reloadreg, mode);
if (tertiary_icode != CODE_FOR_nothing)
{
rtx i1;
rtx substed;
+ /* It is possible that this reload has been only used to set another reload
+ we eliminated earlier and thus deleted this instruction too. */
+ if (INSN_DELETED_P (output_reload_insn))
+ return;
+
/* Get the raw pseudo-register referred to. */
while (GET_CODE (reg) == SUBREG)
/* The caller has already checked that REG dies or is set in INSN.
It has also checked that we are optimizing, and thus some
- inaccurancies in the debugging information are acceptable.
+ inaccuracies in the debugging information are acceptable.
So we could just delete output_reload_insn. But in some cases
we can improve the debugging information without sacrificing
optimization - maybe even improving the code: See if the pseudo
return;
/* ??? We can't finish the loop here, because dst might be
allocated to a pseudo in this block if no reload in this
- block needs any of the clsses containing DST - see
+ block needs any of the classes containing DST - see
spill_hard_reg. There is no easy way to tell this, so we
have to scan till the end of the basic block. */
}
reload_cse_noop_set_p (set)
rtx set;
{
+ if (cselib_reg_set_mode (SET_DEST (set)) != GET_MODE (SET_DEST (set)))
+ return 0;
+
return rtx_equal_for_cselib_p (SET_DEST (set), SET_SRC (set));
}
{
#ifdef LOAD_EXTEND_OP
if (GET_MODE_BITSIZE (GET_MODE (SET_DEST (set))) < BITS_PER_WORD
- && extend_op != NIL)
+ && extend_op != NIL
+#ifdef CANNOT_CHANGE_MODE_CLASS
+ && !CANNOT_CHANGE_MODE_CLASS (GET_MODE (SET_DEST (set)),
+ word_mode,
+ REGNO_REG_CLASS (REGNO (SET_DEST (set))))
+#endif
+ )
{
rtx wide_dest = gen_rtx_REG (word_mode, REGNO (SET_DEST (set)));
ORIGINAL_REGNO (wide_dest) = ORIGINAL_REGNO (SET_DEST (set));
p = constraints[i];
for (;;)
{
- char c = *p++;
+ char c = *p;
switch (c)
{
default:
class
- = reg_class_subunion[(int) class][(int) REG_CLASS_FROM_LETTER ((unsigned char) c)];
+ = (reg_class_subunion
+ [(int) class]
+ [(int) REG_CLASS_FROM_CONSTRAINT ((unsigned char) c, p)]);
break;
case ',': case '\0':
j++;
break;
}
+ p += CONSTRAINT_LEN (c, p);
if (c == '\0')
break;
\f
/* If reload couldn't use reg+reg+offset addressing, try to use reg+reg
addressing now.
- This code might also be useful when reload gave up on reg+reg addresssing
+ This code might also be useful when reload gave up on reg+reg addressing
because of clashes between the return register and INDEX_REG_CLASS. */
/* The maximum number of uses of a register we can keep track of to
last, of these uses.
STORE_RUID is always meaningful if we only want to use a value in a
register in a different place: it denotes the next insn in the insn
- stream (i.e. the last ecountered) that sets or clobbers the register. */
+ stream (i.e. the last encountered) that sets or clobbers the register. */
static struct
{
struct reg_use reg_use[RELOAD_COMBINE_MAX_USES];
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 greater than last_label_luid[n] . */
+ reg_set_luid[n] is greater than move2add_last_label_luid. */
static int reg_set_luid[FIRST_PSEUDO_REGISTER];
/* If reg_base_reg[n] is negative, register n has been set to
static int reg_base_reg[FIRST_PSEUDO_REGISTER];
static enum machine_mode reg_mode[FIRST_PSEUDO_REGISTER];
-/* move2add_luid is linearily increased while scanning the instructions
+/* move2add_luid is linearly increased while scanning the instructions
from first to last. It is used to set reg_set_luid in
reload_cse_move2add and move2add_note_store. */
static int move2add_luid;
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 HOST_WIDE_INT
-sext_for_mode (mode, value)
- enum machine_mode mode;
- HOST_WIDE_INT value;
-{
- HOST_WIDE_INT cval = value & GET_MODE_MASK (mode);
- int width = GET_MODE_BITSIZE (mode);
-
- /* If MODE is narrower than HOST_WIDE_INT and CVAL is a negative number,
- sign extend it. */
- if (width > 0 && width < HOST_BITS_PER_WIDE_INT
- && (cval & ((HOST_WIDE_INT) 1 << (width - 1))) != 0)
- cval |= (HOST_WIDE_INT) -1 << width;
-
- 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) \
to
(set (REGX) (CONST_INT A))
...
- (set (REGX) (plus (REGX) (CONST_INT B-A))) */
+ (set (REGX) (plus (REGX) (CONST_INT B-A)))
+ or
+ (set (REGX) (CONST_INT A))
+ ...
+ (set (STRICT_LOW_PART (REGX)) (CONST_INT B))
+ */
if (GET_CODE (src) == CONST_INT && reg_base_reg[regno] < 0)
{
- int success = 0;
- rtx new_src = GEN_INT (sext_for_mode (GET_MODE (reg),
- INTVAL (src)
- - reg_offset[regno]));
+ rtx new_src =
+ GEN_INT (trunc_int_for_mode (INTVAL (src)
+ - reg_offset[regno],
+ GET_MODE (reg)));
/* (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
value flag. jump2 already knows how to get rid of
no-op moves. */
if (new_src == const0_rtx)
- success = validate_change (insn, &SET_SRC (pat), reg, 0);
+ validate_change (insn, &SET_SRC (pat), reg, 0);
else if (rtx_cost (new_src, PLUS) < rtx_cost (src, SET)
&& have_add2_insn (reg, new_src))
- success = validate_change (insn, &PATTERN (insn),
- gen_add2_insn (reg, new_src), 0);
+ {
+ rtx newpat = gen_add2_insn (reg, new_src);
+ if (INSN_P (newpat) && NEXT_INSN (newpat) == NULL_RTX)
+ newpat = PATTERN (newpat);
+ /* If it was the first insn of a sequence or
+ some other emitted insn, validate_change will
+ reject it. */
+ validate_change (insn, &PATTERN (insn),
+ newpat, 0);
+ }
+ else
+ {
+ enum machine_mode narrow_mode;
+ for (narrow_mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+ narrow_mode != GET_MODE (reg);
+ narrow_mode = GET_MODE_WIDER_MODE (narrow_mode))
+ {
+ if (have_insn_for (STRICT_LOW_PART, narrow_mode)
+ && ((reg_offset[regno]
+ & ~GET_MODE_MASK (narrow_mode))
+ == (INTVAL (src)
+ & ~GET_MODE_MASK (narrow_mode))))
+ {
+ rtx narrow_reg = gen_rtx_REG (narrow_mode,
+ REGNO (reg));
+ rtx narrow_src =
+ GEN_INT (trunc_int_for_mode (INTVAL (src),
+ narrow_mode));
+ rtx new_set =
+ gen_rtx_SET (VOIDmode,
+ gen_rtx_STRICT_LOW_PART (VOIDmode,
+ narrow_reg),
+ narrow_src);
+ if (validate_change (insn, &PATTERN (insn),
+ new_set, 0))
+ break;
+ }
+ }
+ }
reg_set_luid[regno] = move2add_luid;
reg_mode[regno] = GET_MODE (reg);
reg_offset[regno] = INTVAL (src);
(set (REGX) (REGY))
(set (REGX) (PLUS (REGX) (CONST_INT B)))
to
- (REGX) (REGY))
+ (set (REGX) (REGY))
(set (REGX) (PLUS (REGX) (CONST_INT A)))
...
(set (REGX) (plus (REGX) (CONST_INT B-A))) */
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));
+ rtx new_src =
+ GEN_INT (trunc_int_for_mode (added_offset
+ + base_offset
+ - regno_offset,
+ GET_MODE (reg)));
int success = 0;
if (new_src == const0_rtx)
else if ((rtx_cost (new_src, PLUS)
< COSTS_N_INSNS (1) + rtx_cost (src3, SET))
&& have_add2_insn (reg, new_src))
- success
- = validate_change (next, &PATTERN (next),
- gen_add2_insn (reg, new_src), 0);
+ {
+ rtx newpat = gen_add2_insn (reg, new_src);
+ if (INSN_P (newpat)
+ && NEXT_INSN (newpat) == NULL_RTX)
+ newpat = PATTERN (newpat);
+ success
+ = validate_change (next, &PATTERN (next),
+ newpat, 0);
+ }
if (success)
delete_insn (insn);
insn = next;
reg_mode[regno] = GET_MODE (reg);
- reg_offset[regno] = sext_for_mode (GET_MODE (reg),
- added_offset
- + base_offset);
+ reg_offset[regno] =
+ trunc_int_for_mode (added_offset + base_offset,
+ GET_MODE (reg));
continue;
}
}
regno += REGNO (dst);
- if (HARD_REGNO_NREGS (regno, mode) == 1 && GET_CODE (set) == SET
+ if (SCALAR_INT_MODE_P (mode)
+ && HARD_REGNO_NREGS (regno, mode) == 1 && GET_CODE (set) == SET
&& GET_CODE (SET_DEST (set)) != ZERO_EXTRACT
&& GET_CODE (SET_DEST (set)) != SIGN_EXTRACT
&& GET_CODE (SET_DEST (set)) != STRICT_LOW_PART)
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]);
+ reg_offset[regno] = trunc_int_for_mode (offset
+ + reg_offset[base_regno],
+ dst_mode);
}
else
{
If it's placed after a trapping call (i.e. that
call is the last insn anyway), we have no fallthru
edge. Simply delete this use and don't try to insert
- on the non-existant edge. */
+ on the non-existent edge. */
if (GET_CODE (PATTERN (insn)) != USE)
{
/* We're not deleting it, we're moving it. */
}
}
}
+ /* We've possibly turned single trapping insn into multiple ones. */
+ if (flag_non_call_exceptions)
+ {
+ sbitmap blocks;
+ blocks = sbitmap_alloc (last_basic_block);
+ sbitmap_ones (blocks);
+ find_many_sub_basic_blocks (blocks);
+ }
if (inserted)
commit_edge_insertions ();
}