summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc')
-rw-r--r--gcc/global.c65
-rw-r--r--gcc/local-alloc.c22
2 files changed, 86 insertions, 1 deletions
diff --git a/gcc/global.c b/gcc/global.c
index 913c61eff5e..fca911e247f 100644
--- a/gcc/global.c
+++ b/gcc/global.c
@@ -248,6 +248,7 @@ static HARD_REG_SET eliminable_regset;
static int allocno_compare ();
static void mark_reg_store ();
static void mark_reg_clobber ();
+static void mark_reg_conflicts ();
static void mark_reg_live_nc ();
static void mark_reg_death ();
static void dump_conflicts ();
@@ -698,6 +699,31 @@ global_conflicts ()
mark_reg_store (XEXP (link, 0), 0);
#endif
+ /* If INSN has multiple outputs, then any reg that dies here
+ and is used inside of an output
+ must conflict with the other outputs. */
+
+ if (GET_CODE (PATTERN (insn)) == PARALLEL && !single_set (insn))
+ for (link = REG_NOTES (insn); link; link = XEXP (link, 1))
+ if (REG_NOTE_KIND (link) == REG_DEAD)
+ {
+ int used_in_output = 0;
+ int i;
+ rtx reg = XEXP (link, 0);
+
+ for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
+ {
+ rtx set = XVECEXP (PATTERN (insn), 0, i);
+ if (GET_CODE (set) == SET
+ && GET_CODE (SET_DEST (set)) != REG
+ && !rtx_equal_p (reg, SET_DEST (set))
+ && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ used_in_output = 1;
+ }
+ if (used_in_output)
+ mark_reg_conflicts (reg);
+ }
+
/* Mark any registers set in INSN and then never used. */
while (n_regs_set > 0)
@@ -1334,6 +1360,45 @@ mark_reg_clobber (reg, setter)
}
}
}
+
+/* Record that REG has conflicts with all the regs currently live.
+ Do not mark REG itself as live. */
+
+static void
+mark_reg_conflicts (reg)
+ rtx reg;
+{
+ register int regno;
+
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ if (GET_CODE (reg) != REG)
+ return;
+
+ regno = REGNO (reg);
+
+ if (reg_renumber[regno] >= 0)
+ regno = reg_renumber[regno];
+
+ /* Either this is one of the max_allocno pseudo regs not allocated,
+ or it is or has a hardware reg. First handle the pseudo-regs. */
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (reg_allocno[regno] >= 0)
+ record_one_conflict (regno);
+ }
+ /* Handle hardware regs (and pseudos allocated to hard regs). */
+ else if (! fixed_regs[regno])
+ {
+ register int last = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
+ while (regno < last)
+ {
+ record_one_conflict (regno);
+ regno++;
+ }
+ }
+}
/* Mark REG as being dead (following the insn being scanned now).
Store a 0 in regs_live or allocnos_live for this register. */
diff --git a/gcc/local-alloc.c b/gcc/local-alloc.c
index 90cac29b0a0..fc45217d11a 100644
--- a/gcc/local-alloc.c
+++ b/gcc/local-alloc.c
@@ -1729,7 +1729,8 @@ reg_is_born (reg, birth)
/* Record the death of REG in the current insn. If OUTPUT_P is non-zero,
REG is an output that is dying (i.e., it is never used), otherwise it
- is an input (the normal case). */
+ is an input (the normal case).
+ If OUTPUT_P is 1, then we extend the life past the end of this insn. */
static void
wipe_dead_reg (reg, output_p)
@@ -1738,6 +1739,25 @@ wipe_dead_reg (reg, output_p)
{
register int regno = REGNO (reg);
+ /* If this insn has multiple results,
+ and the dead reg is used in one of the results,
+ extend its life to after this insn,
+ so it won't get allocated together with any other result of this insn. */
+ if (GET_CODE (PATTERN (this_insn)) == PARALLEL
+ && !single_set (this_insn))
+ {
+ int i;
+ for (i = XVECLEN (PATTERN (this_insn), 0) - 1; i >= 0; i--)
+ {
+ rtx set = XVECEXP (PATTERN (this_insn), 0, i);
+ if (GET_CODE (set) == SET
+ && GET_CODE (SET_DEST (set)) != REG
+ && !rtx_equal_p (reg, SET_DEST (set))
+ && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+ output_p = 1;
+ }
+ }
+
if (regno < FIRST_PSEUDO_REGISTER)
{
mark_life (regno, GET_MODE (reg), 0);