/* IRA processing allocno lives to build allocno live ranges.
- Copyright (C) 2006, 2007, 2008
+ Copyright (C) 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by Vladimir Makarov <vmakarov@redhat.com>.
#include "tm_p.h"
#include "target.h"
#include "flags.h"
+#include "except.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "insn-config.h"
/* The loop tree node corresponding to the current basic block. */
static ira_loop_tree_node_t curr_bb_node;
+/* The number of the last processed call. */
+static int last_call_num;
+/* The number of last call at which given allocno was saved. */
+static int *allocno_saved_at_call;
+
/* The function processing birth of register REGNO. It updates living
hard regs and conflict hard regs for living allocnos or starts a
new live range for the allocno corresponding to REGNO if it is
static void
update_allocno_pressure_excess_length (ira_allocno_t a)
{
- int start;
- enum reg_class cover_class;
+ int start, i;
+ enum reg_class cover_class, cl;
allocno_live_range_t p;
cover_class = ALLOCNO_COVER_CLASS (a);
- if (high_pressure_start_point[cover_class] < 0)
- return;
- p = ALLOCNO_LIVE_RANGES (a);
- ira_assert (p != NULL);
- start = (high_pressure_start_point[cover_class] > p->start
- ? high_pressure_start_point[cover_class] : p->start);
- ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a) += curr_point - start + 1;
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i]) != LIM_REG_CLASSES;
+ i++)
+ {
+ if (high_pressure_start_point[cl] < 0)
+ continue;
+ p = ALLOCNO_LIVE_RANGES (a);
+ ira_assert (p != NULL);
+ start = (high_pressure_start_point[cl] > p->start
+ ? high_pressure_start_point[cl] : p->start);
+ ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a) += curr_point - start + 1;
+ }
}
/* Process the death of register REGNO. This updates hard_regs_live
static void
set_allocno_live (ira_allocno_t a)
{
- int nregs;
- enum reg_class cover_class;
+ int i;
+ enum reg_class cover_class, cl;
+ /* Invalidate because it is referenced. */
+ allocno_saved_at_call[ALLOCNO_NUM (a)] = 0;
if (sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
return;
sparseset_set_bit (allocnos_live, ALLOCNO_NUM (a));
IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a), hard_regs_live);
IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a), hard_regs_live);
cover_class = ALLOCNO_COVER_CLASS (a);
- nregs = ira_reg_class_nregs[cover_class][ALLOCNO_MODE (a)];
- curr_reg_pressure[cover_class] += nregs;
- if (high_pressure_start_point[cover_class] < 0
- && (curr_reg_pressure[cover_class]
- > ira_available_class_regs[cover_class]))
- high_pressure_start_point[cover_class] = curr_point;
- if (curr_bb_node->reg_pressure[cover_class]
- < curr_reg_pressure[cover_class])
- curr_bb_node->reg_pressure[cover_class] = curr_reg_pressure[cover_class];
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i]) != LIM_REG_CLASSES;
+ i++)
+ {
+ curr_reg_pressure[cl] += ira_reg_class_nregs[cl][ALLOCNO_MODE (a)];
+ if (high_pressure_start_point[cl] < 0
+ && (curr_reg_pressure[cl] > ira_available_class_regs[cl]))
+ high_pressure_start_point[cl] = curr_point;
+ if (curr_bb_node->reg_pressure[cl] < curr_reg_pressure[cl])
+ curr_bb_node->reg_pressure[cl] = curr_reg_pressure[cl];
+ }
}
/* Mark allocno A as currently not living and update current register
static void
clear_allocno_live (ira_allocno_t a)
{
- unsigned int i;
- enum reg_class cover_class;
+ int i;
+ unsigned int j;
+ enum reg_class cover_class, cl;
+ bool set_p;
+ /* Invalidate because it is referenced. */
+ allocno_saved_at_call[ALLOCNO_NUM (a)] = 0;
if (sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
{
cover_class = ALLOCNO_COVER_CLASS (a);
- curr_reg_pressure[cover_class]
- -= ira_reg_class_nregs[cover_class][ALLOCNO_MODE (a)];
- ira_assert (curr_reg_pressure[cover_class] >= 0);
- if (high_pressure_start_point[cover_class] >= 0
- && (curr_reg_pressure[cover_class]
- <= ira_available_class_regs[cover_class]))
+ set_p = false;
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i])
+ != LIM_REG_CLASSES;
+ i++)
{
- EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
- {
- update_allocno_pressure_excess_length (ira_allocnos[i]);
- }
- high_pressure_start_point[cover_class] = -1;
+ curr_reg_pressure[cl] -= ira_reg_class_nregs[cl][ALLOCNO_MODE (a)];
+ ira_assert (curr_reg_pressure[cl] >= 0);
+ if (high_pressure_start_point[cl] >= 0
+ && curr_reg_pressure[cl] <= ira_available_class_regs[cl])
+ set_p = true;
+ }
+ if (set_p)
+ {
+ EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, j)
+ update_allocno_pressure_excess_length (ira_allocnos[j]);
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i])
+ != LIM_REG_CLASSES;
+ i++)
+ if (high_pressure_start_point[cl] >= 0
+ && curr_reg_pressure[cl] <= ira_available_class_regs[cl])
+ high_pressure_start_point[cl] = -1;
+
}
}
sparseset_clear_bit (allocnos_live, ALLOCNO_NUM (a));
static void
mark_reg_live (rtx reg)
{
- int regno;
+ int i, regno;
gcc_assert (REG_P (reg));
regno = REGNO (reg);
if (a != NULL)
{
if (sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
- return;
+ {
+ /* Invalidate because it is referenced. */
+ allocno_saved_at_call[ALLOCNO_NUM (a)] = 0;
+ return;
+ }
set_allocno_live (a);
}
make_regno_born (regno);
else if (! TEST_HARD_REG_BIT (ira_no_alloc_regs, regno))
{
int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
- enum reg_class cover_class;
+ enum reg_class cover_class, cl;
while (regno < last)
{
if (! TEST_HARD_REG_BIT (hard_regs_live, regno)
&& ! TEST_HARD_REG_BIT (eliminable_regset, regno))
{
- cover_class = ira_class_translate[REGNO_REG_CLASS (regno)];
- if (cover_class != NO_REGS)
+ cover_class = ira_hard_regno_cover_class[regno];
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i])
+ != LIM_REG_CLASSES;
+ i++)
{
- curr_reg_pressure[cover_class]++;
- if (high_pressure_start_point[cover_class] < 0
- && (curr_reg_pressure[cover_class]
- > ira_available_class_regs[cover_class]))
- high_pressure_start_point[cover_class] = curr_point;
+ curr_reg_pressure[cl]++;
+ if (high_pressure_start_point[cl] < 0
+ && (curr_reg_pressure[cl]
+ > ira_available_class_regs[cl]))
+ high_pressure_start_point[cl] = curr_point;
}
make_regno_born (regno);
- if (cover_class != NO_REGS
- && (curr_bb_node->reg_pressure[cover_class]
- < curr_reg_pressure[cover_class]))
- curr_bb_node->reg_pressure[cover_class]
- = curr_reg_pressure[cover_class];
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i])
+ != LIM_REG_CLASSES;
+ i++)
+ {
+ if (curr_bb_node->reg_pressure[cl] < curr_reg_pressure[cl])
+ curr_bb_node->reg_pressure[cl] = curr_reg_pressure[cl];
+ }
}
regno++;
}
/* Mark the register referenced by use or def REF as live. */
static void
-mark_ref_live (struct df_ref *ref)
+mark_ref_live (df_ref ref)
{
rtx reg;
if (a != NULL)
{
if (! sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)))
- return;
+ {
+ /* Invalidate because it is referenced. */
+ allocno_saved_at_call[ALLOCNO_NUM (a)] = 0;
+ return;
+ }
clear_allocno_live (a);
}
make_regno_dead (regno);
}
else if (! TEST_HARD_REG_BIT (ira_no_alloc_regs, regno))
{
- unsigned int i;
+ int i;
+ unsigned int j;
int last = regno + hard_regno_nregs[regno][GET_MODE (reg)];
- enum reg_class cover_class;
+ enum reg_class cover_class, cl;
+ bool set_p;
while (regno < last)
{
if (TEST_HARD_REG_BIT (hard_regs_live, regno))
{
- cover_class = ira_class_translate[REGNO_REG_CLASS (regno)];
- if (cover_class != NO_REGS)
+ set_p = false;
+ cover_class = ira_hard_regno_cover_class[regno];
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i])
+ != LIM_REG_CLASSES;
+ i++)
+ {
+ curr_reg_pressure[cl]--;
+ if (high_pressure_start_point[cl] >= 0
+ && curr_reg_pressure[cl] <= ira_available_class_regs[cl])
+ set_p = true;
+ ira_assert (curr_reg_pressure[cl] >= 0);
+ }
+ if (set_p)
{
- curr_reg_pressure[cover_class]--;
- if (high_pressure_start_point[cover_class] >= 0
- && (curr_reg_pressure[cover_class]
- <= ira_available_class_regs[cover_class]))
- {
- EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
- {
- update_allocno_pressure_excess_length
- (ira_allocnos[i]);
- }
- high_pressure_start_point[cover_class] = -1;
- }
- ira_assert (curr_reg_pressure[cover_class] >= 0);
+ EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, j)
+ update_allocno_pressure_excess_length (ira_allocnos[j]);
+ for (i = 0;
+ (cl = ira_reg_class_super_classes[cover_class][i])
+ != LIM_REG_CLASSES;
+ i++)
+ if (high_pressure_start_point[cl] >= 0
+ && (curr_reg_pressure[cl]
+ <= ira_available_class_regs[cl]))
+ high_pressure_start_point[cl] = -1;
}
make_regno_dead (regno);
}
/* Mark the register referenced by definition DEF as dead, if the
definition is a total one. */
static void
-mark_ref_dead (struct df_ref *def)
+mark_ref_dead (df_ref def)
{
rtx reg;
mark_reg_dead (reg);
}
-/* Mark early clobber registers of the current INSN as live (if
- LIVE_P) or dead. Return true if there are such registers. */
+/* Make pseudo REG conflicting with pseudo DREG, if the 1st pseudo
+ class is intersected with class CL. Advance the current program
+ point before making the conflict if ADVANCE_P. Return TRUE if we
+ will need to advance the current program point. */
static bool
-mark_early_clobbers (rtx insn, bool live_p)
+make_pseudo_conflict (rtx reg, enum reg_class cl, rtx dreg, bool advance_p)
{
- int alt;
- int def;
- struct df_ref **def_rec;
- bool set_p = false;
- bool asm_p = asm_noperands (PATTERN (insn)) >= 0;
+ ira_allocno_t a;
- if (asm_p)
- for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
- if (DF_REF_FLAGS_IS_SET (*def_rec, DF_REF_MUST_CLOBBER))
- {
- if (live_p)
- mark_ref_live (*def_rec);
- else
- mark_ref_dead (*def_rec);
- set_p = true;
- }
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ if (! REG_P (reg) || REGNO (reg) < FIRST_PSEUDO_REGISTER)
+ return advance_p;
+
+ a = ira_curr_regno_allocno_map[REGNO (reg)];
+ if (! reg_classes_intersect_p (cl, ALLOCNO_COVER_CLASS (a)))
+ return advance_p;
+
+ if (advance_p)
+ curr_point++;
- for (def = 0; def < recog_data.n_operands; def++)
+ mark_reg_live (reg);
+ mark_reg_live (dreg);
+ mark_reg_dead (reg);
+ mark_reg_dead (dreg);
+
+ return false;
+}
+
+/* Check and make if necessary conflicts for pseudo DREG of class
+ DEF_CL of the current insn with input operand USE of class USE_CL.
+ Advance the current program point before making the conflict if
+ ADVANCE_P. Return TRUE if we will need to advance the current
+ program point. */
+static bool
+check_and_make_def_use_conflict (rtx dreg, enum reg_class def_cl,
+ int use, enum reg_class use_cl,
+ bool advance_p)
+{
+ if (! reg_classes_intersect_p (def_cl, use_cl))
+ return advance_p;
+
+ advance_p = make_pseudo_conflict (recog_data.operand[use],
+ use_cl, dreg, advance_p);
+ /* Reload may end up swapping commutative operands, so you
+ have to take both orderings into account. The
+ constraints for the two operands can be completely
+ different. (Indeed, if the constraints for the two
+ operands are the same for all alternatives, there's no
+ point marking them as commutative.) */
+ if (use < recog_data.n_operands - 1
+ && recog_data.constraints[use][0] == '%')
+ advance_p
+ = make_pseudo_conflict (recog_data.operand[use + 1],
+ use_cl, dreg, advance_p);
+ if (use >= 1
+ && recog_data.constraints[use - 1][0] == '%')
+ advance_p
+ = make_pseudo_conflict (recog_data.operand[use - 1],
+ use_cl, dreg, advance_p);
+ return advance_p;
+}
+
+/* Check and make if necessary conflicts for definition DEF of class
+ DEF_CL of the current insn with input operands. Process only
+ constraints of alternative ALT. */
+static void
+check_and_make_def_conflict (int alt, int def, enum reg_class def_cl)
+{
+ int use, use_match;
+ ira_allocno_t a;
+ enum reg_class use_cl, acl;
+ bool advance_p;
+ rtx dreg = recog_data.operand[def];
+
+ if (def_cl == NO_REGS)
+ return;
+
+ if (GET_CODE (dreg) == SUBREG)
+ dreg = SUBREG_REG (dreg);
+
+ if (! REG_P (dreg) || REGNO (dreg) < FIRST_PSEUDO_REGISTER)
+ return;
+
+ a = ira_curr_regno_allocno_map[REGNO (dreg)];
+ acl = ALLOCNO_COVER_CLASS (a);
+ if (! reg_classes_intersect_p (acl, def_cl))
+ return;
+
+ advance_p = true;
+
+ for (use = 0; use < recog_data.n_operands; use++)
{
- rtx dreg = recog_data.operand[def];
-
- if (GET_CODE (dreg) == SUBREG)
- dreg = SUBREG_REG (dreg);
- if (! REG_P (dreg))
+ int alt1;
+
+ if (use == def || recog_data.operand_type[use] == OP_OUT)
continue;
- for (alt = 0; alt < recog_data.n_alternatives; alt++)
- if ((recog_op_alt[def][alt].earlyclobber)
- && (recog_op_alt[def][alt].cl != NO_REGS))
+ if (recog_op_alt[use][alt].anything_ok)
+ use_cl = ALL_REGS;
+ else
+ use_cl = recog_op_alt[use][alt].cl;
+
+ /* If there's any alternative that allows USE to match DEF, do not
+ record a conflict. If that causes us to create an invalid
+ instruction due to the earlyclobber, reload must fix it up. */
+ for (alt1 = 0; alt1 < recog_data.n_alternatives; alt1++)
+ if (recog_op_alt[use][alt1].matches == def
+ || (use < recog_data.n_operands - 1
+ && recog_data.constraints[use][0] == '%'
+ && recog_op_alt[use + 1][alt1].matches == def)
+ || (use >= 1
+ && recog_data.constraints[use - 1][0] == '%'
+ && recog_op_alt[use - 1][alt1].matches == def))
break;
- if (alt >= recog_data.n_alternatives)
+ if (alt1 < recog_data.n_alternatives)
continue;
- if (live_p)
- mark_reg_live (dreg);
- else
- mark_reg_dead (dreg);
- set_p = true;
+ advance_p = check_and_make_def_use_conflict (dreg, def_cl, use,
+ use_cl, advance_p);
+
+ if ((use_match = recog_op_alt[use][alt].matches) >= 0)
+ {
+ if (use_match == def)
+ continue;
+
+ if (recog_op_alt[use_match][alt].anything_ok)
+ use_cl = ALL_REGS;
+ else
+ use_cl = recog_op_alt[use_match][alt].cl;
+ advance_p = check_and_make_def_use_conflict (dreg, def_cl, use,
+ use_cl, advance_p);
+ }
}
+}
+
+/* Make conflicts of early clobber pseudo registers of the current
+ insn with its inputs. Avoid introducing unnecessary conflicts by
+ checking classes of the constraints and pseudos because otherwise
+ significant code degradation is possible for some targets. */
+static void
+make_early_clobber_and_input_conflicts (void)
+{
+ int alt;
+ int def, def_match;
+ enum reg_class def_cl;
+
+ for (alt = 0; alt < recog_data.n_alternatives; alt++)
+ for (def = 0; def < recog_data.n_operands; def++)
+ {
+ def_cl = NO_REGS;
+ if (recog_op_alt[def][alt].earlyclobber)
+ {
+ if (recog_op_alt[def][alt].anything_ok)
+ def_cl = ALL_REGS;
+ else
+ def_cl = recog_op_alt[def][alt].cl;
+ check_and_make_def_conflict (alt, def, def_cl);
+ }
+ if ((def_match = recog_op_alt[def][alt].matches) >= 0
+ && (recog_op_alt[def_match][alt].earlyclobber
+ || recog_op_alt[def][alt].earlyclobber))
+ {
+ if (recog_op_alt[def_match][alt].anything_ok)
+ def_cl = ALL_REGS;
+ else
+ def_cl = recog_op_alt[def_match][alt].cl;
+ check_and_make_def_conflict (alt, def, def_cl);
+ }
+ }
+}
+
+/* Mark early clobber hard registers of the current INSN as live (if
+ LIVE_P) or dead. Return true if there are such registers. */
+static bool
+mark_hard_reg_early_clobbers (rtx insn, bool live_p)
+{
+ df_ref *def_rec;
+ bool set_p = false;
+
+ for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
+ if (DF_REF_FLAGS_IS_SET (*def_rec, DF_REF_MUST_CLOBBER))
+ {
+ rtx dreg = DF_REF_REG (*def_rec);
+
+ if (GET_CODE (dreg) == SUBREG)
+ dreg = SUBREG_REG (dreg);
+ if (! REG_P (dreg) || REGNO (dreg) >= FIRST_PSEUDO_REGISTER)
+ continue;
+
+ /* Hard register clobbers are believed to be early clobber
+ because there is no way to say that non-operand hard
+ register clobbers are not early ones. */
+ if (live_p)
+ mark_ref_live (*def_rec);
+ else
+ mark_ref_dead (*def_rec);
+ set_p = true;
+ }
+
return set_p;
}
break;
case 'n':
- if (GET_CODE (op) == CONST_INT
+ if (CONST_INT_P (op)
|| (GET_CODE (op) == CONST_DOUBLE && GET_MODE (op) == VOIDmode)
|| (equiv_const != NULL_RTX
- && (GET_CODE (equiv_const) == CONST_INT
+ && (CONST_INT_P (equiv_const)
|| (GET_CODE (equiv_const) == CONST_DOUBLE
&& GET_MODE (equiv_const) == VOIDmode))))
return NO_REGS;
break;
-
+
case 's':
- if ((CONSTANT_P (op) && GET_CODE (op) != CONST_INT
+ if ((CONSTANT_P (op) && !CONST_INT_P (op)
&& (GET_CODE (op) != CONST_DOUBLE || GET_MODE (op) != VOIDmode))
|| (equiv_const != NULL_RTX
&& CONSTANT_P (equiv_const)
- && GET_CODE (equiv_const) != CONST_INT
+ && !CONST_INT_P (equiv_const)
&& (GET_CODE (equiv_const) != CONST_DOUBLE
|| GET_MODE (equiv_const) != VOIDmode)))
return NO_REGS;
break;
-
+
case 'I':
case 'J':
case 'K':
case 'N':
case 'O':
case 'P':
- if ((GET_CODE (op) == CONST_INT
+ if ((CONST_INT_P (op)
&& CONST_OK_FOR_CONSTRAINT_P (INTVAL (op), c, constraints))
|| (equiv_const != NULL_RTX
- && GET_CODE (equiv_const) == CONST_INT
+ && CONST_INT_P (equiv_const)
&& CONST_OK_FOR_CONSTRAINT_P (INTVAL (equiv_const),
c, constraints)))
return NO_REGS;
break;
-
+
case 'E':
case 'F':
if (GET_CODE (op) == CONST_DOUBLE
== MODE_VECTOR_FLOAT)))))
return NO_REGS;
break;
-
+
case 'G':
case 'H':
if ((GET_CODE (op) == CONST_DOUBLE
? GENERAL_REGS
: REG_CLASS_FROM_CONSTRAINT (c, constraints));
if ((cl != NO_REGS && next_cl != cl)
- || ira_available_class_regs[next_cl] > 1)
+ || (ira_available_class_regs[next_cl]
+ > ira_reg_class_nregs[next_cl][GET_MODE (op)]))
return NO_REGS;
cl = next_cl;
break;
-
+
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
next_cl
= single_reg_class (recog_data.constraints[c - '0'],
recog_data.operand[c - '0'], NULL_RTX);
- if ((cl != NO_REGS && next_cl != cl) || next_cl == NO_REGS
- || ira_available_class_regs[next_cl] > 1)
+ if ((cl != NO_REGS && next_cl != cl)
+ || next_cl == NO_REGS
+ || (ira_available_class_regs[next_cl]
+ > ira_reg_class_nregs[next_cl][GET_MODE (op)]))
return NO_REGS;
cl = next_cl;
break;
-
+
default:
return NO_REGS;
}
recog_data.operand[op_num], NULL_RTX);
}
+/* The function sets up hard register set *SET to hard registers which
+ might be used by insn reloads because the constraints are too
+ strict. */
+void
+ira_implicitly_set_insn_hard_regs (HARD_REG_SET *set)
+{
+ int i, c, regno = 0;
+ bool ignore_p;
+ enum reg_class cl;
+ rtx op;
+ enum machine_mode mode;
+
+ CLEAR_HARD_REG_SET (*set);
+ for (i = 0; i < recog_data.n_operands; i++)
+ {
+ op = recog_data.operand[i];
+
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+
+ if (GET_CODE (op) == SCRATCH
+ || (REG_P (op) && (regno = REGNO (op)) >= FIRST_PSEUDO_REGISTER))
+ {
+ const char *p = recog_data.constraints[i];
+
+ mode = (GET_CODE (op) == SCRATCH
+ ? GET_MODE (op) : PSEUDO_REGNO_MODE (regno));
+ cl = NO_REGS;
+ for (ignore_p = false; (c = *p); p += CONSTRAINT_LEN (c, p))
+ if (c == '#')
+ ignore_p = true;
+ else if (c == ',')
+ ignore_p = false;
+ else if (! ignore_p)
+ switch (c)
+ {
+ case 'r':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'h': case 'j': case 'k': case 'l':
+ case 'q': case 't': case 'u':
+ case 'v': case 'w': case 'x': case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'Q': case 'R': case 'S': case 'T': case 'U':
+ case 'W': case 'Y': case 'Z':
+ cl = (c == 'r'
+ ? GENERAL_REGS
+ : REG_CLASS_FROM_CONSTRAINT (c, p));
+ if (cl != NO_REGS
+ /* There is no register pressure problem if all of the
+ regs in this class are fixed. */
+ && ira_available_class_regs[cl] != 0
+ && (ira_available_class_regs[cl]
+ <= ira_reg_class_nregs[cl][mode]))
+ IOR_HARD_REG_SET (*set, reg_class_contents[cl]);
+ break;
+ }
+ }
+ }
+}
/* Processes input operands, if IN_P, or output operands otherwise of
the current insn with FREQ to find allocno which can use only one
hard register and makes other currently living allocnos conflicting
{
int i, regno, cost;
unsigned int px;
- enum reg_class cl, cover_class;
+ enum reg_class cl;
rtx operand;
ira_allocno_t operand_a, a;
if (GET_CODE (operand) == SUBREG)
operand = SUBREG_REG (operand);
-
+
if (REG_P (operand)
&& (regno = REGNO (operand)) >= FIRST_PSEUDO_REGISTER)
{
[ira_class_hard_regs[cl][0]]) >= 0
&& reg_class_size[cl] <= (unsigned) CLASS_MAX_NREGS (cl, mode))
{
- /* ??? FREQ */
- cost = freq * (in_p
- ? ira_register_move_cost[mode][cover_class][cl]
- : ira_register_move_cost[mode][cl][cover_class]);
+ int i, size;
+ cost
+ = (freq
+ * (in_p
+ ? ira_get_register_move_cost (mode, cover_class, cl)
+ : ira_get_register_move_cost (mode, cl, cover_class)));
ira_allocate_and_set_costs
(&ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a), cover_class, 0);
- ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a)
- [ira_class_hard_reg_index
- [cover_class][ira_class_hard_regs[cl][0]]]
- -= cost;
+ size = ira_reg_class_nregs[cover_class][mode];
+ for (i = 0; i < size; i++)
+ ALLOCNO_CONFLICT_HARD_REG_COSTS (operand_a)
+ [ira_class_hard_reg_index
+ [cover_class][ira_class_hard_regs[cl][i]]]
+ -= cost;
}
}
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, px)
{
a = ira_allocnos[px];
- cover_class = ALLOCNO_COVER_CLASS (a);
if (a != operand_a)
{
/* We could increase costs of A instead of making it
}
}
+/* Return true when one of the predecessor edges of BB is marked with
+ EDGE_ABNORMAL_CALL or EDGE_EH. */
+static bool
+bb_has_abnormal_call_pred (basic_block bb)
+{
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ if (e->flags & (EDGE_ABNORMAL_CALL | EDGE_EH))
+ return true;
+ }
+ return false;
+}
+
/* Process insns of the basic block given by its LOOP_TREE_NODE to
update allocno live ranges, allocno hard register conflicts,
intersected calls, and register pressure info for allocnos for the
unsigned int j;
basic_block bb;
rtx insn;
- edge e;
- edge_iterator ei;
bitmap_iterator bi;
bitmap reg_live_out;
unsigned int px;
for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (TEST_HARD_REG_BIT (hard_regs_live, i))
{
- enum reg_class cover_class;
-
- cover_class = REGNO_REG_CLASS (i);
- if (cover_class == NO_REGS)
- continue;
- cover_class = ira_class_translate[cover_class];
- curr_reg_pressure[cover_class]++;
- if (curr_bb_node->reg_pressure[cover_class]
- < curr_reg_pressure[cover_class])
- curr_bb_node->reg_pressure[cover_class]
- = curr_reg_pressure[cover_class];
- ira_assert (curr_reg_pressure[cover_class]
- <= ira_available_class_regs[cover_class]);
+ enum reg_class cover_class, cl;
+
+ cover_class = ira_class_translate[REGNO_REG_CLASS (i)];
+ for (j = 0;
+ (cl = ira_reg_class_super_classes[cover_class][j])
+ != LIM_REG_CLASSES;
+ j++)
+ {
+ curr_reg_pressure[cl]++;
+ if (curr_bb_node->reg_pressure[cl] < curr_reg_pressure[cl])
+ curr_bb_node->reg_pressure[cl] = curr_reg_pressure[cl];
+ ira_assert (curr_reg_pressure[cl]
+ <= ira_available_class_regs[cl]);
+ }
}
EXECUTE_IF_SET_IN_BITMAP (reg_live_out, FIRST_PSEUDO_REGISTER, j, bi)
{
ira_allocno_t a = ira_curr_regno_allocno_map[j];
-
+
if (a == NULL)
continue;
ira_assert (! sparseset_bit_p (allocnos_live, ALLOCNO_NUM (a)));
set_allocno_live (a);
make_regno_born (j);
}
-
+
freq = REG_FREQ_FROM_BB (bb);
if (freq == 0)
freq = 1;
+ /* Invalidate all allocno_saved_at_call entries. */
+ last_call_num++;
+
/* Scan the code of this basic block, noting which allocnos and
hard regs are born or die.
pessimistic, but it probably doesn't matter much in practice. */
FOR_BB_INSNS_REVERSE (bb, insn)
{
- struct df_ref **def_rec, **use_rec;
+ df_ref *def_rec, *use_rec;
bool call_p;
-
- if (! INSN_P (insn))
+
+ if (!NONDEBUG_INSN_P (insn))
continue;
-
+
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
fprintf (ira_dump_file, " Insn %u(l%d): point = %d\n",
INSN_UID (insn), loop_tree_node->parent->loop->num,
}
}
}
-
+
extract_insn (insn);
preprocess_constraints ();
process_single_reg_class_operands (false, freq);
-
+
/* See which defined values die here. */
for (def_rec = DF_INSN_DEFS (insn); *def_rec; def_rec++)
if (!call_p || !DF_REF_FLAGS_IS_SET (*def_rec, DF_REF_MAY_CLOBBER))
if (call_p)
{
+ last_call_num++;
/* The current set of live allocnos are live across the call. */
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, i)
{
ira_allocno_t a = ira_allocnos[i];
-
- ALLOCNO_CALL_FREQ (a) += freq;
+
+ if (allocno_saved_at_call[i] != last_call_num)
+ /* Here we are mimicking caller-save.c behaviour
+ which does not save hard register at a call if
+ it was saved on previous call in the same basic
+ block and the hard register was not mentioned
+ between the two calls. */
+ ALLOCNO_CALL_FREQ (a) += freq;
+ /* Mark it as saved at the next call. */
+ allocno_saved_at_call[i] = last_call_num + 1;
ALLOCNO_CALLS_CROSSED_NUM (a)++;
/* Don't allocate allocnos that cross setjmps or any
call, if this function receives a nonlocal
SET_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a));
SET_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a));
}
+ if (can_throw_internal (insn))
+ {
+ IOR_HARD_REG_SET (ALLOCNO_TOTAL_CONFLICT_HARD_REGS (a),
+ call_used_reg_set);
+ IOR_HARD_REG_SET (ALLOCNO_CONFLICT_HARD_REGS (a),
+ call_used_reg_set);
+ }
}
}
-
+
+ make_early_clobber_and_input_conflicts ();
+
curr_point++;
/* Mark each used value as live. */
for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
mark_ref_live (*use_rec);
- set_p = mark_early_clobbers (insn, true);
-
process_single_reg_class_operands (true, freq);
-
+
+ set_p = mark_hard_reg_early_clobbers (insn, true);
+
if (set_p)
- mark_early_clobbers (insn, false);
+ {
+ mark_hard_reg_early_clobbers (insn, false);
+
+ /* Mark each hard reg as live again. For example, a
+ hard register can be in clobber and in an insn
+ input. */
+ for (use_rec = DF_INSN_USES (insn); *use_rec; use_rec++)
+ {
+ rtx ureg = DF_REF_REG (*use_rec);
+
+ if (GET_CODE (ureg) == SUBREG)
+ ureg = SUBREG_REG (ureg);
+ if (! REG_P (ureg) || REGNO (ureg) >= FIRST_PSEUDO_REGISTER)
+ continue;
+
+ mark_ref_live (*use_rec);
+ }
+ }
curr_point++;
}
+#ifdef EH_RETURN_DATA_REGNO
+ if (bb_has_eh_pred (bb))
+ for (j = 0; ; ++j)
+ {
+ unsigned int regno = EH_RETURN_DATA_REGNO (j);
+ if (regno == INVALID_REGNUM)
+ break;
+ make_regno_born (regno);
+ }
+#endif
+
/* Allocnos can't go in stack regs at the start of a basic block
that is reached by an abnormal edge. Likewise for call
clobbered regs, because caller-save, fixup_abnormal_edges and
possibly the table driven EH machinery are not quite ready to
handle such allocnos live across such edges. */
- FOR_EACH_EDGE (e, ei, bb->preds)
- if (e->flags & EDGE_ABNORMAL)
- break;
-
- if (e != NULL)
+ if (bb_has_abnormal_pred (bb))
{
#ifdef STACK_REGS
EXECUTE_IF_SET_IN_SPARSESET (allocnos_live, px)
/* No need to record conflicts for call clobbered regs if we
have nonlocal labels around, as we don't ever try to
allocate such regs in this case. */
- if (!cfun->has_nonlocal_label)
+ if (!cfun->has_nonlocal_label && bb_has_abnormal_call_pred (bb))
for (px = 0; px < FIRST_PSEUDO_REGISTER; px++)
if (call_used_regs[px])
make_regno_born (px);
allocno_live_range_t r;
bitmap born_or_died;
bitmap_iterator bi;
-
+
born_or_died = ira_allocate_bitmap ();
FOR_EACH_ALLOCNO (a, ai)
{
{
allocnos_live = sparseset_alloc (ira_allocnos_num);
curr_point = 0;
+ last_call_num = 0;
+ allocno_saved_at_call
+ = (int *) ira_allocate (ira_allocnos_num * sizeof (int));
+ memset (allocno_saved_at_call, 0, ira_allocnos_num * sizeof (int));
ira_traverse_loop_tree (true, ira_loop_tree_root, NULL,
process_bb_node_lives);
ira_max_point = curr_point;
if (internal_flag_ira_verbose > 2 && ira_dump_file != NULL)
print_live_ranges (ira_dump_file);
/* Clean up. */
+ ira_free (allocno_saved_at_call);
sparseset_free (allocnos_live);
}