/* Register renaming for the GNU compiler.
- Copyright (C) 2000, 2001 Free Software Foundation, Inc.
+ Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
This file is part of GCC.
/* See whether it accepts all modes that occur in
definition and uses. */
for (tmp = this; tmp; tmp = tmp->next_use)
- if (! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc)))
+ if (! HARD_REGNO_MODE_OK (new_reg, GET_MODE (*tmp->loc))
+ || (tmp->need_caller_save_reg
+ && ! (HARD_REGNO_CALL_PART_CLOBBERED
+ (reg, GET_MODE (*tmp->loc)))
+ && (HARD_REGNO_CALL_PART_CLOBBERED
+ (new_reg, GET_MODE (*tmp->loc)))))
break;
if (! tmp)
{
if (locI)
scan_rtx_address (insn, locI, INDEX_REG_CLASS, action, mode);
if (locB)
- scan_rtx_address (insn, locB, BASE_REG_CLASS, action, mode);
+ scan_rtx_address (insn, locB, MODE_BASE_REG_CLASS (mode), action, mode);
return;
}
break;
case MEM:
- scan_rtx_address (insn, &XEXP (x, 0), BASE_REG_CLASS, action,
+ scan_rtx_address (insn, &XEXP (x, 0),
+ MODE_BASE_REG_CLASS (GET_MODE (x)), action,
GET_MODE (x));
return;
case CONST:
case CONST_INT:
case CONST_DOUBLE:
+ case CONST_VECTOR:
case SYMBOL_REF:
case LABEL_REF:
case CC0:
return;
case MEM:
- scan_rtx_address (insn, &XEXP (x, 0), BASE_REG_CLASS, action,
+ scan_rtx_address (insn, &XEXP (x, 0),
+ MODE_BASE_REG_CLASS (GET_MODE (x)), action,
GET_MODE (x));
return;
rtx note;
rtx old_operands[MAX_RECOG_OPERANDS];
rtx old_dups[MAX_DUP_OPERANDS];
- int i;
+ int i, icode;
int alt;
int predicated;
(6) For any write we find in an operand, make a new chain.
(7) For any REG_UNUSED, close any chains we just opened. */
+ icode = recog_memoized (insn);
extract_insn (insn);
- constrain_operands (1);
+ if (! constrain_operands (1))
+ fatal_insn_not_found (insn);
preprocess_constraints ();
alt = which_alternative;
n_ops = recog_data.n_operands;
}
for (i = 0; i < recog_data.n_dups; i++)
{
+ int dup_num = recog_data.dup_num[i];
+
old_dups[i] = *recog_data.dup_loc[i];
*recog_data.dup_loc[i] = cc0_rtx;
+
+ /* For match_dup of match_operator or match_parallel, share
+ them, so that we don't miss changes in the dup. */
+ if (icode >= 0
+ && insn_data[icode].operand[dup_num].eliminable == 0)
+ old_dups[i] = recog_data.operand[dup_num];
}
scan_rtx (insn, &PATTERN (insn), NO_REGS, terminate_all_read,
scan_rtx (insn, &CALL_INSN_FUNCTION_USAGE (insn),
NO_REGS, terminate_all_read, OP_IN, 0);
+ /* Step 2C: Can't rename asm operands that were originally
+ hard registers. */
+ if (asm_noperands (PATTERN (insn)) > 0)
+ for (i = 0; i < n_ops; i++)
+ {
+ rtx *loc = recog_data.operand_loc[i];
+ rtx op = *loc;
+
+ if (GET_CODE (op) == REG
+ && REGNO (op) == ORIGINAL_REGNO (op)
+ && (recog_data.operand_type[i] == OP_IN
+ || recog_data.operand_type[i] == OP_INOUT))
+ scan_rtx (insn, loc, NO_REGS, terminate_all_read, OP_IN, 0);
+ }
+
/* Step 3: Append to chains for reads inside operands. */
for (i = 0; i < n_ops + recog_data.n_dups; i++)
{
/* Step 6: Begin new chains for writes inside operands. */
/* ??? Many targets have output constraints on the SET_DEST
of a call insn, which is stupid, since these are certainly
- ABI defined hard registers. Don't change calls at all. */
- if (GET_CODE (insn) != CALL_INSN)
+ ABI defined hard registers. Don't change calls at all.
+ Similarly take special care for asm statement that originally
+ referenced hard registers. */
+ if (asm_noperands (PATTERN (insn)) > 0)
+ {
+ for (i = 0; i < n_ops; i++)
+ if (recog_data.operand_type[i] == OP_OUT)
+ {
+ rtx *loc = recog_data.operand_loc[i];
+ rtx op = *loc;
+ enum reg_class class = recog_op_alt[i][alt].class;
+
+ if (GET_CODE (op) == REG
+ && REGNO (op) == ORIGINAL_REGNO (op))
+ continue;
+
+ scan_rtx (insn, loc, class, mark_write, OP_OUT,
+ recog_op_alt[i][alt].earlyclobber);
+ }
+ }
+ else if (GET_CODE (insn) != CALL_INSN)
for (i = 0; i < n_ops + recog_data.n_dups; i++)
{
int opn = i < n_ops ? i : recog_data.dup_num[i - n_ops];
static void copy_value PARAMS ((rtx, rtx, struct value_data *));
static bool mode_change_ok PARAMS ((enum machine_mode, enum machine_mode,
unsigned int));
-static rtx find_oldest_value_reg PARAMS ((enum reg_class, unsigned int,
- enum machine_mode,
- struct value_data *));
+static rtx find_oldest_value_reg PARAMS ((enum reg_class, rtx,
+ struct value_data *));
static bool replace_oldest_value_reg PARAMS ((rtx *, enum reg_class, rtx,
struct value_data *));
static bool replace_oldest_value_addr PARAMS ((rtx *, enum reg_class,
rtx x;
struct value_data *vd;
{
+ /* SUBREGS are supposed to have been eliminated by now. But some
+ ports, e.g. i386 sse, use them to smuggle vector type information
+ through to instruction selection. Each such SUBREG should simplify,
+ so if we get a NULL we've done something wrong elsewhere. */
+
+ if (GET_CODE (x) == SUBREG)
+ x = simplify_subreg (GET_MODE (x), SUBREG_REG (x),
+ GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
if (REG_P (x))
{
unsigned int regno = REGNO (x);
{
if (vd->e[j].mode == VOIDmode)
continue;
- n = HARD_REGNO_NREGS (regno, vd->e[j].mode);
+ n = HARD_REGNO_NREGS (j, vd->e[j].mode);
if (j + n > regno)
for (i = 0; i < n; ++i)
kill_value_regno (j + i, vd);
void *data;
{
struct value_data *vd = data;
- if (GET_CODE (set) != CLOBBER && REG_P (x))
+ if (GET_CODE (set) != CLOBBER)
{
kill_value (x, vd);
- set_value_regno (REGNO (x), GET_MODE (x), vd);
+ if (REG_P (x))
+ set_value_regno (REGNO (x), GET_MODE (x), vd);
}
}
{
unsigned int dr = REGNO (dest);
unsigned int sr = REGNO (src);
+ unsigned int dn, sn;
unsigned int i;
/* ??? At present, it's possible to see noop sets. It'd be nice if
if (frame_pointer_needed && dr == HARD_FRAME_POINTER_REGNUM)
return;
+ /* If SRC and DEST overlap, don't record anything. */
+ dn = HARD_REGNO_NREGS (dr, GET_MODE (dest));
+ sn = HARD_REGNO_NREGS (sr, GET_MODE (dest));
+ if ((dr > sr && dr < sr + sn)
+ || (sr > dr && sr < dr + dn))
+ return;
+
/* If SRC had no assigned mode (i.e. we didn't know it was live)
assign it now and assume the value came from an input argument
or somesuch. */
if (vd->e[sr].mode == VOIDmode)
set_value_regno (sr, vd->e[dr].mode, vd);
+ /* If SRC had been assigned a mode narrower than the copy, we can't
+ link DEST into the chain, because not all of the pieces of the
+ copy came from oldest_regno. */
+ else if (sn > (unsigned int) HARD_REGNO_NREGS (sr, vd->e[sr].mode))
+ return;
+
/* Link DR at the end of the value chain used by SR. */
vd->e[dr].oldest_regno = vd->e[sr].oldest_regno;
of that oldest register, otherwise return NULL. */
static rtx
-find_oldest_value_reg (class, regno, mode, vd)
+find_oldest_value_reg (class, reg, vd)
enum reg_class class;
- unsigned int regno;
- enum machine_mode mode;
+ rtx reg;
struct value_data *vd;
{
+ unsigned int regno = REGNO (reg);
+ enum machine_mode mode = GET_MODE (reg);
unsigned int i;
+ /* If we are accessing REG in some mode other that what we set it in,
+ make sure that the replacement is valid. In particular, consider
+ (set (reg:DI r11) (...))
+ (set (reg:SI r9) (reg:SI r11))
+ (set (reg:SI r10) (...))
+ (set (...) (reg:DI r9))
+ Replacing r9 with r11 is invalid. */
+ if (mode != vd->e[regno].mode)
+ {
+ if (HARD_REGNO_NREGS (regno, mode)
+ > HARD_REGNO_NREGS (regno, vd->e[regno].mode))
+ return NULL_RTX;
+ }
+
for (i = vd->e[regno].oldest_regno; i != regno; i = vd->e[i].next_regno)
if (TEST_HARD_REG_BIT (reg_class_contents[class], i)
&& (vd->e[i].mode == mode
- || mode_change_ok (vd->e[i].mode, mode, regno)))
- return gen_rtx_REG (mode, i);
+ || mode_change_ok (vd->e[i].mode, mode, i)))
+ {
+ rtx new = gen_rtx_raw_REG (mode, i);
+ ORIGINAL_REGNO (new) = ORIGINAL_REGNO (reg);
+ return new;
+ }
return NULL_RTX;
}
rtx insn;
struct value_data *vd;
{
- rtx new = find_oldest_value_reg (class, REGNO (*loc), GET_MODE (*loc), vd);
+ rtx new = find_oldest_value_reg (class, *loc, vd);
if (new)
{
if (rtl_dump_file)
changed |= replace_oldest_value_addr (locI, INDEX_REG_CLASS, mode,
insn, vd);
if (locB)
- changed |= replace_oldest_value_addr (locB, BASE_REG_CLASS, mode,
- insn, vd);
+ changed |= replace_oldest_value_addr (locB,
+ MODE_BASE_REG_CLASS (mode),
+ mode, insn, vd);
return changed;
}
rtx insn;
struct value_data *vd;
{
- return replace_oldest_value_addr (&XEXP (x, 0), BASE_REG_CLASS,
+ return replace_oldest_value_addr (&XEXP (x, 0),
+ MODE_BASE_REG_CLASS (GET_MODE (x)),
GET_MODE (x), insn, vd);
}
for (insn = bb->head; ; insn = NEXT_INSN (insn))
{
int n_ops, i, alt, predicated;
+ bool is_asm;
rtx set;
if (! INSN_P (insn))
set = single_set (insn);
extract_insn (insn);
- constrain_operands (1);
+ if (! constrain_operands (1))
+ fatal_insn_not_found (insn);
preprocess_constraints ();
alt = which_alternative;
n_ops = recog_data.n_operands;
+ is_asm = asm_noperands (PATTERN (insn)) >= 0;
/* Simplify the code below by rewriting things to reflect
matching constraints. Also promote OP_OUT to OP_INOUT
be able to do the move from a different register class. */
if (set && REG_P (SET_SRC (set)))
{
- unsigned int regno = REGNO (SET_SRC (set));
- enum machine_mode mode = GET_MODE (SET_SRC (set));
+ rtx src = SET_SRC (set);
+ unsigned int regno = REGNO (src);
+ enum machine_mode mode = GET_MODE (src);
unsigned int i;
rtx new;
+ /* If we are accessing SRC in some mode other that what we
+ set it in, make sure that the replacement is valid. */
+ if (mode != vd->e[regno].mode)
+ {
+ if (HARD_REGNO_NREGS (regno, mode)
+ > HARD_REGNO_NREGS (regno, vd->e[regno].mode))
+ goto no_move_special_case;
+ }
+
/* If the destination is also a register, try to find a source
register in the same class. */
if (REG_P (SET_DEST (set)))
{
- new = find_oldest_value_reg (REGNO_REG_CLASS (regno),
- regno, mode, vd);
+ new = find_oldest_value_reg (REGNO_REG_CLASS (regno), src, vd);
if (new && validate_change (insn, &SET_SRC (set), new, 0))
{
if (rtl_dump_file)
/* Otherwise, try all valid registers and see if its valid. */
for (i = vd->e[regno].oldest_regno; i != regno;
i = vd->e[i].next_regno)
- if (mode == vd->e[regno].mode)
+ if (vd->e[i].mode == mode
+ || mode_change_ok (vd->e[i].mode, mode, i))
{
- new = gen_rtx_REG (mode, i);
+ new = gen_rtx_raw_REG (mode, i);
if (validate_change (insn, &SET_SRC (set), new, 0))
{
+ ORIGINAL_REGNO (new) = ORIGINAL_REGNO (src);
if (rtl_dump_file)
fprintf (rtl_dump_file,
"insn %u: replaced reg %u with %u\n",
}
}
}
+ no_move_special_case:
/* For each input operand, replace a hard register with the
eldest live copy that's in an appropriate register class. */
if (recog_data.constraints[i][0] == '\0')
continue;
+ /* Don't replace in asms intentionally referencing hard regs. */
+ if (is_asm && GET_CODE (recog_data.operand[i]) == REG
+ && (REGNO (recog_data.operand[i])
+ == ORIGINAL_REGNO (recog_data.operand[i])))
+ continue;
+
if (recog_data.operand_type[i] == OP_IN)
{
if (recog_op_alt[i][alt].is_address)
j != INVALID_REGNUM;
j = vd->e[j].next_regno)
{
- if (TEST_HARD_REG_BIT (set, vd->e[j].next_regno))
+ if (TEST_HARD_REG_BIT (set, j))
{
fprintf (stderr, "[%u] Loop in regno chain\n", j);
return;