https://gcc.gnu.org/g:c23f20c10d89af4f74346c23061b489ef87c09db
commit r17-1783-gc23f20c10d89af4f74346c23061b489ef87c09db Author: Robin Dapp <[email protected]> Date: Thu Apr 30 12:07:13 2026 +0200 ira: Add dependent filter handling. 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. Diff: --- gcc/ira-build.cc | 25 ++++++++++++++ gcc/ira-color.cc | 56 +++++++++++++++++++++++++++++- gcc/ira-conflicts.cc | 52 ++++++++++++++++++++++++++-- gcc/ira-int.h | 23 +++++++++++++ gcc/ira-lives.cc | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/ira.cc | 1 + 6 files changed, 251 insertions(+), 3 deletions(-) diff --git a/gcc/ira-build.cc b/gcc/ira-build.cc index bd3f1d36ac82..d8c9da931921 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 b48e3821e6b4..2b389b5aab1c 100644 --- a/gcc/ira-color.cc +++ b/gcc/ira-color.cc @@ -35,12 +35,51 @@ along with GCC; see the file COPYING3. If not see #include "ira-int.h" #include "reload.h" #include "cfgloop.h" +#include "lra.h" /* To prevent soft conflict detection becoming quadratic in the loop depth. Only for very pathological cases, so it hardly 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 >= 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]) diff --git a/gcc/ira-conflicts.cc b/gcc/ira-conflicts.cc index c43b1e72ccc2..b5152ffaecee 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 c14bd3d749cc..898a2d24625a 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) @@ -1081,6 +1101,9 @@ extern void ira_compress_allocno_live_ranges (void); extern void ira_finish_allocno_live_ranges (void); extern void ira_implicitly_set_insn_hard_regs (HARD_REG_SET *, alternative_mask); +extern void ira_add_dependent_filter (ira_allocno_t, int, + machine_mode, ira_allocno_t, + unsigned int, machine_mode); /* ira-conflicts.cc */ extern void ira_debug_conflicts (bool); diff --git a/gcc/ira-lives.cc b/gcc/ira-lives.cc index 031b6412737b..9103b3e4ff29 100644 --- a/gcc/ira-lives.cc +++ b/gcc/ira-lives.cc @@ -1135,6 +1135,102 @@ 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. + This is a linear search and the assumption is that we'll never + have more than a handful of dependent filters. Right now, the + maximum is 32 (see gensupport.cc). */ + 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. */ @@ -1443,6 +1539,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 79d6de7796a5..798a7f01c5da 100644 --- a/gcc/ira.cc +++ b/gcc/ira.cc @@ -5680,6 +5680,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
