Merged latest trunk changes to ira-select.
From-SVN: r279316
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 6ed7aba..05afcea 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,21 @@
+2018-07-18 Vladimir Makarov <vmakarov@redhat.com>
+
+ Merge with trunk.
+ * common.opt (fira-select): New. Choose the selection algorithm
+ by default.
+ * ira-costs.c (struct op_info, op_infos, insn_selection): New.
+ (insn_selections): New.
+ (CONST_POOL_OK_P, SMALL_REGISTER_CLASS_P, general_constant_p):
+ New.
+ (insn_constraints, setup_insn_alt): New.
+ (record_address_regs): Forward declaration.
+ (define_op_cost_from_alt): New.
+ (scan_one_insn): Call setup_insn_alt and define_op_cost_from_alt
+ for the selection algorithm.
+ (find_costs_and_classes): Release insn_constraints and
+ insn_selections. Don't reuse already calculated pseudo classes
+ for the selection algorithm.
+
2019-11-22 Jakub Jelinek <jakub@redhat.com>
PR c++/92458
diff --git a/gcc/common.opt b/gcc/common.opt
index 404b6aa..eb0ea35 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1836,6 +1836,10 @@
Use IRA based register pressure calculation
in RTL loop optimizations.
+fira-select
+Common Report Var(flag_ira_select) Init(1)
+Pseudo-register class calculations based on insn alternative selection.
+
fira-share-save-slots
Common Report Var(flag_ira_share_save_slots) Init(1) Optimization
Share slots for saving different hard registers.
diff --git a/gcc/ira-costs.c b/gcc/ira-costs.c
index baf7261..5eeb7d7 100644
--- a/gcc/ira-costs.c
+++ b/gcc/ira-costs.c
@@ -473,6 +473,928 @@
+/* Some insn operand info after choosing an insn alternative. */
+struct op_info
+{
+ /* Flags of that insn alternative permits the operand to be in
+ memory or an address. */
+ bool allows_mem_p;
+ bool allows_addr_p;
+ /* Class of the operand permitted by the insn alternative. It is an
+ approximation. The accuracy can be improved if we have a set of
+ register classes. Does we need such accuracy? The question is
+ open. */
+ enum reg_class op_class;
+};
+
+/* Container of the above info about the insn operands. */
+static vec<struct op_info> op_infos;
+
+/* Info about selected alternative of the insns. We could use set of
+ alternatives here. Does it worth spent time for keeping and
+ processing more one alternative? The question is open. */
+struct insn_selection
+{
+ /* Number of the selcted alternative. */
+ int alt;
+ /* Start index of the insn operands info in op_infos. */
+ int op_info_start;
+};
+
+/* Container of the above info about the selected insns
+ alternative. */
+static vec<struct insn_selection> insn_selections;
+
+/* True if X is a constant that can be forced into the constant pool.
+ MODE is the mode of the operand, or VOIDmode if not known. */
+#define CONST_POOL_OK_P(MODE, X) \
+ ((MODE) != VOIDmode \
+ && CONSTANT_P (X) \
+ && GET_CODE (X) != HIGH \
+ && !targetm.cannot_force_const_mem (MODE, X))
+
+/* True if C is a non-empty register class that has too few registers
+ to be safely used as a reload target class. */
+#define SMALL_REGISTER_CLASS_P(C) \
+ (reg_class_size [(C)] == 1 \
+ || (reg_class_size [(C)] >= 1 && targetm.class_likely_spilled_p (C)))
+
+static inline bool
+general_constant_p (rtx x)
+{
+ return CONSTANT_P (x) && (! flag_pic || LEGITIMATE_PIC_OPERAND_P (x));
+}
+
+/* A container used for quick access to operand constraints for given
+ alternative. Use preferred pseudo reg classes from the previous
+ pass when it is possible. */
+static vec<const char *> insn_constraints;
+
+/* Choose INSN alternative and setup corresponding elements of
+ INSN_SELECTIONS and OP_INFOS. */
+static void
+setup_insn_alt (rtx_insn *insn)
+{
+ bool ok_p = false;
+ int nop, best_overall, overall, nalt, i, m, len, uid;
+ /* LOSERS counts the operands that don't fit this alternative and
+ would require loading. */
+ int best_losers, losers;
+ /* REJECT is a count of how undesirable this alternative says it is
+ if any reloading is required. If the alternative matches exactly
+ then REJECT is ignored, but otherwise it gets this much counted
+ against it in addition to the reloading needed. */
+ int reject;
+ enum reg_class curr_alt[MAX_RECOG_OPERANDS];
+ enum reg_class goal_op_alt[MAX_RECOG_OPERANDS];
+ bool curr_alt_win[MAX_RECOG_OPERANDS];
+ bool curr_alt_offmemok[MAX_RECOG_OPERANDS];
+ bool goal_alt_offmemok[MAX_RECOG_OPERANDS];
+ bool curr_alt_addrok[MAX_RECOG_OPERANDS];
+ bool goal_alt_addrok[MAX_RECOG_OPERANDS];
+ int curr_alt_matches[MAX_RECOG_OPERANDS];
+ rtx op;
+ /* The register when the operand is a subreg of register, otherwise the
+ operand itself. */
+ rtx no_subreg_reg_operand[MAX_RECOG_OPERANDS];
+ /* The register if the operand is a register or subreg of register,
+ otherwise NULL. */
+ rtx operand_reg[MAX_RECOG_OPERANDS];
+ enum op_type operand_type[MAX_RECOG_OPERANDS];
+ int hard_regno[MAX_RECOG_OPERANDS];
+ bool curr_swapped, goal_alt_swapped = false;
+ bool early_clobber_p[MAX_RECOG_OPERANDS];
+ int best_reload_nregs = 0, reload_nregs;
+ bool costly_p, addrok;
+ enum reg_class cl;
+ enum constraint_num cn;
+ enum machine_mode curr_operand_mode[MAX_RECOG_OPERANDS];
+ enum machine_mode mode;
+ int goal_alt_number = -1;
+ int commutative = -1;
+ bool no_input_reloads_p, no_output_reloads_p;
+ rtx set;
+ const char *p;
+
+ uid = INSN_UID (insn);
+ len = insn_selections.length ();
+ if (len <= uid)
+ {
+ insn_selections.safe_grow (uid + 1);
+ for (i = len; i <= uid; i++)
+ insn_selections[i].alt = -1;
+ }
+ if (!NONDEBUG_INSN_P (insn))
+ {
+ insn_selections[INSN_UID (insn)].alt = -1;
+ return;
+ }
+ if ((set = single_set (insn)) != NULL_RTX)
+ {
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ if (GET_CODE (dest) == SUBREG)
+ dest = SUBREG_REG (dest);
+ if (GET_CODE (src) == SUBREG)
+ src = SUBREG_REG (src);
+ if (((REG_P (dest) && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
+ || 0&&MEM_P (dest))
+ && ((REG_P (src) && REGNO (src) >= FIRST_PSEUDO_REGISTER)
+ || 0&&MEM_P (src)))
+ {
+ if (ira_dump_file != NULL)
+ fprintf (ira_dump_file, "insn %d is move -- ignore\n", INSN_UID (insn));
+ insn_selections[INSN_UID (insn)].alt = -1;
+ return;
+ }
+ }
+ no_input_reloads_p = no_output_reloads_p = false;
+ /* JUMP_INSNs and CALL_INSNs are not allowed to have any output
+ reloads; neither are insns that SET cc0. Insns that use CC0 are
+ not allowed to have any input reloads. */
+ if (JUMP_P (insn) || CALL_P (insn))
+ no_output_reloads_p = true;
+#ifdef HAVE_cc0
+ if (reg_referenced_p (cc0_rtx, PATTERN (insn)))
+ no_input_reloads_p = true;
+ if (reg_set_p (cc0_rtx, PATTERN (insn)))
+ no_output_reloads_p = true;
+#endif
+ insn_constraints.truncate (0);
+ insn_constraints.safe_grow_cleared (recog_data.n_operands
+ * recog_data.n_alternatives + 1);
+ best_losers = best_overall = INT_MAX;
+ for (curr_swapped = false;;curr_swapped = true)
+ {
+ /* Calculate some data common for all alternatives to speed up the
+ function. */
+ for (nop = 0; nop < recog_data.n_operands; nop++)
+ {
+ operand_type[nop] = OP_IN;
+ op = no_subreg_reg_operand[nop] = recog_data.operand[nop];
+ operand_reg[nop] = op;
+ hard_regno[nop] = -1;
+ if (GET_CODE (operand_reg[nop]) == SUBREG)
+ operand_reg[nop] = SUBREG_REG (operand_reg[nop]);
+ if (REG_P (operand_reg[nop]))
+ {
+ no_subreg_reg_operand[nop] = operand_reg[nop];
+ if (REGNO (operand_reg[nop]) < FIRST_PSEUDO_REGISTER)
+ hard_regno[nop] = REGNO (operand_reg[nop]);
+ }
+ else
+ operand_reg[nop] = NULL_RTX;
+ for (nalt = 0, p = recog_data.constraints[nop];
+ nalt < recog_data.n_alternatives;
+ nalt++)
+ {
+ insn_constraints[nop * recog_data.n_alternatives + nalt] = p;
+ while (*p && *p != ',')
+ p++;
+ if (*p)
+ p++;
+ }
+ mode = GET_MODE (recog_data.operand[nop]);
+ curr_operand_mode[nop] = mode;
+ }
+
+ alternative_mask enabled = get_enabled_alternatives (insn);
+ /* The constraints are made of several alternatives. Each operand's
+ constraint looks like foo,bar,... with commas separating the
+ alternatives. The first alternatives for all operands go
+ together, the second alternatives go together, etc.
+
+ First loop over alternatives. */
+ for (nalt = 0; nalt < recog_data.n_alternatives; nalt++)
+ {
+ /* Loop over operands for one constraint alternative. */
+ if (!TEST_BIT (enabled, nalt))
+ continue;
+
+ overall = losers = reject = reload_nregs = 0;
+
+ for (nop = 0; nop < recog_data.n_operands; nop++)
+ {
+ int len, c, this_alternative_matches;
+ bool win, did_match, offmemok;
+ /* false => this operand can be reloaded somehow for this
+ alternative. */
+ bool badop;
+ /* true => this operand can be reloaded if the alternative
+ allows regs. */
+ bool winreg;
+ /* True if a constant forced into memory would be OK for
+ this operand. */
+ bool constmemok;
+ enum reg_class this_alternative, this_costly_alternative;
+ bool this_alternative_win;
+ bool this_alternative_offmemok;
+ bool scratch_p;
+ enum machine_mode mode;
+
+ p = insn_constraints[nop * recog_data.n_alternatives + nalt];
+ if (*p == 0 || *p == ',')
+ {
+ /* Fast track for no constraints at all. */
+ curr_alt[nop] = NO_REGS;
+ curr_alt_win[nop] = true;
+ curr_alt_offmemok[nop] = false;
+ curr_alt_addrok[nop] = false;
+ curr_alt_matches[nop] = -1;
+ continue;
+ }
+
+ op = no_subreg_reg_operand[nop];
+ mode = curr_operand_mode[nop];
+
+ win = did_match = winreg = offmemok = constmemok = addrok = false;
+ badop = true;
+
+ early_clobber_p[nop] = false;
+
+ this_costly_alternative = this_alternative = NO_REGS;
+ this_alternative_win = false;
+ this_alternative_offmemok = false;
+ this_alternative_matches = -1;
+ scratch_p = GET_CODE (no_subreg_reg_operand[nop]) == SCRATCH;
+
+ /* Scan this alternative's specs for this operand; set WIN
+ if the operand fits any letter in this alternative.
+ Otherwise, clear BADOP if this operand could fit some
+ letter after reloads, or set WINREG if this operand could
+ fit after reloads provided the constraint allows some
+ registers. */
+ costly_p = false;
+ do
+ {
+ switch ((c = *p, len = CONSTRAINT_LEN (c, p)), c)
+ {
+ case '\0':
+ len = 0;
+ break;
+ case ',':
+ c = '\0';
+ break;
+ case '=':
+ operand_type[nop] = OP_OUT;
+ break;
+ case '+':
+ operand_type[nop] = OP_INOUT;
+ break;
+ case ' ': case '\t':
+ break;
+ case '*':
+ /* Ignore the next letter for this pass. */
+ c = *++p;
+ len = CONSTRAINT_LEN (c, p);
+ break;
+ case '^':
+ case '?':
+ reject += 6;
+ break;
+ case '!':
+ reject += 600;
+ break;
+ case '%':
+ /* We only support one commutative marker, the first
+ one. We already set commutative above. */
+ if (commutative < 0)
+ commutative = nop;
+ break;
+ case '&':
+ early_clobber_p[nop] = true;
+ break;
+
+ case '#':
+ /* Ignore rest of this alternative. */
+ do
+ p++;
+ while (*p && *p != ',');
+ c = '\0';
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ char *end;
+ bool match_p;
+
+ m = strtoul (p, &end, 10);
+ p = end;
+ len = 0;
+ ira_assert (nop > m);
+
+ this_alternative_matches = m;
+ /* We are supposed to match a previous
+ operand. If we do, we win if that one did.
+ If we do not, count both of the operands as
+ losers. (This is too conservative, since
+ most of the time only a single reload insn
+ will be needed to make the two operands
+ win. As a result, this alternative may be
+ rejected when it is actually
+ desirable.) */
+ match_p = false;
+ if (rtx_equal_p (recog_data.operand[nop],
+ recog_data.operand[m]))
+ {
+ /* We should reject matching of an early
+ clobber operand if the matching operand is
+ not dying in the insn. */
+ if (! early_clobber_p[m]
+ || operand_reg[nop] == NULL_RTX
+ || (find_regno_note (insn, REG_DEAD,
+ REGNO (op))
+ || REGNO (op) == REGNO (operand_reg[m])))
+ match_p = true;
+ }
+ if (match_p)
+ {
+ /* If we are matching a non-offsettable
+ address where an offsettable address
+ was expected, then we must reject this
+ combination, because we can't reload
+ it. */
+ if (curr_alt_offmemok[m]
+ && MEM_P (recog_data.operand[m])
+ && curr_alt[m] == NO_REGS && ! curr_alt_win[m])
+ continue;
+
+ }
+ else
+ {
+ /* Operands don't match. Both operands must
+ allow a reload register, otherwise we
+ cannot make them match. */
+ if (curr_alt[m] == NO_REGS)
+ break;
+ /* Retroactively mark the operand we had to
+ match as a loser, if it wasn't already and
+ it wasn't matched to a register constraint
+ (e.g it might be matched by memory). */
+ if (curr_alt_win[m] && operand_reg[m] == NULL_RTX)
+ {
+ losers++;
+ reload_nregs
+ += (ira_reg_class_max_nregs[curr_alt[m]]
+ [GET_MODE (recog_data.operand[m])]);
+ }
+
+ /* We prefer no matching alternatives because
+ it gives more freedom in RA. */
+ if (operand_reg[nop] == NULL_RTX
+ || (find_regno_note (insn, REG_DEAD,
+ REGNO (operand_reg[nop]))
+ == NULL_RTX))
+ reject += 2;
+ }
+ /* If we have to reload this operand and some
+ previous operand also had to match the same
+ thing as this operand, we don't know how to do
+ that. */
+ if (!match_p || !curr_alt_win[m])
+ {
+ for (i = 0; i < nop; i++)
+ if (curr_alt_matches[i] == m)
+ break;
+ if (i < nop)
+ break;
+ }
+ else
+ did_match = true;
+
+ /* This can be fixed with reloads if the operand
+ we are supposed to match can be fixed with
+ reloads. */
+ badop = false;
+ this_alternative = curr_alt[m];
+ winreg = this_alternative != NO_REGS;
+ break;
+ }
+
+ case 'g':
+ if (MEM_P (op) || general_constant_p (op))
+ win = true;
+ /* Drop through into 'r' case. */
+ cl = GENERAL_REGS;
+ goto reg;
+
+ default:
+ cn = lookup_constraint (p);
+ switch (get_constraint_type (cn))
+ {
+ case CT_REGISTER:
+ cl = reg_class_for_constraint (cn);
+ if (cl != NO_REGS)
+ goto reg;
+ break;
+
+ case CT_CONST_INT:
+ if (CONST_INT_P (op)
+ && insn_const_int_ok_for_constraint (INTVAL (op), cn))
+ win = true;
+ break;
+
+ case CT_MEMORY:
+ if (constraint_satisfied_p (op, cn))
+ win = true;
+
+ /* If we didn't already win, we can reload
+ constants via force_const_mem or put the
+ pseudo value into memory, or make other
+ memory by reloading the address like for
+ 'o'. */
+ if (CONST_POOL_OK_P (mode, op)
+ || MEM_P (op) || REG_P (op))
+ badop = false;
+ constmemok = true;
+ offmemok = true;
+ break;
+
+ case CT_ADDRESS:
+ /* If we didn't already win, we can reload
+ the address into a base register. */
+ if (constraint_satisfied_p (op, cn))
+ win = true;
+ cl = base_reg_class (VOIDmode, ADDR_SPACE_GENERIC,
+ ADDRESS, SCRATCH);
+ badop = false;
+ addrok = true;
+ goto reg;
+
+ case CT_FIXED_FORM:
+ if (constraint_satisfied_p (op, cn))
+ win = true;
+ break;
+
+ case CT_SPECIAL_MEMORY:
+ if (constraint_satisfied_p (op, cn))
+ win = true;
+ break;
+ }
+ break;
+
+ reg:
+ this_alternative
+ = reg_class_subunion[this_alternative][cl];
+ if (costly_p)
+ this_costly_alternative
+ = (reg_class_subunion
+ [this_costly_alternative][cl]);
+ if (mode == BLKmode)
+ break;
+ winreg = true;
+ if (hard_regno[nop] >= 0)
+ {
+ if (in_hard_reg_set_p (reg_class_contents[this_alternative],
+ mode, hard_regno[nop]))
+ win = true;
+ }
+ else if ((pref == NULL && REG_P (op)) || scratch_p)
+ win = true;
+ else if (pref != NULL && REG_P (op)
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && pref[COST_INDEX (REGNO (op))] != NO_REGS
+ && (ira_class_subset_p
+ [pref[COST_INDEX (REGNO (op))]][this_alternative]))
+ win = true;
+ break;
+ }
+ if (c != ' ' && c != '\t')
+ costly_p = c == '*';
+ }
+ while ((p += len), c);
+
+ /* Record which operands fit this alternative. */
+ if (win)
+ {
+ this_alternative_win = true;
+ if (operand_reg[nop] != NULL_RTX || scratch_p)
+ {
+ if (this_costly_alternative == this_alternative
+ || (hard_regno[nop] >= 0
+ && in_hard_reg_set_p (reg_class_contents
+ [this_costly_alternative],
+ mode, hard_regno[nop]))
+ || (pref != NULL && REG_P (op)
+ && REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && (ira_class_subset_p
+ [pref[COST_INDEX (REGNO (op))]]
+ [this_costly_alternative])))
+ reject++;
+ }
+ }
+ else if (did_match)
+ ;
+ else
+ {
+ int const_to_mem = 0;
+ bool no_regs_p;
+
+ /* If this alternative asks for a specific reg class, see if there
+ is at least one allocatable register in that class. */
+ no_regs_p
+ = (this_alternative == NO_REGS
+ || (hard_reg_set_subset_p
+ (reg_class_contents[this_alternative],
+ ira_no_alloc_regs)));
+
+ /* For asms, verify that the class for this alternative is possible
+ for the mode that is specified. */
+ if (!no_regs_p && INSN_CODE (insn) < 0)
+ {
+ int i;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (targetm.hard_regno_mode_ok (i, mode)
+ && in_hard_reg_set_p (reg_class_contents[this_alternative],
+ mode, i))
+ break;
+ if (i == FIRST_PSEUDO_REGISTER)
+ winreg = false;
+ }
+
+ /* If this operand accepts a register, and if the
+ register class has at least one allocatable register,
+ then this operand can be reloaded. */
+ if (winreg && !no_regs_p)
+ badop = false;
+
+ if (badop)
+ goto fail;
+
+ this_alternative_offmemok = offmemok;
+ if (this_costly_alternative != NO_REGS)
+ reject++;
+ /* If the operand is dying, has a matching constraint,
+ and satisfies constraints of the matched operand
+ which failed to satisfy the own constraints, we do
+ not need to generate a reload insn for this
+ operand. */
+ if (!(this_alternative_matches >= 0
+ && !curr_alt_win[this_alternative_matches]
+ && REG_P (op)
+ && find_regno_note (insn, REG_DEAD, REGNO (op))
+ && ((hard_regno[nop] >= 0
+ && in_hard_reg_set_p (reg_class_contents
+ [curr_alt[this_alternative_matches]],
+ mode, hard_regno[nop]))
+ || pref == NULL
+ || (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && pref[COST_INDEX (REGNO (op))] != NO_REGS
+ && (ira_class_subset_p
+ [pref[COST_INDEX (REGNO (op))]]
+ [curr_alt[this_alternative_matches]])))))
+ losers++;
+ /* If this is a constant that is reloaded into the
+ desired class by copying it to memory first, count
+ that as another reload. This is consistent with
+ other code and is required to avoid choosing another
+ alternative when the constant is moved into memory.
+ Note that the test here is precisely the same as in
+ the code below that calls force_const_mem. */
+ if (CONST_POOL_OK_P (mode, op)
+ && ((targetm.preferred_reload_class
+ (op, this_alternative) == NO_REGS)
+ || no_input_reloads_p))
+ {
+ const_to_mem = 1;
+ if (! no_regs_p)
+ losers++;
+ }
+
+ /* Alternative loses if it requires a type of reload not
+ permitted for this insn. We can always reload
+ objects with a REG_UNUSED note. */
+ if ((operand_type[nop] != OP_IN
+ && no_output_reloads_p
+ && ! find_reg_note (insn, REG_UNUSED, op))
+ || (operand_type[nop] != OP_OUT
+ && no_input_reloads_p && ! const_to_mem))
+ goto fail;
+
+ /* Check strong discouragement of reload of non-constant
+ into class THIS_ALTERNATIVE. */
+ if (! CONSTANT_P (op) && ! no_regs_p
+ && (targetm.preferred_reload_class
+ (op, this_alternative) == NO_REGS
+ || (operand_type[nop] == OP_OUT
+ && (targetm.preferred_output_reload_class
+ (op, this_alternative) == NO_REGS))))
+ reject += 600;
+
+ if (! (MEM_P (op) && offmemok)
+ && ! (const_to_mem && constmemok))
+ {
+ /* We prefer to reload pseudos over reloading
+ other things, since such reloads may be able
+ to be eliminated later. So bump REJECT in
+ other cases. Don't do this in the case where
+ we are forcing a constant into memory and it
+ will then win since we don't want to have a
+ different alternative match then. */
+ if (! (scratch_p
+ || (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)))
+ reject += 2;
+
+ if (! no_regs_p)
+ reload_nregs
+ += ira_reg_class_max_nregs[this_alternative][mode];
+
+ if (SMALL_REGISTER_CLASS_P (this_alternative))
+ reject += 3;
+ }
+
+ /* We are trying to spill pseudo into memory. It is
+ usually more costly than moving to a hard
+ register although it might takes the same number
+ of reloads. */
+ if (no_regs_p && (REG_P (op) || scratch_p))
+ reject += 3;
+
+ /* Input reloads can be inherited more often than
+ output reloads can be removed, so penalize output
+ reloads. */
+ if (! (REG_P (op) || scratch_p) || operand_type[nop] != OP_IN)
+ reject++;
+ }
+
+ if (early_clobber_p[nop] && ! scratch_p)
+ reject++;
+
+ /* ??? We check early clobbers after processing all
+ operands (see loop below) and there we update the
+ costs more. Should we update the cost (may be
+ approximately) here because of early clobber register
+ reloads or it is a rare or non-important thing to be
+ worth to do it. */
+ overall = losers * 6 + reject;
+ if ((best_losers == 0 || losers != 0) && best_overall < overall)
+ goto fail;
+
+ curr_alt[nop] = this_alternative;
+ curr_alt_win[nop] = this_alternative_win;
+ curr_alt_offmemok[nop] = this_alternative_offmemok;
+ curr_alt_addrok[nop] = addrok;
+ curr_alt_matches[nop] = this_alternative_matches;
+
+ if (this_alternative_matches >= 0
+ && !did_match && !this_alternative_win)
+ curr_alt_win[this_alternative_matches] = false;
+ }
+ ok_p = true;
+ /* If this alternative can be made to work by reloading, and
+ it needs less reloading than the others checked so far,
+ record it as the chosen goal for reloading. */
+ if ((best_losers != 0 && losers == 0)
+ || (((best_losers == 0 && losers == 0)
+ || (best_losers != 0 && losers != 0))
+ && (best_overall > overall
+ || (best_overall == overall
+ /* If the cost of the reloads is the same,
+ prefer alternative which requires minimal
+ number of reload regs. */
+ && (reload_nregs < best_reload_nregs
+ || (reload_nregs == best_reload_nregs
+ && nalt < goal_alt_number))))))
+ {
+ goal_alt_swapped = curr_swapped;
+ for (nop = 0; nop < recog_data.n_operands; nop++)
+ {
+ goal_op_alt[nop] = curr_alt[nop];
+ goal_alt_offmemok[nop] = curr_alt_offmemok[nop];
+ goal_alt_addrok[nop] = curr_alt_addrok[nop];
+ if ((m = curr_alt_matches[nop]) >= 0)
+ {
+ goal_op_alt[nop] = reg_class_subunion[curr_alt[m]][curr_alt[nop]];
+ if (curr_alt_offmemok[m])
+ goal_alt_offmemok[nop] = true;
+ }
+ }
+ best_overall = overall;
+ best_losers = losers;
+ best_reload_nregs = reload_nregs;
+ goal_alt_number = nalt;
+ }
+ if (losers == 0)
+ /* Everything is satisfied. Do not process alternatives
+ anymore. */
+ break;
+ fail:
+ ;
+ }
+ if (commutative < 0)
+ break;
+ if (curr_swapped)
+ break;
+ op = recog_data.operand[commutative];
+ recog_data.operand[commutative] = recog_data.operand[commutative + 1];
+ recog_data.operand[commutative + 1] = op;
+ }
+ if (curr_swapped)
+ {
+ op = recog_data.operand[commutative];
+ recog_data.operand[commutative] = recog_data.operand[commutative + 1];
+ recog_data.operand[commutative + 1] = op;
+ }
+ if (! ok_p)
+ goal_alt_number = 0;
+ if (ira_dump_file != NULL)
+ {
+ fprintf (ira_dump_file, "insn %u", uid);
+ if (INSN_CODE (insn) >= 0
+ && (p = get_insn_name (INSN_CODE (insn))) != NULL)
+ fprintf (ira_dump_file, " {%s}", p);
+ fprintf (ira_dump_file, " will use alt %d%s:", goal_alt_number,
+ goal_alt_swapped ? " with swap" : "" );
+ }
+ len = op_infos.length ();
+ if (insn_selections[uid].alt >= 0 && insn_selections[uid].alt != goal_alt_number)
+ fprintf (stderr, "change alt %d to %d in insn %d\n",
+ insn_selections[uid].alt, goal_alt_number, uid);
+ insn_selections[uid].alt = goal_alt_number;
+ insn_selections[uid].op_info_start = len;
+ op_infos.safe_grow (len + recog_data.n_operands);
+ for (nop = 0; nop < recog_data.n_operands; nop++)
+ {
+ op_infos[len + nop].op_class = goal_op_alt[nop];
+ op_infos[len + nop].allows_mem_p = goal_alt_offmemok[nop];
+ op_infos[len + nop].allows_addr_p = goal_alt_addrok[nop];
+ if (ira_dump_file != NULL)
+ {
+ p = insn_constraints[nop * recog_data.n_alternatives + goal_alt_number];
+ if (p == NULL || *p == '\0')
+ continue;
+ fprintf (ira_dump_file, " (%d) ", nop);
+ for (; *p != '\0' && *p != ',' && *p != '#'; p++)
+ fputc (*p, ira_dump_file);
+ }
+ }
+ if (ira_dump_file != NULL)
+ fprintf (ira_dump_file, "\n");
+}
+
+static void
+record_address_regs (enum machine_mode mode, addr_space_t as, rtx x,
+ int context, enum rtx_code outer_code,
+ enum rtx_code index_code, int scale);
+
+/* Update reg classes and memory costs of pseudos after choosing
+ alternative ISEL of INSN. Don't update memory costs if
+ IGNORE_MEM_COST_P is TRUE. */
+static void
+define_op_cost_from_alt (rtx_insn *insn, const struct insn_selection &isel, bool ignore_mem_cost_p)
+{
+ int nop, op_start, regno, other_regno;
+ rtx set;
+
+ ira_assert (isel.alt >= 0);
+ op_start = isel.op_info_start;
+ for (nop = 0; nop < recog_data.n_operands; nop++)
+ {
+ rtx op = recog_data.operand[nop];
+ int k, add_cost, regno;
+ bool in_p, out_p, allows_mem_p;
+ enum machine_mode mode;
+ struct costs *pp;
+ int *pp_costs;
+ cost_classes_t cost_classes_ptr;
+ enum reg_class *cost_classes;
+ enum reg_class op_class, cost_class;
+ move_table *move_in_cost, *move_out_cost;
+
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ op_class = op_infos[op_start + nop].op_class;
+ if (MEM_P (op))
+ record_address_regs (GET_MODE (op), MEM_ADDR_SPACE (op),
+ XEXP (op, 0), 0, MEM, SCRATCH, frequency * 2);
+ else if (op_infos[op_start + nop].allows_addr_p)
+ record_address_regs (VOIDmode, ADDR_SPACE_GENERIC,
+ op, 0, ADDRESS, SCRATCH, frequency * 2);
+
+ if (! REG_P (op) || (regno = REGNO (op)) < FIRST_PSEUDO_REGISTER)
+ continue;
+
+ op_class = op_infos[op_start + nop].op_class;
+ mode = GET_MODE (op); /* ??? smallest mode for paradoxical subreg */
+ pp = COSTS (costs, COST_INDEX (regno));
+ cost_classes_ptr = regno_cost_classes[regno];
+ cost_classes = cost_classes_ptr->classes;
+ pp_costs = pp->cost;
+ in_p = recog_data.operand_type[nop] != OP_OUT;
+ out_p = recog_data.operand_type[nop] != OP_IN;
+ ira_init_register_move_cost_if_necessary (mode);
+ if (! in_p)
+ {
+ ira_assert (out_p);
+ move_out_cost = ira_may_move_out_cost[mode];
+ for (k = cost_classes_ptr->num - 1; k >= 0; k--)
+ {
+ cost_class = cost_classes[k];
+ add_cost = move_out_cost[op_class][cost_class] * frequency;
+ if (INT_MAX - add_cost < pp_costs[k])
+ pp_costs[k] = INT_MAX;
+ else
+ pp_costs[k] += add_cost;
+ }
+ }
+ else if (! out_p)
+ {
+ ira_assert (in_p);
+ move_in_cost = ira_may_move_in_cost[mode];
+ for (k = cost_classes_ptr->num - 1; k >= 0; k--)
+ {
+ cost_class = cost_classes[k];
+ add_cost = move_in_cost[cost_class][op_class] * frequency;
+ if (INT_MAX - add_cost < pp_costs[k])
+ pp_costs[k] = INT_MAX;
+ else
+ pp_costs[k] += add_cost;
+ }
+ }
+ else
+ {
+ move_in_cost = ira_may_move_in_cost[mode];
+ move_out_cost = ira_may_move_out_cost[mode];
+ for (k = cost_classes_ptr->num - 1; k >= 0; k--)
+ {
+ cost_class = cost_classes[k];
+ add_cost = (move_in_cost[cost_class][op_class]
+ + move_out_cost[op_class][cost_class]) * frequency;
+ if (INT_MAX - add_cost < pp_costs[k])
+ pp_costs[k] = INT_MAX;
+ else
+ pp_costs[k] += add_cost;
+ }
+ }
+
+ if (!ignore_mem_cost_p)
+ {
+ allows_mem_p = op_infos[op_start + nop].allows_mem_p;
+ /* If the alternative actually allows memory, make things a bit
+ cheaper since we won't need an extra insn to load it. */
+ add_cost = ((out_p ? ira_memory_move_cost[mode][op_class][0] : 0)
+ + (in_p ? ira_memory_move_cost[mode][op_class][1] : 0)
+ - allows_mem_p) * frequency;
+ if (INT_MAX - add_cost < pp->mem_cost)
+ pp->mem_cost = INT_MAX;
+ else
+ pp->mem_cost += add_cost;
+ }
+ }
+ if ((set = single_set (insn)) != NULL_RTX)
+ {
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+
+ dest = SET_DEST (set);
+ src = SET_SRC (set);
+ if (REG_P (src) && REG_P (dest)
+ && find_regno_note (insn, REG_DEAD, REGNO (src))
+ && (((regno = REGNO (src)) >= FIRST_PSEUDO_REGISTER
+ && (other_regno = REGNO (dest)) < FIRST_PSEUDO_REGISTER)
+ || ((regno = REGNO (dest)) >= FIRST_PSEUDO_REGISTER
+ && (other_regno = REGNO (src)) < FIRST_PSEUDO_REGISTER)))
+ {
+ struct costs *pp = COSTS (costs, COST_INDEX (regno));
+ int *pp_costs = pp->cost;
+ enum machine_mode mode = GET_MODE (src);
+ cost_classes_t cost_classes_ptr = regno_cost_classes[regno];
+ enum reg_class *cost_classes = cost_classes_ptr->classes;
+ reg_class_t rclass;
+ int k, nr;
+
+ for (k = cost_classes_ptr->num - 1; k >= 0; k--)
+ {
+ rclass = cost_classes[k];
+ if (TEST_HARD_REG_BIT (reg_class_contents[rclass], other_regno)
+ && (reg_class_size[(int) rclass]
+ == ira_reg_class_max_nregs [(int) rclass][(int) mode]))
+ {
+ if (reg_class_size[rclass] == 1)
+ pp_costs[k] -= frequency;
+ else
+ {
+ for (nr = 0;
+ nr < hard_regno_nregs (other_regno, mode);
+ nr++)
+ if (! TEST_HARD_REG_BIT (reg_class_contents[rclass],
+ other_regno + nr))
+ break;
+
+ if (nr == hard_regno_nregs (other_regno, mode))
+ pp_costs[k] -= frequency;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
/* Record the cost of using memory or hard registers of various
classes for the operands in INSN.
@@ -1480,6 +2402,10 @@
counted_mem = false;
set = single_set (insn);
extract_insn (insn);
+ if (flag_ira_select
+ && (insn_selections.length () <= (size_t) INSN_UID (insn)
+ || insn_selections [INSN_UID (insn)].alt < 0))
+ setup_insn_alt (insn);
/* If this insn loads a parameter from its stack slot, then it
represents a savings, rather than a cost, if the parameter is
@@ -1522,42 +2448,47 @@
counted_mem = true;
}
- record_operand_costs (insn, pref);
-
- /* Now add the cost for each operand to the total costs for its
- allocno. */
- for (i = 0; i < recog_data.n_operands; i++)
+ if (flag_ira_select && insn_selections [INSN_UID (insn)].alt >= 0)
+ define_op_cost_from_alt (insn, insn_selections [INSN_UID (insn)], counted_mem);
+ else
{
- rtx op = recog_data.operand[i];
+ record_operand_costs (insn, pref);
- if (GET_CODE (op) == SUBREG)
- op = SUBREG_REG (op);
- if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
+ /* Now add the cost for each operand to the total costs for its
+ allocno. */
+ for (i = 0; i < recog_data.n_operands; i++)
{
- int regno = REGNO (op);
- struct costs *p = COSTS (costs, COST_INDEX (regno));
- struct costs *q = op_costs[i];
- int *p_costs = p->cost, *q_costs = q->cost;
- cost_classes_t cost_classes_ptr = regno_cost_classes[regno];
- int add_cost;
+ rtx op = recog_data.operand[i];
- /* If the already accounted for the memory "cost" above, don't
- do so again. */
- if (!counted_mem)
+ if (GET_CODE (op) == SUBREG)
+ op = SUBREG_REG (op);
+ if (REG_P (op) && REGNO (op) >= FIRST_PSEUDO_REGISTER)
{
- add_cost = q->mem_cost;
- if (add_cost > 0 && INT_MAX - add_cost < p->mem_cost)
- p->mem_cost = INT_MAX;
- else
- p->mem_cost += add_cost;
- }
- for (k = cost_classes_ptr->num - 1; k >= 0; k--)
- {
- add_cost = q_costs[k];
- if (add_cost > 0 && INT_MAX - add_cost < p_costs[k])
- p_costs[k] = INT_MAX;
- else
- p_costs[k] += add_cost;
+ int regno = REGNO (op);
+ struct costs *p = COSTS (costs, COST_INDEX (regno));
+ struct costs *q = op_costs[i];
+ int *p_costs = p->cost, *q_costs = q->cost;
+ cost_classes_t cost_classes_ptr = regno_cost_classes[regno];
+ int add_cost;
+
+ /* If the already accounted for the memory "cost" above, don't
+ do so again. */
+ if (!counted_mem)
+ {
+ add_cost = q->mem_cost;
+ if (add_cost > 0 && INT_MAX - add_cost < p->mem_cost)
+ p->mem_cost = INT_MAX;
+ else
+ p->mem_cost += add_cost;
+ }
+ for (k = cost_classes_ptr->num - 1; k >= 0; k--)
+ {
+ add_cost = q_costs[k];
+ if (add_cost > 0 && INT_MAX - add_cost < p_costs[k])
+ p_costs[k] = INT_MAX;
+ else
+ p_costs[k] += add_cost;
+ }
}
}
}
@@ -1677,9 +2608,11 @@
regno_best_class
= (enum reg_class *) ira_allocate (max_reg_num ()
* sizeof (enum reg_class));
+ insn_constraints.release ();
+ insn_selections.release ();
for (i = max_reg_num () - 1; i >= FIRST_PSEUDO_REGISTER; i--)
regno_best_class[i] = NO_REGS;
- if (!resize_reg_info () && allocno_p
+ if (!resize_reg_info () && allocno_p && ! flag_ira_select
&& pseudo_classes_defined_p && flag_expensive_optimizations)
{
ira_allocno_t a;
@@ -1890,7 +2823,7 @@
alt_class = reg_class_subunion[alt_class][rclass];
}
alt_class = ira_allocno_class_translate[alt_class];
- if (best_cost > i_mem_cost
+ if (best_cost > i_mem_cost && (1||cost_classes_ptr->num == 0||! flag_ira_select)
&& ! non_spilled_static_chain_regno_p (i))
regno_aclass[i] = NO_REGS;
else if (!optimize && !targetm.class_likely_spilled_p (best))
@@ -1935,7 +2868,7 @@
}
if (pass == flag_expensive_optimizations)
{
- if (best_cost > i_mem_cost
+ if (best_cost > i_mem_cost && (1||cost_classes_ptr->num == 0||! flag_ira_select)
/* Do not assign NO_REGS to static chain pointer
pseudo when non-local goto is used. */
&& ! non_spilled_static_chain_regno_p (i))
@@ -1953,7 +2886,7 @@
regno_best_class[i] = best;
if (! allocno_p)
{
- pref[i] = (best_cost > i_mem_cost
+ pref[i] = (best_cost > i_mem_cost && (1||cost_classes_ptr->num == 0||! flag_ira_select)
&& ! non_spilled_static_chain_regno_p (i)
? NO_REGS : best);
continue;