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

Reply via email to