[4/5] ira: Handle register filters

Message ID 20231112145229.2924713-5-richard.sandiford@arm.com
State Unresolved
Headers
Series Add support for operand-specific alignment requirements |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Richard Sandiford Nov. 12, 2023, 2:52 p.m. UTC
  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 |  8 +++++++
 gcc/ira-color.cc | 10 ++++++++
 gcc/ira-int.h    | 14 +++++++++++
 gcc/ira-lives.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 93 insertions(+)
  

Patch

diff --git a/gcc/ira-build.cc b/gcc/ira-build.cc
index 93e46033170..c715a834f12 100644
--- a/gcc/ira-build.cc
+++ b/gcc/ira-build.cc
@@ -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);
diff --git a/gcc/ira-color.cc b/gcc/ira-color.cc
index f2e8ea34152..214a4f16d3c 100644
--- a/gcc/ira-color.cc
+++ b/gcc/ira-color.cc
@@ -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;
diff --git a/gcc/ira-int.h b/gcc/ira-int.h
index 0685e1f4e8d..1c3548df4ea 100644
--- a/gcc/ira-int.h
+++ b/gcc/ira-int.h
@@ -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)
diff --git a/gcc/ira-lives.cc b/gcc/ira-lives.cc
index 81af5c06460..63f2314409f 100644
--- a/gcc/ira-lives.cc
+++ b/gcc/ira-lives.cc
@@ -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)