]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
ira: Handle register filters
authorRichard Sandiford <richard.sandiford@arm.com>
Tue, 21 Nov 2023 15:39:10 +0000 (15:39 +0000)
committerRichard Sandiford <richard.sandiford@arm.com>
Tue, 21 Nov 2023 15:39:10 +0000 (15:39 +0000)
This patch makes IRA apply register filters when picking hard registers.
All the new code should be optimised away on targets that don't use
register filters.  On targets that do use them, the new register_filters
bitfield is expected to be only a handful of bits.

Information about register filters is recorded in process_bb_node_lives.
The information isn't really related to liveness, but it's a convenient
point because (a) we've already built the allocno structures and
(b) we've already extracted the insn and preprocessed the constraints.

gcc/
* ira-int.h (ira_allocno): Add a register_filters field.
(ALLOCNO_REGISTER_FILTERS): New macro.
(ALLOCNO_SET_REGISTER_FILTERS): Likewise.
* ira-build.cc (ira_create_allocno): Initialize register_filters.
(create_cap_allocno): Propagate register_filters.
(propagate_allocno_info): Likewise.
(propagate_some_info_from_allocno): Likewise.
* ira-lives.cc (process_register_constraint_filters): New function.
(process_bb_node_lives): Use it to record register filter
information.
* ira-color.cc (assign_hard_reg): Check register filters.
(improve_allocation, fast_allocation): Likewise.

gcc/ira-build.cc
gcc/ira-color.cc
gcc/ira-int.h
gcc/ira-lives.cc

index 93e4603317081b26c9abf2b5e0e20ddc340fb1ff..c715a834f12e010e885d48014d732ac0dbf048a3 100644 (file)
@@ -498,6 +498,7 @@ ira_create_allocno (int regno, bool cap_p,
   ALLOCNO_NREFS (a) = 0;
   ALLOCNO_FREQ (a) = 0;
   ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P (a) = false;
+  ALLOCNO_SET_REGISTER_FILTERS (a, 0);
   ALLOCNO_HARD_REGNO (a) = -1;
   ALLOCNO_CALL_FREQ (a) = 0;
   ALLOCNO_CALLS_CROSSED_NUM (a) = 0;
@@ -902,6 +903,7 @@ create_cap_allocno (ira_allocno_t a)
   ALLOCNO_NREFS (cap) = ALLOCNO_NREFS (a);
   ALLOCNO_FREQ (cap) = ALLOCNO_FREQ (a);
   ALLOCNO_CALL_FREQ (cap) = ALLOCNO_CALL_FREQ (a);
+  ALLOCNO_SET_REGISTER_FILTERS (cap, ALLOCNO_REGISTER_FILTERS (a));
 
   merge_hard_reg_conflicts (a, cap, false);
 
@@ -2064,6 +2066,9 @@ propagate_allocno_info (void)
            ALLOCNO_BAD_SPILL_P (parent_a) = false;
          ALLOCNO_NREFS (parent_a) += ALLOCNO_NREFS (a);
          ALLOCNO_FREQ (parent_a) += ALLOCNO_FREQ (a);
+         ALLOCNO_SET_REGISTER_FILTERS (parent_a,
+                                       ALLOCNO_REGISTER_FILTERS (parent_a)
+                                       | ALLOCNO_REGISTER_FILTERS (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.
@@ -2465,6 +2470,9 @@ propagate_some_info_from_allocno (ira_allocno_t a, ira_allocno_t from_a)
   ALLOCNO_CROSSED_CALLS_ABIS (a) |= ALLOCNO_CROSSED_CALLS_ABIS (from_a);
   ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (a)
     |= ALLOCNO_CROSSED_CALLS_CLOBBERED_REGS (from_a);
+  ALLOCNO_SET_REGISTER_FILTERS (a,
+                               ALLOCNO_REGISTER_FILTERS (from_a)
+                               | ALLOCNO_REGISTER_FILTERS (a));
 
   ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (a)
     += ALLOCNO_EXCESS_PRESSURE_POINTS_NUM (from_a);
index f2e8ea34152bf7d1505eb723ebf672c2aac7ea67..214a4f16d3c28bbd7ab8002e869eb927ec989408 100644 (file)
@@ -2163,6 +2163,9 @@ assign_hard_reg (ira_allocno_t a, bool retry_p)
       if (! check_hard_reg_p (a, hard_regno,
                              conflicting_regs, profitable_hard_regs))
        continue;
+      if (NUM_REGISTER_FILTERS
+         && !test_register_filters (ALLOCNO_REGISTER_FILTERS (a), hard_regno))
+       continue;
       cost = costs[i];
       full_cost = full_costs[i];
       if (!HONOR_REG_ALLOC_ORDER)
@@ -3205,6 +3208,9 @@ improve_allocation (void)
          if (! check_hard_reg_p (a, hregno,
                                  conflicting_regs, profitable_hard_regs))
            continue;
+         if (NUM_REGISTER_FILTERS
+             && !test_register_filters (ALLOCNO_REGISTER_FILTERS (a), hregno))
+           continue;
          ira_assert (ira_class_hard_reg_index[aclass][hregno] == j);
          k = allocno_costs == NULL ? 0 : j;
          costs[hregno] = (allocno_costs == NULL
@@ -5275,6 +5281,10 @@ fast_allocation (void)
              || (TEST_HARD_REG_BIT
                  (ira_prohibited_class_mode_regs[aclass][mode], hard_regno)))
            continue;
+         if (NUM_REGISTER_FILTERS
+             && !test_register_filters (ALLOCNO_REGISTER_FILTERS (a),
+                                        hard_regno))
+           continue;
          if (costs == NULL)
            {
              best_hard_regno = hard_regno;
index 0685e1f4e8df3c1bc288579decec834b46f06f68..1c3548df4eaa36624190b7a2e8dadd8491a8a0f1 100644 (file)
@@ -328,6 +328,13 @@ struct ira_allocno
 
      This is only ever true for non-cap allocnos.  */
   unsigned int might_conflict_with_parent_p : 1;
+#ifndef NUM_REGISTER_FILTERS
+#error "insn-config.h not included"
+#elif NUM_REGISTER_FILTERS
+  /* The set of register filters applied to the allocno by operand
+     alternatives that accept class ACLASS.  */
+  unsigned int register_filters : NUM_REGISTER_FILTERS;
+#endif
   /* 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
@@ -432,6 +439,13 @@ struct ira_allocno
 #define ALLOCNO_FREQ(A) ((A)->freq)
 #define ALLOCNO_MIGHT_CONFLICT_WITH_PARENT_P(A) \
   ((A)->might_conflict_with_parent_p)
+#if NUM_REGISTER_FILTERS
+#define ALLOCNO_REGISTER_FILTERS(A) (A)->register_filters
+#define ALLOCNO_SET_REGISTER_FILTERS(A, X) ((A)->register_filters = (X))
+#else
+#define ALLOCNO_REGISTER_FILTERS(A) 0
+#define ALLOCNO_SET_REGISTER_FILTERS(A, X) ((void) (A), gcc_assert ((X) == 0))
+#endif
 #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)
index 81af5c06460c29c15caa2aa001b238653462bf9a..63f2314409f6af10709650275ddf104e72d19dc4 100644 (file)
@@ -1066,6 +1066,66 @@ process_single_reg_class_operands (bool in_p, int freq)
     }
 }
 
+/* Go through the operands of the extracted insn looking for operand
+   alternatives that apply a register filter.  Record any such filters
+   in the operand's allocno.  */
+static void
+process_register_constraint_filters ()
+{
+  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))
+       {
+         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);
+             /* The two extremes are easy:
+
+                - We should record the filter if CL matches the
+                  allocno class.
+
+                - We should ignore the filter if CL and the allocno class
+                  are disjoint.  We'll either pick a different alternative
+                  or reload the operand.
+
+                Things are trickier if the classes overlap.  However:
+
+                - If the allocno class includes registers that are not
+                  in CL, some choices of hard register will need a reload
+                  anyway.  It isn't obvious that reloads due to filters
+                  are worse than reloads due to regnos being outside CL.
+
+                - Conversely, if the allocno class is a subset of CL,
+                  any allocation will satisfy the class requirement.
+                  We should try to make sure it satisfies the filter
+                  requirement too.  This is useful if, for example,
+                  an allocno needs to be in "low" registers to satisfy
+                  some uses, and its allocno class is therefore those
+                  low registers, but the allocno is elsewhere allowed
+                  to be in any even-numbered register.  Picking an
+                  even-numbered low register satisfies both types of use.  */
+             if (!ira_class_subset_p[ALLOCNO_CLASS (a)][cl])
+               continue;
+
+             auto filters = alternative_register_filters (op_alt, opno);
+             if (!filters)
+               continue;
+
+             filters |= ALLOCNO_REGISTER_FILTERS (a);
+             ALLOCNO_SET_REGISTER_FILTERS (a, filters);
+           }
+       }
+    }
+}
+
 /* 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.  */
@@ -1378,6 +1438,7 @@ process_bb_node_lives (ira_loop_tree_node_t loop_tree_node)
              }
 
          preferred_alternatives = ira_setup_alts (insn);
+         process_register_constraint_filters ();
          process_single_reg_class_operands (false, freq);
 
          if (call_p)