+/* Mark a pseudo, or one of its subwords, as dead. REGNO is the pseudo's
+ register number; ORIG_REG is the access in the insn, which may be a
+ subreg. */
+static void
+mark_pseudo_reg_dead (rtx orig_reg, unsigned regno)
+{
+ if (df_read_modify_subreg_p (orig_reg))
+ {
+ mark_pseudo_regno_subword_dead (regno,
+ subreg_lowpart_p (orig_reg) ? 0 : 1);
+ }
+ else
+ mark_pseudo_regno_dead (regno);
+}
+
+/* Mark the register referenced by definition DEF as dead, if the
+ definition is a total one. */
+static void
+mark_ref_dead (df_ref def)
+{
+ rtx reg = DF_REF_REG (def);
+ rtx orig_reg = reg;
+
+ if (DF_REF_FLAGS_IS_SET (def, DF_REF_CONDITIONAL))
+ return;
+
+ if (GET_CODE (reg) == SUBREG)
+ reg = SUBREG_REG (reg);
+
+ if (DF_REF_FLAGS_IS_SET (def, DF_REF_PARTIAL)
+ && (GET_CODE (orig_reg) != SUBREG
+ || REGNO (reg) < FIRST_PSEUDO_REGISTER
+ || !df_read_modify_subreg_p (orig_reg)))
+ return;
+
+ if (REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+ mark_pseudo_reg_dead (orig_reg, REGNO (reg));
+ else
+ mark_hard_reg_dead (reg);
+}
+
+/* If REG is a pseudo or a subreg of it, and the class of its allocno
+ intersects CL, make a conflict with pseudo DREG. ORIG_DREG is the
+ rtx actually accessed, it may be indentical to DREG or a subreg of it.
+ 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
+make_pseudo_conflict (rtx reg, enum reg_class cl, rtx dreg, rtx orig_dreg,
+ bool advance_p)
+{
+ rtx orig_reg = reg;
+ ira_allocno_t a;
+
+ 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_CLASS (a)))
+ return advance_p;
+
+ if (advance_p)
+ curr_point++;
+
+ mark_pseudo_reg_live (orig_reg, REGNO (reg));
+ mark_pseudo_reg_live (orig_dreg, REGNO (dreg));
+ mark_pseudo_reg_dead (orig_reg, REGNO (reg));
+ mark_pseudo_reg_dead (orig_dreg, REGNO (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.
+ ORIG_DREG is the rtx actually accessed, it may be indentical to
+ DREG or a subreg of it. 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, rtx orig_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, orig_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, orig_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, orig_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];
+ rtx orig_dreg = dreg;
+
+ 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_CLASS (a);
+ if (! reg_classes_intersect_p (acl, def_cl))
+ return;
+
+ advance_p = true;
+
+ for (use = 0; use < recog_data.n_operands; use++)
+ {
+ int alt1;
+
+ if (use == def || recog_data.operand_type[use] == OP_OUT)
+ continue;
+
+ 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 (alt1 < recog_data.n_alternatives)
+ continue;
+
+ advance_p = check_and_make_def_use_conflict (dreg, orig_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, orig_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;
+}
+