/* Code for RTL transformations to satisfy insn constraints.
- Copyright (C) 2010-2019 Free Software Foundation, Inc.
+ Copyright (C) 2010-2020 Free Software Foundation, Inc.
Contributed by Vladimir Makarov <vmakarov@redhat.com>.
This file is part of GCC.
#include "expr.h"
#include "cfgrtl.h"
#include "rtl-error.h"
-#include "params.h"
#include "lra.h"
#include "lra-int.h"
#include "print-rtl.h"
+#include "function-abi.h"
+#include "rtl-iter.h"
/* Value of LRA_CURR_RELOAD_NUM at the beginning of BB of the current
insn. Remember that LRA_CURR_RELOAD_NUM is the number of emitted
CL. Use elimination first if REG is a hard register. If REG is a
reload pseudo created by this constraints pass, assume that it will
be allocated a hard register from its allocno class, but allow that
- class to be narrowed to CL if it is currently a superset of CL.
+ class to be narrowed to CL if it is currently a superset of CL and
+ if either:
+
+ - ALLOW_ALL_RELOAD_CLASS_CHANGES_P is true or
+ - the instruction we're processing is not a reload move.
If NEW_CLASS is nonnull, set *NEW_CLASS to the new allocno class of
REGNO (reg), or NO_REGS if no change in its class was needed. */
static bool
-in_class_p (rtx reg, enum reg_class cl, enum reg_class *new_class)
+in_class_p (rtx reg, enum reg_class cl, enum reg_class *new_class,
+ bool allow_all_reload_class_changes_p = false)
{
enum reg_class rclass, common_class;
machine_mode reg_mode;
typically moves that have many alternatives, and restricting
reload pseudos for one alternative may lead to situations
where other reload pseudos are no longer allocatable. */
- || (INSN_UID (curr_insn) >= new_insn_uid_start
+ || (!allow_all_reload_class_changes_p
+ && INSN_UID (curr_insn) >= new_insn_uid_start
&& curr_insn_set != NULL
&& ((OBJECT_P (SET_SRC (curr_insn_set))
&& ! CONSTANT_P (SET_SRC (curr_insn_set)))
*m_index_loc = m_index_reg;
}
-/* Return true if the eliminated form of AD is a legitimate target address. */
+/* Return true if the eliminated form of AD is a legitimate target address.
+ If OP is a MEM, AD is the address within OP, otherwise OP should be
+ ignored. CONSTRAINT is one constraint that the operand may need
+ to meet. */
static bool
-valid_address_p (struct address_info *ad)
+valid_address_p (rtx op, struct address_info *ad,
+ enum constraint_num constraint)
{
address_eliminator eliminator (ad);
+
+ /* Allow a memory OP if it matches CONSTRAINT, even if CONSTRAINT is more
+ forgiving than "m".
+ Need to extract memory from op for special memory constraint,
+ i.e. bcst_mem_operand in i386 backend. */
+ if (MEM_P (extract_mem_from_operand (op))
+ && (insn_extra_memory_constraint (constraint)
+ || insn_extra_special_memory_constraint (constraint))
+ && constraint_satisfied_p (op, constraint))
+ return true;
+
return valid_address_p (ad->mode, *ad->outer, ad->as);
}
+/* For special_memory_operand, it could be false for MEM_P (op),
+ i.e. bcst_mem_operand in i386 backend.
+ Extract and return real memory operand or op. */
+rtx
+extract_mem_from_operand (rtx op)
+{
+ for (rtx x = op;; x = XEXP (x, 0))
+ {
+ if (MEM_P (x))
+ return x;
+ if (GET_RTX_LENGTH (GET_CODE (x)) != 1
+ || GET_RTX_FORMAT (GET_CODE (x))[0] != 'e')
+ break;
+ }
+ return op;
+}
+
/* Return true if the eliminated form of memory reference OP satisfies
extra (special) memory constraint CONSTRAINT. */
static bool
satisfies_memory_constraint_p (rtx op, enum constraint_num constraint)
{
struct address_info ad;
+ rtx mem = extract_mem_from_operand (op);
+ if (!MEM_P (mem))
+ return false;
- decompose_mem_address (&ad, op);
+ decompose_mem_address (&ad, mem);
address_eliminator eliminator (&ad);
return constraint_satisfied_p (op, constraint);
}
curr_insn_input_reloads_num = 0;
}
-/* Create a new pseudo using MODE, RCLASS, ORIGINAL or reuse already
- created input reload pseudo (only if TYPE is not OP_OUT). Don't
- reuse pseudo if IN_SUBREG_P is true and the reused pseudo should be
- wrapped up in SUBREG. The result pseudo is returned through
- RESULT_REG. Return TRUE if we created a new pseudo, FALSE if we
- reused the already created input reload pseudo. Use TITLE to
- describe new registers for debug purposes. */
+/* The canonical form of an rtx inside a MEM is not necessarily the same as the
+ canonical form of the rtx outside the MEM. Fix this up in the case that
+ we're reloading an address (and therefore pulling it outside a MEM). */
+static rtx
+canonicalize_reload_addr (rtx addr)
+{
+ subrtx_var_iterator::array_type array;
+ FOR_EACH_SUBRTX_VAR (iter, array, addr, NONCONST)
+ {
+ rtx x = *iter;
+ if (GET_CODE (x) == MULT && CONST_INT_P (XEXP (x, 1)))
+ {
+ const HOST_WIDE_INT ci = INTVAL (XEXP (x, 1));
+ const int pwr2 = exact_log2 (ci);
+ if (pwr2 > 0)
+ {
+ /* Rewrite this to use a shift instead, which is canonical when
+ outside of a MEM. */
+ PUT_CODE (x, ASHIFT);
+ XEXP (x, 1) = GEN_INT (pwr2);
+ }
+ }
+ }
+
+ return addr;
+}
+
+/* Create a new pseudo using MODE, RCLASS, ORIGINAL or reuse an existing
+ reload pseudo. Don't reuse an existing reload pseudo if IN_SUBREG_P
+ is true and the reused pseudo should be wrapped up in a SUBREG.
+ The result pseudo is returned through RESULT_REG. Return TRUE if we
+ created a new pseudo, FALSE if we reused an existing reload pseudo.
+ Use TITLE to describe new registers for debug purposes. */
static bool
get_reload_reg (enum op_type type, machine_mode mode, rtx original,
enum reg_class rclass, bool in_subreg_p,
if (type == OP_OUT)
{
+ /* Output reload registers tend to start out with a conservative
+ choice of register class. Usually this is ALL_REGS, although
+ a target might narrow it (for performance reasons) through
+ targetm.preferred_reload_class. It's therefore quite common
+ for a reload instruction to require a more restrictive class
+ than the class that was originally assigned to the reload register.
+
+ In these situations, it's more efficient to refine the choice
+ of register class rather than create a second reload register.
+ This also helps to avoid cycling for registers that are only
+ used by reload instructions. */
+ if (REG_P (original)
+ && (int) REGNO (original) >= new_regno_start
+ && INSN_UID (curr_insn) >= new_insn_uid_start
+ && in_class_p (original, rclass, &new_class, true))
+ {
+ unsigned int regno = REGNO (original);
+ if (lra_dump_file != NULL)
+ {
+ fprintf (lra_dump_file, " Reuse r%d for output ", regno);
+ dump_value_slim (lra_dump_file, original, 1);
+ }
+ if (new_class != lra_get_allocno_class (regno))
+ lra_change_class (regno, new_class, ", change to", false);
+ if (lra_dump_file != NULL)
+ fprintf (lra_dump_file, "\n");
+ *result_reg = original;
+ return false;
+ }
*result_reg
= lra_create_new_reg_with_unique_value (mode, original, rclass, title);
return true;
curr_insn_input_reloads[curr_insn_input_reloads_num].match_p = true;
curr_insn_input_reloads[curr_insn_input_reloads_num++].reg = new_in_reg;
for (i = 0; (in = ins[i]) >= 0; i++)
- {
- lra_assert
- (GET_MODE (*curr_id->operand_loc[in]) == VOIDmode
- || GET_MODE (new_in_reg) == GET_MODE (*curr_id->operand_loc[in]));
+ if (GET_MODE (*curr_id->operand_loc[in]) == VOIDmode
+ || GET_MODE (new_in_reg) == GET_MODE (*curr_id->operand_loc[in]))
*curr_id->operand_loc[in] = new_in_reg;
- }
+ else
+ {
+ lra_assert
+ (GET_MODE (new_out_reg) == GET_MODE (*curr_id->operand_loc[in]));
+ *curr_id->operand_loc[in] = new_out_reg;
+ }
lra_update_dups (curr_id, ins);
if (out < 0)
return;
narrow_reload_pseudo_class (out_rtx, goal_class);
if (find_reg_note (curr_insn, REG_UNUSED, out_rtx) == NULL_RTX)
{
+ reg = SUBREG_P (out_rtx) ? SUBREG_REG (out_rtx) : out_rtx;
start_sequence ();
+ /* If we had strict_low_part, use it also in reload to keep other
+ parts unchanged but do it only for regs as strict_low_part
+ has no sense for memory and probably there is no insn pattern
+ to match the reload insn in memory case. */
+ if (out >= 0 && curr_static_id->operand[out].strict_low && REG_P (reg))
+ out_rtx = gen_rtx_STRICT_LOW_PART (VOIDmode, out_rtx);
lra_emit_move (out_rtx, copy_rtx (new_out_reg));
emit_insn (*after);
*after = get_insns ();
static bool
simplify_operand_subreg (int nop, machine_mode reg_mode)
{
- int hard_regno;
+ int hard_regno, inner_hard_regno;
rtx_insn *before, *after;
machine_mode mode, innermode;
rtx reg, new_reg;
alter_subreg (curr_id->operand_loc[nop], false);
rtx subst = *curr_id->operand_loc[nop];
lra_assert (MEM_P (subst));
-
+ const bool addr_is_valid = valid_address_p (GET_MODE (subst),
+ XEXP (subst, 0),
+ MEM_ADDR_SPACE (subst));
if (!addr_was_valid
- || valid_address_p (GET_MODE (subst), XEXP (subst, 0),
- MEM_ADDR_SPACE (subst))
+ || addr_is_valid
|| ((get_constraint_type (lookup_constraint
(curr_static_id->operand[nop].constraint))
!= CT_SPECIAL_MEMORY)
data into a register when the inner is narrower than outer or
missing important data from memory when the inner is wider than
outer. This rule only applies to modes that are no wider than
- a word. */
- if (!(maybe_ne (GET_MODE_PRECISION (mode),
- GET_MODE_PRECISION (innermode))
- && known_le (GET_MODE_SIZE (mode), UNITS_PER_WORD)
- && known_le (GET_MODE_SIZE (innermode), UNITS_PER_WORD)
- && WORD_REGISTER_OPERATIONS)
+ a word.
+
+ If valid memory becomes invalid after subreg elimination
+ and address might be different we still have to reload
+ memory.
+ */
+ if ((! addr_was_valid
+ || addr_is_valid
+ || known_eq (GET_MODE_SIZE (mode), GET_MODE_SIZE (innermode)))
+ && !(maybe_ne (GET_MODE_PRECISION (mode),
+ GET_MODE_PRECISION (innermode))
+ && known_le (GET_MODE_SIZE (mode), UNITS_PER_WORD)
+ && known_le (GET_MODE_SIZE (innermode), UNITS_PER_WORD)
+ && WORD_REGISTER_OPERATIONS)
&& (!(MEM_ALIGN (subst) < GET_MODE_ALIGNMENT (mode)
&& targetm.slow_unaligned_access (mode, MEM_ALIGN (subst)))
|| (MEM_ALIGN (reg) < GET_MODE_ALIGNMENT (innermode)
enum reg_class rclass
= (enum reg_class) targetm.preferred_reload_class (reg, ALL_REGS);
if (get_reload_reg (curr_static_id->operand[nop].type, innermode,
- reg, rclass, TRUE, "slow mem", &new_reg))
+ reg, rclass, TRUE, "slow/invalid mem", &new_reg))
{
bool insert_before, insert_after;
bitmap_set_bit (&lra_subreg_reload_pseudos, REGNO (new_reg));
rclass
= (enum reg_class) targetm.preferred_reload_class (reg, ALL_REGS);
if (get_reload_reg (curr_static_id->operand[nop].type, mode, reg,
- rclass, TRUE, "slow mem", &new_reg))
+ rclass, TRUE, "slow/invalid mem", &new_reg))
{
bool insert_before, insert_after;
bitmap_set_bit (&lra_subreg_reload_pseudos, REGNO (new_reg));
}
*curr_id->operand_loc[nop] = new_reg;
lra_process_new_insns (curr_insn, before, after,
- "Inserting slow mem reload");
+ "Inserting slow/invalid mem reload");
return true;
}
for the new uses. */
else if (REG_P (reg)
&& REGNO (reg) >= FIRST_PSEUDO_REGISTER
- && (hard_regno = lra_get_regno_hard_regno (REGNO (reg))) >= 0
- && (hard_regno_nregs (hard_regno, innermode)
- < hard_regno_nregs (hard_regno, mode))
- && (regclass = lra_get_allocno_class (REGNO (reg)))
- && (type != OP_IN
- || !in_hard_reg_set_p (reg_class_contents[regclass],
- mode, hard_regno)
- || overlaps_hard_reg_set_p (lra_no_alloc_regs,
- mode, hard_regno)))
+ && paradoxical_subreg_p (operand)
+ && (inner_hard_regno = lra_get_regno_hard_regno (REGNO (reg))) >= 0
+ && ((hard_regno
+ = simplify_subreg_regno (inner_hard_regno, innermode,
+ SUBREG_BYTE (operand), mode)) < 0
+ || ((hard_regno_nregs (inner_hard_regno, innermode)
+ < hard_regno_nregs (hard_regno, mode))
+ && (regclass = lra_get_allocno_class (REGNO (reg)))
+ && (type != OP_IN
+ || !in_hard_reg_set_p (reg_class_contents[regclass],
+ mode, hard_regno)
+ || overlaps_hard_reg_set_p (lra_no_alloc_regs,
+ mode, hard_regno)))))
{
/* The class will be defined later in curr_insn_transform. */
enum reg_class rclass
return false;
code = GET_CODE (x);
mode = GET_MODE (x);
+
if (code == SUBREG)
{
+ /* For all SUBREGs we want to check whether the full multi-register
+ overlaps the set. For normal SUBREGs this means 'get_hard_regno' of
+ the inner register, for paradoxical SUBREGs this means the
+ 'get_hard_regno' of the full SUBREG and for complete SUBREGs either is
+ fine. Use the wider mode for all cases. */
+ rtx subreg = SUBREG_REG (x);
mode = wider_subreg_mode (x);
- x = SUBREG_REG (x);
- code = GET_CODE (x);
+ if (mode == GET_MODE (subreg))
+ {
+ x = subreg;
+ code = GET_CODE (x);
+ }
}
- if (REG_P (x))
+ if (REG_P (x) || SUBREG_P (x))
{
x_hard_regno = get_hard_regno (x, true);
return (x_hard_regno >= 0
HARD_REG_SET temp;
lra_assert (hard_reg_set_subset_p (reg_class_contents[rclass], set));
- COPY_HARD_REG_SET (temp, set);
- AND_COMPL_HARD_REG_SET (temp, lra_no_alloc_regs);
+ temp = set & ~lra_no_alloc_regs;
return (hard_reg_set_subset_p
(temp, ira_prohibited_class_mode_regs[rclass][mode]));
}
alternative. */
static unsigned int curr_small_class_check = 0;
-/* Update number of used inputs of class OP_CLASS for operand NOP.
- Return true if we have more such class operands than the number of
- available regs. */
+/* Update number of used inputs of class OP_CLASS for operand NOP
+ of alternative NALT. Return true if we have more such class operands
+ than the number of available regs. */
static bool
-update_and_check_small_class_inputs (int nop, enum reg_class op_class)
+update_and_check_small_class_inputs (int nop, int nalt,
+ enum reg_class op_class)
{
static unsigned int small_class_check[LIM_REG_CLASSES];
static int small_class_input_nums[LIM_REG_CLASSES];
&& hard_reg_set_intersect_p (reg_class_contents[op_class],
ira_no_alloc_regs)
&& (curr_static_id->operand[nop].type != OP_OUT
- || curr_static_id->operand[nop].early_clobber))
+ || TEST_BIT (curr_static_id->operand[nop].early_clobber_alts, nalt)))
{
if (small_class_check[op_class] == curr_small_class_check)
small_class_input_nums[op_class]++;
/* We should reject matching of an early
clobber operand if the matching operand is
not dying in the insn. */
- if (! curr_static_id->operand[m].early_clobber
+ if (!TEST_BIT (curr_static_id->operand[m]
+ .early_clobber_alts, nalt)
|| operand_reg[nop] == NULL_RTX
|| (find_regno_note (curr_insn, REG_DEAD,
REGNO (op))
}
else
{
+ /* If the operands do not match and one
+ operand is INOUT, we can not match them.
+ Try other possibilities, e.g. other
+ alternatives or commutative operand
+ exchange. */
+ if (curr_static_id->operand[nop].type == OP_INOUT
+ || curr_static_id->operand[m].type == OP_INOUT)
+ break;
/* Operands don't match. If the operands are
- different user defined explicit hard registers,
- then we cannot make them match. */
+ different user defined explicit hard
+ registers, then we cannot make them match
+ when one is early clobber operand. */
if ((REG_P (*curr_id->operand_loc[nop])
|| SUBREG_P (*curr_id->operand_loc[nop]))
&& (REG_P (*curr_id->operand_loc[m])
&& REG_P (m_reg)
&& HARD_REGISTER_P (m_reg)
&& REG_USERVAR_P (m_reg))
- break;
+ {
+ int i;
+
+ for (i = 0; i < early_clobbered_regs_num; i++)
+ if (m == early_clobbered_nops[i])
+ break;
+ if (i < early_clobbered_regs_num
+ || early_clobber_p)
+ break;
+ }
}
-
/* Both operands must allow a reload register,
otherwise we cannot make them match. */
if (curr_alt[m] == NO_REGS)
it results in less hard regs required for
the insn than a non-matching earlyclobber
alternative. */
- if (curr_static_id->operand[m].early_clobber)
+ if (TEST_BIT (curr_static_id->operand[m]
+ .early_clobber_alts, nalt))
{
if (lra_dump_file != NULL)
fprintf
reloads. */
badop = false;
this_alternative = curr_alt[m];
- COPY_HARD_REG_SET (this_alternative_set, curr_alt_set[m]);
+ this_alternative_set = curr_alt_set[m];
winreg = this_alternative != NO_REGS;
break;
}
break;
case CT_SPECIAL_MEMORY:
- if (MEM_P (op)
- && satisfies_memory_constraint_p (op, cn))
+ if (satisfies_memory_constraint_p (op, cn))
win = true;
else if (spilled_pseudo_p (op))
win = true;
break;
reg:
+ if (mode == BLKmode)
+ break;
this_alternative = reg_class_subunion[this_alternative][cl];
- IOR_HARD_REG_SET (this_alternative_set,
- reg_class_contents[cl]);
+ this_alternative_set |= reg_class_contents[cl];
if (costly_p)
{
this_costly_alternative
= reg_class_subunion[this_costly_alternative][cl];
- IOR_HARD_REG_SET (this_costly_alternative_set,
- reg_class_contents[cl]);
+ this_costly_alternative_set |= reg_class_contents[cl];
}
- if (mode == BLKmode)
- break;
winreg = true;
if (REG_P (op))
{
while ((p += len), c);
scratch_p = (operand_reg[nop] != NULL_RTX
- && lra_former_scratch_p (REGNO (operand_reg[nop])));
+ && ira_former_scratch_p (REGNO (operand_reg[nop])));
/* Record which operands fit this alternative. */
if (win)
{
if (this_alternative != NO_REGS)
{
- HARD_REG_SET available_regs;
-
- COPY_HARD_REG_SET (available_regs,
- reg_class_contents[this_alternative]);
- AND_COMPL_HARD_REG_SET
- (available_regs,
- ira_prohibited_class_mode_regs[this_alternative][mode]);
- AND_COMPL_HARD_REG_SET (available_regs, lra_no_alloc_regs);
+ HARD_REG_SET available_regs
+ = (reg_class_contents[this_alternative]
+ & ~((ira_prohibited_class_mode_regs
+ [this_alternative][mode])
+ | lra_no_alloc_regs));
if (hard_reg_set_empty_p (available_regs))
{
/* There are no hard regs holding a value of given
if (lra_dump_file != NULL)
fprintf (lra_dump_file,
" alt=%d: reload pseudo for op %d "
- " cannot hold the mode value -- refuse\n",
+ "cannot hold the mode value -- refuse\n",
nalt, nop);
goto fail;
}
&& (targetm.preferred_output_reload_class
(op, this_alternative) == NO_REGS))))
{
- if (lra_dump_file != NULL)
- fprintf (lra_dump_file,
- " %d Non-prefered reload: reject+=%d\n",
- nop, LRA_MAX_REJECT);
- reject += LRA_MAX_REJECT;
+ if (offmemok && REG_P (op))
+ {
+ if (lra_dump_file != NULL)
+ fprintf
+ (lra_dump_file,
+ " %d Spill pseudo into memory: reject+=3\n",
+ nop);
+ reject += 3;
+ }
+ else
+ {
+ if (lra_dump_file != NULL)
+ fprintf
+ (lra_dump_file,
+ " %d Non-prefered reload: reject+=%d\n",
+ nop, LRA_MAX_REJECT);
+ reject += LRA_MAX_REJECT;
+ }
}
if (! (MEM_P (op) && offmemok)
(GET_MODE (op), this_alternative, cl)))))
losers++;
- /* Input reloads can be inherited more often than output
- reloads can be removed, so penalize output
- reloads. */
- if (!REG_P (op) || curr_static_id->operand[nop].type != OP_IN)
- {
- if (lra_dump_file != NULL)
- fprintf
- (lra_dump_file,
- " %d Non input pseudo reload: reject++\n",
- nop);
- reject++;
- }
-
if (MEM_P (op) && offmemok)
addr_losers++;
- else if (curr_static_id->operand[nop].type == OP_INOUT)
+ else
{
- if (lra_dump_file != NULL)
- fprintf
- (lra_dump_file,
- " %d Input/Output reload: reject+=%d\n",
- nop, LRA_LOSER_COST_FACTOR);
- reject += LRA_LOSER_COST_FACTOR;
+ /* Input reloads can be inherited more often than
+ output reloads can be removed, so penalize output
+ reloads. */
+ if (!REG_P (op) || curr_static_id->operand[nop].type != OP_IN)
+ {
+ if (lra_dump_file != NULL)
+ fprintf
+ (lra_dump_file,
+ " %d Non input pseudo reload: reject++\n",
+ nop);
+ reject++;
+ }
+
+ if (curr_static_id->operand[nop].type == OP_INOUT)
+ {
+ if (lra_dump_file != NULL)
+ fprintf
+ (lra_dump_file,
+ " %d Input/Output reload: reject+=%d\n",
+ nop, LRA_LOSER_COST_FACTOR);
+ reject += LRA_LOSER_COST_FACTOR;
+ }
}
}
goto fail;
}
- if (update_and_check_small_class_inputs (nop, this_alternative))
+ if (update_and_check_small_class_inputs (nop, nalt,
+ this_alternative))
{
if (lra_dump_file != NULL)
fprintf (lra_dump_file,
goto fail;
}
curr_alt[nop] = this_alternative;
- COPY_HARD_REG_SET (curr_alt_set[nop], this_alternative_set);
+ curr_alt_set[nop] = this_alternative_set;
curr_alt_win[nop] = this_alternative_win;
curr_alt_match_win[nop] = this_alternative_match_win;
curr_alt_offmemok[nop] = this_alternative_offmemok;
rtx new_reg;
HOST_WIDE_INT scale;
rtx op = *curr_id->operand_loc[nop];
+ rtx mem = extract_mem_from_operand (op);
const char *constraint = curr_static_id->operand[nop].constraint;
enum constraint_num cn = lookup_constraint (constraint);
bool change_p = false;
- if (MEM_P (op)
- && GET_MODE (op) == BLKmode
- && GET_CODE (XEXP (op, 0)) == SCRATCH)
+ if (MEM_P (mem)
+ && GET_MODE (mem) == BLKmode
+ && GET_CODE (XEXP (mem, 0)) == SCRATCH)
return false;
if (insn_extra_address_constraint (cn)
&& curr_static_id->operand[nop].is_address)
decompose_lea_address (&ad, curr_id->operand_loc[nop]);
/* Do not attempt to decompose arbitrary addresses generated by combine
- for asm operands with loose constraints, e.g 'X'. */
- else if (MEM_P (op)
+ for asm operands with loose constraints, e.g 'X'.
+ Need to extract memory from op for special memory constraint,
+ i.e. bcst_mem_operand in i386 backend. */
+ else if (MEM_P (mem)
&& !(INSN_CODE (curr_insn) < 0
&& get_constraint_type (cn) == CT_FIXED_FORM
&& constraint_satisfied_p (op, cn)))
- decompose_mem_address (&ad, op);
+ decompose_mem_address (&ad, mem);
else if (GET_CODE (op) == SUBREG
&& MEM_P (SUBREG_REG (op)))
decompose_mem_address (&ad, SUBREG_REG (op));
All these cases involve a non-autoinc address, so there is no
point revalidating other types. */
- if (ad.autoinc_p || valid_address_p (&ad))
+ if (ad.autoinc_p || valid_address_p (op, &ad, cn))
return change_p;
/* Any index existed before LRA started, so we can assume that the
if (code >= 0)
{
*ad.inner = gen_rtx_LO_SUM (Pmode, new_reg, addr);
- if (! valid_address_p (ad.mode, *ad.outer, ad.as))
+ if (!valid_address_p (op, &ad, cn))
{
/* Try to put lo_sum into register. */
insn = emit_insn (gen_rtx_SET
if (code >= 0)
{
*ad.inner = new_reg;
- if (! valid_address_p (ad.mode, *ad.outer, ad.as))
+ if (!valid_address_p (op, &ad, cn))
{
*ad.inner = addr;
code = -1;
&& CONSTANT_P (XEXP (SET_SRC (set), 1)))
{
*ad.inner = SET_SRC (set);
- if (valid_address_p (ad.mode, *ad.outer, ad.as))
+ if (valid_address_p (op, &ad, cn))
{
*ad.base_term = XEXP (SET_SRC (set), 0);
*ad.disp_term = XEXP (SET_SRC (set), 1);
no_input_reloads_p = no_output_reloads_p = false;
goal_alt_number = -1;
change_p = sec_mem_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 (curr_insn) || CALL_P (curr_insn))
+ /* 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 (CALL_P (curr_insn))
no_output_reloads_p = true;
if (HAVE_cc0 && reg_referenced_p (cc0_rtx, PATTERN (curr_insn)))
error_for_asm (curr_insn,
"inconsistent operand constraints in an %<asm%>");
lra_asm_error_p = true;
- /* Avoid further trouble with this insn. Don't generate use
- pattern here as we could use the insn SP offset. */
- lra_set_insn_deleted (curr_insn);
+ if (! JUMP_P (curr_insn))
+ {
+ /* Avoid further trouble with this insn. Don't generate use
+ pattern here as we could use the insn SP offset. */
+ lra_set_insn_deleted (curr_insn);
+ }
+ else
+ {
+ lra_invalidate_insn_data (curr_insn);
+ ira_nullify_asm_goto (curr_insn);
+ lra_update_insn_regno_info (curr_insn);
+ }
return true;
}
assigment pass and the scratch pseudo will be
spilled. Spilled scratch pseudos are transformed
back to scratches at the LRA end. */
- && lra_former_scratch_operand_p (curr_insn, i)
- && lra_former_scratch_p (REGNO (op)))
+ && ira_former_scratch_operand_p (curr_insn, i)
+ && ira_former_scratch_p (REGNO (op)))
{
int regno = REGNO (op);
lra_change_class (regno, NO_REGS, " Change to", true);
&& goal_alt[i] != NO_REGS && REG_P (op)
&& (regno = REGNO (op)) >= FIRST_PSEUDO_REGISTER
&& regno < new_regno_start
- && ! lra_former_scratch_p (regno)
+ && ! ira_former_scratch_p (regno)
&& reg_renumber[regno] < 0
/* Check that the optional reload pseudo will be able to
hold given mode value. */
|| MEM_P (SET_DEST (curr_insn_set))
|| GET_CODE (SET_DEST (curr_insn_set)) == SUBREG))))
optional_p = true;
+ else if (goal_alt_matched[i][0] != -1
+ && curr_static_id->operand[i].type == OP_OUT
+ && (curr_static_id->operand_alternative
+ [goal_alt_number * n_operands + i].earlyclobber)
+ && REG_P (op))
+ {
+ for (j = 0; goal_alt_matched[i][j] != -1; j++)
+ {
+ rtx op2 = *curr_id->operand_loc[goal_alt_matched[i][j]];
+
+ if (REG_P (op2) && REGNO (op) != REGNO (op2))
+ break;
+ }
+ if (goal_alt_matched[i][j] != -1)
+ {
+ /* Generate reloads for different output and matched
+ input registers. This is the easiest way to avoid
+ creation of non-existing register conflicts in
+ lra-lives.c. */
+ match_reload (i, goal_alt_matched[i], outputs, goal_alt[i], &before,
+ &after, TRUE);
+ outputs[n_outputs++] = i;
+ outputs[n_outputs] = -1;
+ }
+ continue;
+ }
else
continue;
}
{
rtx addr = *loc;
enum rtx_code code = GET_CODE (addr);
-
+ bool align_p = false;
+
if (code == AND && CONST_INT_P (XEXP (addr, 1)))
- /* (and ... (const_int -X)) is used to align to X bytes. */
- addr = XEXP (*loc, 0);
+ {
+ /* (and ... (const_int -X)) is used to align to X bytes. */
+ align_p = true;
+ addr = XEXP (*loc, 0);
+ }
+ else
+ addr = canonicalize_reload_addr (addr);
+
lra_emit_move (new_reg, addr);
- if (addr != *loc)
+ if (align_p)
emit_move_insn (new_reg, gen_rtx_AND (GET_MODE (new_reg), new_reg, XEXP (*loc, 1)));
}
before = get_insns ();
regno = lra_get_regno_hard_regno (regno);
if (regno < 0)
return false;
- COMPL_HARD_REG_SET (alloc_regs, lra_no_alloc_regs);
+ alloc_regs = ~lra_no_alloc_regs;
return overlaps_hard_reg_set_p (alloc_regs, GET_MODE (x), regno);
}
else
/* The current iteration number of this LRA pass. */
int lra_constraint_iter;
-/* True if we substituted equiv which needs checking register
- allocation correctness because the equivalent value contains
- allocatable hard registers or when we restore multi-register
- pseudo. */
-bool lra_risky_transformations_p;
+/* True if we should during assignment sub-pass check assignment
+ correctness for all pseudos and spill some of them to correct
+ conflicts. It can be necessary when we substitute equiv which
+ needs checking register allocation correctness because the
+ equivalent value contains allocatable hard registers, or when we
+ restore multi-register pseudo, or when we change the insn code and
+ its operand became INOUT operand when it was IN one before. */
+bool check_and_force_assignment_correctness_p;
/* Return true if REGNO is referenced in more than one block. */
static bool
if (regno < FIRST_PSEUDO_REGISTER)
return false;
- EXECUTE_IF_SET_IN_BITMAP (&lra_reg_info[regno].insn_bitmap, 0, uid, bi)
- if (bb == NULL)
- bb = BLOCK_FOR_INSN (lra_insn_recog_data[uid]->insn);
- else if (BLOCK_FOR_INSN (lra_insn_recog_data[uid]->insn) != bb)
- return true;
- return false;
+ EXECUTE_IF_SET_IN_BITMAP (&lra_reg_info[regno].insn_bitmap, 0, uid, bi)
+ if (bb == NULL)
+ bb = BLOCK_FOR_INSN (lra_insn_recog_data[uid]->insn);
+ else if (BLOCK_FOR_INSN (lra_insn_recog_data[uid]->insn) != bb)
+ return true;
+ return false;
}
/* Return true if LIST contains a deleted insn. */
changed_p = false;
if (pic_offset_table_rtx
&& REGNO (pic_offset_table_rtx) >= FIRST_PSEUDO_REGISTER)
- lra_risky_transformations_p = true;
- else
+ check_and_force_assignment_correctness_p = true;
+ else if (first_p)
/* On the first iteration we should check IRA assignment
correctness. In rare cases, the assignments can be wrong as
- early clobbers operands are ignored in IRA. */
- lra_risky_transformations_p = first_p;
+ early clobbers operands are ignored in IRA or usages of
+ paradoxical sub-registers are not taken into account by
+ IRA. */
+ check_and_force_assignment_correctness_p = true;
new_insn_uid_start = get_max_uid ();
new_regno_start = first_p ? lra_constraint_new_regno_start : max_reg_num ();
/* Mark used hard regs for target stack size calulations. */
}
if (new_insns_num > MAX_RELOAD_INSNS_NUMBER)
internal_error
- ("Max. number of generated reload insns per insn is achieved (%d)\n",
+ ("maximum number of generated reload insns per insn achieved (%d)",
MAX_RELOAD_INSNS_NUMBER);
new_insns_num++;
if (DEBUG_INSN_P (curr_insn))
dump_insn_slim (lra_dump_file, curr_insn);
}
if (contains_reg_p (x, true, false))
- lra_risky_transformations_p = true;
+ check_and_force_assignment_correctness_p = true;
lra_set_insn_deleted (curr_insn);
continue;
}
/* Number of calls passed so far in current EBB. */
static int calls_num;
+/* Index ID is the CALLS_NUM associated the last call we saw with
+ ABI identifier ID. */
+static int last_call_for_abi[NUM_ABI_IDS];
+
+/* Which registers have been fully or partially clobbered by a call
+ since they were last used. */
+static HARD_REG_SET full_and_partial_call_clobbers;
+
/* Current reload pseudo check for validity of elements in
USAGE_INSNS. */
static int curr_usage_insns_check;
usage_insns[regno].reloads_num = reloads_num;
usage_insns[regno].calls_num = calls_num;
usage_insns[regno].after_p = after_p;
+ if (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] >= 0)
+ remove_from_hard_reg_set (&full_and_partial_call_clobbers,
+ PSEUDO_REGNO_MODE (regno),
+ reg_renumber[regno]);
}
/* The function is used to form list REGNO usages which consists of
need_for_call_save_p (int regno)
{
lra_assert (regno >= FIRST_PSEUDO_REGISTER && reg_renumber[regno] >= 0);
- return (usage_insns[regno].calls_num < calls_num
- && (overlaps_hard_reg_set_p
- ((flag_ipa_ra &&
- ! hard_reg_set_empty_p (lra_reg_info[regno].actual_call_used_reg_set))
- ? lra_reg_info[regno].actual_call_used_reg_set
- : call_used_reg_set,
- PSEUDO_REGNO_MODE (regno), reg_renumber[regno])
- || (targetm.hard_regno_call_part_clobbered
- (reg_renumber[regno], PSEUDO_REGNO_MODE (regno)))));
+ if (usage_insns[regno].calls_num < calls_num)
+ {
+ unsigned int abis = 0;
+ for (unsigned int i = 0; i < NUM_ABI_IDS; ++i)
+ if (last_call_for_abi[i] > usage_insns[regno].calls_num)
+ abis |= 1 << i;
+ gcc_assert (abis);
+ if (call_clobbered_in_region_p (abis, full_and_partial_call_clobbers,
+ PSEUDO_REGNO_MODE (regno),
+ reg_renumber[regno]))
+ return true;
+ }
+ return false;
}
/* Global registers occurring in the current EBB. */
/* Don't split call clobbered hard regs living through
calls, otherwise we might have a check problem in the
assign sub-pass as in the most cases (exception is a
- situation when lra_risky_transformations_p value is
+ situation when check_and_force_assignment_correctness_p value is
true) the assign pass assumes that all pseudos living
through calls are assigned to call saved hard regs. */
&& (regno >= FIRST_PSEUDO_REGISTER
- || ! TEST_HARD_REG_BIT (call_used_reg_set, regno)
- || usage_insns[regno].calls_num == calls_num)
+ || !TEST_HARD_REG_BIT (full_and_partial_call_clobbers, regno))
/* We need at least 2 reloads to make pseudo splitting
profitable. We should provide hard regno splitting in
any case to solve 1st insn scheduling problem when
sub-register levels, LRA do this on pseudos level right now and
this discrepancy may create allocation conflicts after
splitting. */
- lra_risky_transformations_p = true;
+ check_and_force_assignment_correctness_p = true;
if (lra_dump_file != NULL)
fprintf (lra_dump_file,
" ))))))))))))))))))))))))))))))))))))))))))))))))\n");
enum rtx_code code;
int i, j;
+ if (side_effects_p (x))
+ return false;
+
code = GET_CODE (x);
mode = GET_MODE (x);
if (code == SUBREG)
curr_usage_insns_check++;
clear_invariants ();
reloads_num = calls_num = 0;
+ for (unsigned int i = 0; i < NUM_ABI_IDS; ++i)
+ last_call_for_abi[i] = 0;
+ CLEAR_HARD_REG_SET (full_and_partial_call_clobbers);
bitmap_clear (&check_only_regs);
bitmap_clear (&invalid_invariant_regs);
last_processed_bb = NULL;
CLEAR_HARD_REG_SET (potential_reload_hard_regs);
- COPY_HARD_REG_SET (live_hard_regs, eliminable_regset);
- IOR_HARD_REG_SET (live_hard_regs, lra_no_alloc_regs);
+ live_hard_regs = eliminable_regset | lra_no_alloc_regs;
/* We don't process new insns generated in the loop. */
for (curr_insn = tail; curr_insn != PREV_INSN (head); curr_insn = prev_insn)
{
else
setup_next_usage_insn (src_regno, curr_insn, reloads_num, false);
if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs))
- IOR_HARD_REG_SET (potential_reload_hard_regs,
- reg_class_contents[cl]);
+ potential_reload_hard_regs |= reg_class_contents[cl];
}
else if (src_regno < 0
&& dst_regno >= lra_constraint_new_regno_start
if (process_invariant_for_inheritance (SET_DEST (curr_set), SET_SRC (curr_set)))
change_p = true;
if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs))
- IOR_HARD_REG_SET (potential_reload_hard_regs,
- reg_class_contents[cl]);
+ potential_reload_hard_regs |= reg_class_contents[cl];
}
else if (src_regno >= lra_constraint_new_regno_start
&& dst_regno < lra_constraint_new_regno_start
/* Invalidate. */
usage_insns[dst_regno].check = 0;
if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs))
- IOR_HARD_REG_SET (potential_reload_hard_regs,
- reg_class_contents[cl]);
+ potential_reload_hard_regs |= reg_class_contents[cl];
}
else if (INSN_P (curr_insn))
{
else
add_to_hard_reg_set (&s, PSEUDO_REGNO_MODE (dst_regno),
reg_renumber[dst_regno]);
- AND_COMPL_HARD_REG_SET (live_hard_regs, s);
+ live_hard_regs &= ~s;
+ potential_reload_hard_regs &= ~s;
}
/* We should invalidate potential inheritance or
splitting for the current insn usages to the next
int regno, hard_regno;
calls_num++;
+ function_abi callee_abi = insn_callee_abi (curr_insn);
+ last_call_for_abi[callee_abi.id ()] = calls_num;
+ full_and_partial_call_clobbers
+ |= callee_abi.full_and_partial_reg_clobbers ();
if ((cheap = find_reg_note (curr_insn,
REG_RETURNED, NULL_RTX)) != NULL_RTX
&& ((cheap = XEXP (cheap, 0)), true)
/* If there are pending saves/restores, the
optimization is not worth. */
&& usage_insns[regno].calls_num == calls_num - 1
- && TEST_HARD_REG_BIT (call_used_reg_set, hard_regno))
+ && callee_abi.clobbers_reg_p (GET_MODE (cheap), hard_regno))
{
/* Restore the pseudo from the call result as
REG_RETURNED note says that the pseudo value is
/* We don't need to save/restore of the pseudo from
this call. */
usage_insns[regno].calls_num = calls_num;
+ remove_from_hard_reg_set
+ (&full_and_partial_call_clobbers,
+ GET_MODE (cheap), hard_regno);
bitmap_set_bit (&check_only_regs, regno);
}
}
before_p, curr_insn, max_uid))
{
if (reg->subreg_p)
- lra_risky_transformations_p = true;
+ check_and_force_assignment_correctness_p = true;
change_p = true;
/* Invalidate. */
usage_insns[src_regno].check = 0;
if (ira_class_hard_regs_num[cl] <= max_small_class_regs_num)
reloads_num++;
if (hard_reg_set_subset_p (reg_class_contents[cl], live_hard_regs))
- IOR_HARD_REG_SET (potential_reload_hard_regs,
- reg_class_contents[cl]);
+ potential_reload_hard_regs |= reg_class_contents[cl];
}
}
if (NONDEBUG_INSN_P (curr_insn))
a BB is not greater than the following value, we don't add the BB
to EBB. */
#define EBB_PROBABILITY_CUTOFF \
- ((REG_BR_PROB_BASE * LRA_INHERITANCE_EBB_PROBABILITY_CUTOFF) / 100)
+ ((REG_BR_PROB_BASE * param_lra_inheritance_ebb_probability_cutoff) / 100)
/* Current number of inheritance/split iteration. */
int lra_inheritance_iter;