From: Robin Dapp <[email protected]>
This adds dependent-filter handling to ira in a similar way register
filters where added. The actual querying of the filter is partially
routed to the lra routines introduced before.
gcc/ChangeLog:
* ira-build.cc (ira_create_allocno): Initialize dependent
filters.
(copy_dependent_filters): New function to copy filters.
(create_cap_allocno): Use new function.
(propagate_allocno_info): Ditto.
(propagate_some_info_from_allocno): Ditto.
* ira-color.cc (ira_dependent_filter): New function that
calls lra_get_dependent_filter.
(assign_hard_reg): Use new function.
(improve_allocation): Ditto.
(fast_allocation): Ditto.
* ira-conflicts.cc (dependent_filter_same_reg_ok_p): New
function to check if a dependent filter allows the same reg.
(can_use_same_reg_p): Use new function.
* ira-int.h (struct ira_dependent_filter): New struct holding
allocno-specific dependent-filter properties.
(ALLOCNO_DEPENDENT_FILTERS): New accessor.
(ira_add_dependent_filter): Declare.
* ira-lives.cc (ira_add_dependent_filter): New function.
(process_dependent_filters): New function.
(process_bb_node_lives): Call new function.
* ira.cc (ira): Reset filters.
---
gcc/ira-build.cc | 25 ++++++++++++
gcc/ira-color.cc | 63 ++++++++++++++++++++++++++++-
gcc/ira-conflicts.cc | 52 +++++++++++++++++++++++-
gcc/ira-int.h | 29 ++++++++++++++
gcc/ira-lives.cc | 94 ++++++++++++++++++++++++++++++++++++++++++++
gcc/ira.cc | 1 +
6 files changed, 261 insertions(+), 3 deletions(-)
diff --git a/gcc/ira-build.cc b/gcc/ira-build.cc
index 68c6d129056..5726ecd9e05 100644
--- a/gcc/ira-build.cc
+++ b/gcc/ira-build.cc
@@ -499,6 +499,7 @@ ira_create_allocno (int regno, bool cap_p,
ALLOCNO_FREQ (a) = 0;
ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P (a) = false;
ALLOCNO_SET_REGISTER_FILTERS (a, 0);
+ ALLOCNO_DEPENDENT_FILTERS (a) = NULL;
ALLOCNO_HARD_REGNO (a) = -1;
ALLOCNO_CALL_FREQ (a) = 0;
ALLOCNO_CALLS_CROSSED_NUM (a) = 0;
@@ -874,6 +875,19 @@ ira_print_expanded_allocno (ira_allocno_t a)
fprintf (ira_dump_file, ")");
}
+/* Copy SRC's dependent-filter list to DST's. */
+
+static void
+copy_dependent_filters (ira_allocno_t dst, ira_allocno_t src)
+{
+ for (auto *filter = ALLOCNO_DEPENDENT_FILTERS (src);
+ filter;
+ filter = filter->next)
+ ira_add_dependent_filter (dst, filter->id, filter->mode,
+ filter->ref_allocno, filter->ref_hard_regno,
+ filter->ref_mode);
+}
+
/* Create and return the cap representing allocno A in the
parent loop. */
static ira_allocno_t
@@ -904,6 +918,8 @@ create_cap_allocno (ira_allocno_t a)
ALLOCNO_FREQ (cap) = ALLOCNO_FREQ (a);
ALLOCNO_CALL_FREQ (cap) = ALLOCNO_CALL_FREQ (a);
ALLOCNO_SET_REGISTER_FILTERS (cap, ALLOCNO_REGISTER_FILTERS (a));
+ ALLOCNO_DEPENDENT_FILTERS (cap) = NULL;
+ copy_dependent_filters (cap, a);
merge_hard_reg_conflicts (a, cap, false);
@@ -1144,6 +1160,13 @@ ira_free_allocno_costs (ira_allocno_t a)
static void
finish_allocno (ira_allocno_t a)
{
+ auto *filter = ALLOCNO_DEPENDENT_FILTERS (a);
+ while (filter)
+ {
+ auto *next = filter->next;
+ ira_free (filter);
+ filter = next;
+ }
ira_free_allocno_costs (a);
allocno_pool.remove (a);
}
@@ -2070,6 +2093,7 @@ propagate_allocno_info (void)
ALLOCNO_SET_REGISTER_FILTERS (parent_a,
ALLOCNO_REGISTER_FILTERS (parent_a)
| ALLOCNO_REGISTER_FILTERS (a));
+ copy_dependent_filters (parent_a, a);
/* If A's allocation can differ from PARENT_A's, we can if necessary
spill PARENT_A on entry to A's loop and restore it afterwards.
@@ -2474,6 +2498,7 @@ propagate_some_info_from_allocno (ira_allocno_t a,
ira_allocno_t from_a)
ALLOCNO_SET_REGISTER_FILTERS (a,
ALLOCNO_REGISTER_FILTERS (from_a)
| ALLOCNO_REGISTER_FILTERS (a));
+ copy_dependent_filters (a, from_a);
ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a)
+= ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (from_a);
diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc
index 953dbd94ad8..9ae1e5e5f83 100644
--- a/gcc/ira-color.cc
+++ b/gcc/ira-color.cc
@@ -41,6 +41,45 @@ along with GCC; see the file COPYING3. If not see
seems worth a --param. */
const int max_soft_conflict_loop_depth = 64;
+/* Return the regset for allocno A that represents all registers
+ allowed by A's dependent filters. */
+
+static HARD_REG_SET
+ira_dependent_filter (ira_allocno_t a)
+{
+ HARD_REG_SET allowed;
+ SET_HARD_REG_SET (allowed);
+
+ for (auto *dep_filter = ALLOCNO_DEPENDENT_FILTERS (a);
+ dep_filter;
+ dep_filter = dep_filter->next)
+ {
+ unsigned int ref_regno;
+ /* Nothing to filter if the referenced allocno didn't get a
+ hardreg. */
+ if (dep_filter->ref_allocno)
+ {
+ int hr = ALLOCNO_HARD_REGNO (dep_filter->ref_allocno);
+ if (hr < 0)
+ continue;
+ ref_regno = (unsigned int) hr;
+ }
+ else
+ {
+ ref_regno = dep_filter->ref_hard_regno;
+ if (ref_regno == INVALID_REGNUM
+ || ref_regno >= FIRST_PSEUDO_REGISTER)
+ continue;
+ }
+ const HARD_REG_SET *filter
+ = lra_get_dependent_filter (dep_filter->id, dep_filter->mode,
+ ref_regno, dep_filter->ref_mode,
+ false);
+ allowed &= *filter;
+ }
+ return allowed;
+}
+
typedef struct allocno_hard_regs *allocno_hard_regs_t;
/* The structure contains information about hard registers can be
@@ -1968,12 +2007,13 @@ spill_soft_conflicts (ira_allocno_t a, bitmap
allocnos_to_spill,
static bool
assign_hard_reg (ira_allocno_t a, bool retry_p)
{
- HARD_REG_SET conflicting_regs[2], profitable_hard_regs;
+ HARD_REG_SET conflicting_regs[2], profitable_hard_regs, dep_allowed;
int i, j, hard_regno, best_hard_regno, class_size;
int cost, mem_cost, min_cost, full_cost, min_full_cost, nwords, word;
int *a_costs;
enum reg_class aclass;
machine_mode mode;
+ bool dep_filter_p;
static int costs[FIRST_PSEUDO_REGISTER], full_costs[FIRST_PSEUDO_REGISTER];
int saved_nregs;
enum reg_class rclass;
@@ -2194,6 +2234,9 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
allocated first (it is usual practice to put them first in
REG_ALLOC_ORDER). */
mode = ALLOCNO_MODE (a);
+ dep_filter_p = NUM_DEPENDENT_FILTERS && ALLOCNO_DEPENDENT_FILTERS (a);
+ if (dep_filter_p)
+ dep_allowed = ira_dependent_filter (a);
for (i = 0; i < class_size; i++)
{
hard_regno = ira_class_hard_regs[aclass][i];
@@ -2208,6 +2251,8 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
if (NUM_REGISTER_FILTERS
&& !test_register_filters (ALLOCNO_REGISTER_FILTERS (a), hard_regno))
continue;
+ if (dep_filter_p && !TEST_HARD_REG_BIT (dep_allowed, hard_regno))
+ continue;
cost = costs[i];
full_cost = full_costs[i];
if (!HONOR_REG_ALLOC_ORDER)
@@ -3266,6 +3311,11 @@ improve_allocation (void)
&profitable_hard_regs);
class_size = ira_class_hard_regs_num[aclass];
mode = ALLOCNO_MODE (a);
+ HARD_REG_SET dep_filter_allowed;
+ bool dep_filter_p
+ = NUM_DEPENDENT_FILTERS && ALLOCNO_DEPENDENT_FILTERS (a);
+ if (dep_filter_p)
+ dep_filter_allowed = ira_dependent_filter (a);
/* Set up cost improvement for usage of each profitable hard
register for allocno A. */
for (j = 0; j < class_size; j++)
@@ -3277,6 +3327,8 @@ improve_allocation (void)
if (NUM_REGISTER_FILTERS
&& !test_register_filters (ALLOCNO_REGISTER_FILTERS (a), hregno))
continue;
+ if (dep_filter_p && !TEST_HARD_REG_BIT (dep_filter_allowed, hregno))
+ continue;
ira_assert (ira_class_hard_reg_index[aclass][hregno] == j);
k = allocno_costs == NULL ? 0 : j;
costs[hregno] = (allocno_costs == NULL
@@ -3371,6 +3423,8 @@ improve_allocation (void)
if (NUM_REGISTER_FILTERS
&& !test_register_filters (ALLOCNO_REGISTER_FILTERS (a), hregno))
continue;
+ if (dep_filter_p && !TEST_HARD_REG_BIT (dep_filter_allowed, hregno))
+ continue;
if (check_hard_reg_p (a, hregno,
conflicting_regs, profitable_hard_regs)
&& min_cost > costs[hregno])
@@ -5319,6 +5373,11 @@ fast_allocation (void)
costs = ALLOCNO_HARD_REG_COSTS (a);
min_cost = INT_MAX;
best_hard_regno = -1;
+ HARD_REG_SET dep_allowed;
+ bool dep_filter_p
+ = NUM_DEPENDENT_FILTERS && ALLOCNO_DEPENDENT_FILTERS (a);
+ if (dep_filter_p)
+ dep_allowed = ira_dependent_filter (a);
for (j = 0; j < class_size; j++)
{
hard_regno = ira_class_hard_regs[aclass][j];
@@ -5335,6 +5394,8 @@ fast_allocation (void)
&& !test_register_filters (ALLOCNO_REGISTER_FILTERS (a),
hard_regno))
continue;
+ if (dep_filter_p && !TEST_HARD_REG_BIT (dep_allowed, hard_regno))
+ continue;
if (costs == NULL)
{
best_hard_regno = hard_regno;
diff --git a/gcc/ira-conflicts.cc b/gcc/ira-conflicts.cc
index c43b1e72ccc..b5152ffaece 100644
--- a/gcc/ira-conflicts.cc
+++ b/gcc/ira-conflicts.cc
@@ -384,6 +384,47 @@ process_regs_for_copy (rtx reg1, rtx reg2, bool
constraint_p, rtx_insn *insn,
return true;
}
+/* Go through all of OP's dependent filters, check if they reference
+ REF, and if so, check for all eligible hard regs in its class
+ whether the filter allows the same regno for both (dependent) operand,
+ as well as referenced operand. Return true if all filters allow that,
+ or false otherwise. */
+
+static bool
+dependent_filter_same_reg_ok_p (const operand_alternative *op_alt,
+ int op, int ref)
+{
+ unsigned mask = alternative_dependent_filters (op_alt, op);
+ if (!mask)
+ return true;
+
+ enum reg_class cl
+ = ira_reg_class_intersect[op_alt[op].cl][op_alt[ref].cl];
+
+ machine_mode mode = recog_data.operand_mode[op];
+ machine_mode ref_mode = recog_data.operand_mode[ref];
+
+ for (int id = 0; id < NUM_DEPENDENT_FILTERS; ++id)
+ {
+ if (!(mask & (1U << id)))
+ continue;
+ if (get_dependent_filter_ref (id) != ref)
+ continue;
+
+ bool ok = false;
+ for (int i = 0; i < ira_class_hard_regs_num[cl]; ++i)
+ {
+ unsigned regno = ira_class_hard_regs[cl][i];
+ ok = eval_dependent_filter (id, regno, mode, regno, ref_mode);
+ if (ok)
+ break;
+ }
+ if (!ok)
+ return false;
+ }
+ return true;
+}
+
/* Return true if output operand OUTPUT and input operand INPUT of
INSN can use the same register class for at least one alternative.
INSN is already described in recog_data and recog_op_alt. */
@@ -405,8 +446,15 @@ can_use_same_reg_p (rtx_insn *insn, int output, int input)
continue;
if (ira_reg_class_intersect[op_alt[input].cl][op_alt[output].cl]
- != NO_REGS)
- return true;
+ == NO_REGS)
+ continue;
+
+ if (NUM_DEPENDENT_FILTERS
+ && (!dependent_filter_same_reg_ok_p (op_alt, input, output)
+ || !dependent_filter_same_reg_ok_p (op_alt, output, input)))
+ continue;
+
+ return true;
}
return false;
}
diff --git a/gcc/ira-int.h b/gcc/ira-int.h
index 6726a0f4cad..94f201b7760 100644
--- a/gcc/ira-int.h
+++ b/gcc/ira-int.h
@@ -264,6 +264,23 @@ struct ira_object
unsigned int conflict_vec_p : 1;
};
+
+/* Filter that restricts an allocno's hard regnos by referencing
+ another allocno. */
+struct ira_dependent_filter
+{
+ /* Filter ID and MODE of this (dependent) op. */
+ int id;
+ ENUM_BITFIELD (machine_mode) mode : MACHINE_MODE_BITSIZE;
+
+ /* Allocno, hard regno and mode of the referenced op. */
+ ira_allocno_t ref_allocno;
+ unsigned int ref_hard_regno;
+ ENUM_BITFIELD (machine_mode) ref_mode : MACHINE_MODE_BITSIZE;
+
+ struct ira_dependent_filter *next;
+};
+
/* A structure representing an allocno (allocation entity). Allocno
represents a pseudo-register in an allocation region. If
pseudo-register does not live in a region but it lives in the
@@ -335,6 +352,8 @@ struct ira_allocno
alternatives that accept class ACLASS. */
unsigned int register_filters : NUM_REGISTER_FILTERS;
#endif
+ /* List of dependent filters. */
+ struct ira_dependent_filter *dependent_filters;
/* Accumulated usage references of the allocno. Here and below,
word 'accumulated' means info for given region and all nested
subregions. In this case, 'accumulated' means sum of references
@@ -446,6 +465,7 @@ struct ira_allocno
#define ALLOCNO_REGISTER_FILTERS(A) 0
#define ALLOCNO_SET_REGISTER_FILTERS(A, X) ((void) (A), gcc_assert ((X) == 0))
#endif
+#define ALLOCNO_DEPENDENT_FILTERS(A) ((A)->dependent_filters)
#define ALLOCNO_HARD_REGNO(A) ((A)->hard_regno)
#define ALLOCNO_CALL_FREQ(A) ((A)->call_freq)
#define ALLOCNO_CALLS_CROSSED_NUM(A) ((A)->calls_crossed_num)
@@ -1086,6 +1106,15 @@ extern void ira_implicitly_set_insn_hard_regs
(HARD_REG_SET *,
extern void ira_debug_conflicts (bool);
extern void ira_build_conflicts (void);
+/* ira-lives.cc */
+extern void ira_add_dependent_filter (ira_allocno_t, int,
+ machine_mode, ira_allocno_t,
+ unsigned int, machine_mode);
+
+/* lra-constraints.cc */
+extern const HARD_REG_SET *lra_get_dependent_filter
+ (int, machine_mode, unsigned int, machine_mode, bool);
+
/* ira-color.cc */
extern ira_allocno_t ira_soft_conflict (ira_allocno_t, ira_allocno_t);
extern void ira_debug_hard_regs_forest (void);
diff --git a/gcc/ira-lives.cc b/gcc/ira-lives.cc
index a6b3eff1ab8..396dddc30a6 100644
--- a/gcc/ira-lives.cc
+++ b/gcc/ira-lives.cc
@@ -1135,6 +1135,99 @@ process_register_constraint_filters ()
}
}
+/* Append a dependent filter ID with mode MODE, referenced allocno
+ REF_ALLOCNO, hardreg REF_HARD_REGNO, and REF_MODE to allocno A. */
+
+void
+ira_add_dependent_filter (ira_allocno_t a, int id,
+ machine_mode mode, ira_allocno_t ref_allocno,
+ unsigned int ref_hard_regno, machine_mode ref_mode)
+{
+ /* Check if we already have the filter that should be added. */
+ for (auto *filter = ALLOCNO_DEPENDENT_FILTERS (a);
+ filter;
+ filter = filter->next)
+ if (filter->id == id
+ && filter->ref_allocno == ref_allocno
+ && filter->ref_hard_regno == ref_hard_regno
+ && filter->mode == mode
+ && filter->ref_mode == ref_mode)
+ return;
+
+ auto *filter = (ira_dependent_filter *)
+ ira_allocate (sizeof (ira_dependent_filter));
+ filter->id = id;
+ filter->ref_allocno = ref_allocno;
+ filter->ref_hard_regno = ref_hard_regno;
+ filter->mode = mode;
+ filter->ref_mode = ref_mode;
+ filter->next = ALLOCNO_DEPENDENT_FILTERS (a);
+ ALLOCNO_DEPENDENT_FILTERS (a) = filter;
+}
+
+/* Walk the operand alternatives of the current insn. For each
+ operand with a dependent-filter constraint, add one
+ ira_dependent_filter in the appropriate allocno. */
+
+static void
+process_dependent_filters ()
+{
+ if (!NUM_DEPENDENT_FILTERS)
+ return;
+
+ for (int opno = 0; opno < recog_data.n_operands; ++opno)
+ {
+ rtx op = recog_data.operand[opno];
+ if (SUBREG_P (op))
+ op = SUBREG_REG (op);
+ if (!REG_P (op) || HARD_REGISTER_P (op))
+ continue;
+
+ ira_allocno_t a = ira_curr_regno_allocno_map[REGNO (op)];
+
+ for (int alt = 0; alt < recog_data.n_alternatives; alt++)
+ {
+ if (!TEST_BIT (preferred_alternatives, alt))
+ continue;
+
+ auto *op_alt = &recog_op_alt[alt * recog_data.n_operands];
+ auto cl = alternative_class (op_alt, opno);
+ if (!ira_class_subset_p[ALLOCNO_CLASS (a)][cl])
+ continue;
+
+ auto dep_filter_mask = alternative_dependent_filters (op_alt, opno);
+ if (!dep_filter_mask)
+ continue;
+
+ for (int id = 0; id < NUM_DEPENDENT_FILTERS; ++id)
+ {
+ if (!(dep_filter_mask & (1U << id)))
+ continue;
+
+ int ref_opno = get_dependent_filter_ref (id);
+ if (ref_opno < 0 || ref_opno >= recog_data.n_operands)
+ continue;
+ rtx ref_op = recog_data.operand[ref_opno];
+ if (SUBREG_P (ref_op))
+ ref_op = SUBREG_REG (ref_op);
+ if (!REG_P (ref_op))
+ continue;
+
+ ira_allocno_t ref_a = NULL;
+ unsigned int ref_hard_regno = INVALID_REGNUM;
+ if (HARD_REGISTER_P (ref_op))
+ ref_hard_regno = REGNO (ref_op);
+ else
+ ref_a = ira_curr_regno_allocno_map[REGNO (ref_op)];
+
+ ira_add_dependent_filter (a, id, GET_MODE (op),
+ ref_a, ref_hard_regno,
+ GET_MODE (ref_op));
+ }
+ }
+ }
+}
+
/* Look through the CALL_INSN_FUNCTION_USAGE of a call insn INSN, and see if
we find a SET rtx that we can use to deduce that a register can be cheaply
caller-saved. Return such a register, or NULL_RTX if none is found. */
@@ -1442,6 +1535,7 @@ process_bb_node_lives (ira_loop_tree_node_t
loop_tree_node)
preferred_alternatives = ira_setup_alts (insn);
process_register_constraint_filters ();
+ process_dependent_filters ();
process_single_reg_class_operands (false, freq);
if (call_p)
diff --git a/gcc/ira.cc b/gcc/ira.cc
index 85c7ec419b3..e40668c775d 100644
--- a/gcc/ira.cc
+++ b/gcc/ira.cc
@@ -5662,6 +5662,7 @@ ira (FILE *f)
bool output_jump_reload_p = false;
setup_hard_regno_nrefs ();
+ lra_reset_dependent_filters ();
if (ira_use_lra_p)
{
/* First put potential jump output reloads on the output edges
--
2.53.0