commit 1f7e5a7b91862b999aab88ee0319052aaf00f0f1
Author: Vladimir N. Makarov <vmakarov@redhat.com>
Date:   Fri Jul 7 09:53:38 2023 -0400

    LRA: Refine reload pseudo class
    
    For given testcase a reload pseudo happened to occur only in reload
    insns created on one constraint sub-pass.  Therefore its initial class
    (ALL_REGS) was not refined and the reload insns were not processed on
    the next constraint sub-passes.  This resulted into the wrong insn.
    
            PR rtl-optimization/110372
    
    gcc/ChangeLog:
    
            * lra-assigns.cc (assign_by_spills): Add reload insns involving
            reload pseudos with non-refined class to be processed on the next
            sub-pass.
            * lra-constraints.cc (enough_allocatable_hard_regs_p): New func.
            (in_class_p): Use it.
            (print_curr_insn_alt): New func.
            (process_alt_operands): Use it.  Improve debug info.
            (curr_insn_transform): Use print_curr_insn_alt.  Refine reload
            pseudo class if it is not refined yet.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/i386/pr110372.c: New.

diff --git a/gcc/lra-assigns.cc b/gcc/lra-assigns.cc
index 73fbef29912..2f95121df06 100644
--- a/gcc/lra-assigns.cc
+++ b/gcc/lra-assigns.cc
@@ -1443,10 +1443,11 @@ assign_by_spills (void)
 		 pass.  Indicate that it is no longer spilled.  */
 	      bitmap_clear_bit (&all_spilled_pseudos, regno);
 	      assign_hard_regno (hard_regno, regno);
-	      if (! reload_p)
-		/* As non-reload pseudo assignment is changed we
-		   should reconsider insns referring for the
-		   pseudo.  */
+	      if (! reload_p || regno_allocno_class_array[regno] == ALL_REGS)
+		/* As non-reload pseudo assignment is changed we should
+		   reconsider insns referring for the pseudo.  Do the same if a
+		   reload pseudo did not refine its class which can happens
+		   when the pseudo occurs only in reload insns.  */
 		bitmap_set_bit (&changed_pseudo_bitmap, regno);
 	    }
 	}
diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc
index 4dc2d70c402..123ff662cbc 100644
--- a/gcc/lra-constraints.cc
+++ b/gcc/lra-constraints.cc
@@ -233,6 +233,34 @@ get_reg_class (int regno)
   return NO_REGS;
 }
 
+/* Return true if REG_CLASS has enough allocatable hard regs to keep value of
+   REG_MODE.  */
+static bool
+enough_allocatable_hard_regs_p (enum reg_class reg_class,
+				enum machine_mode reg_mode)
+{
+  int i, j, hard_regno, class_size, nregs;
+  
+  if (hard_reg_set_subset_p (reg_class_contents[reg_class], lra_no_alloc_regs))
+    return false;
+  class_size = ira_class_hard_regs_num[reg_class];
+  for (i = 0; i < class_size; i++)
+    {
+      hard_regno = ira_class_hard_regs[reg_class][i];
+      nregs = hard_regno_nregs (hard_regno, reg_mode);
+      if (nregs == 1)
+	return true;
+      for (j = 0; j < nregs; j++)
+	if (TEST_HARD_REG_BIT (lra_no_alloc_regs, hard_regno + j)
+	    || ! TEST_HARD_REG_BIT (reg_class_contents[reg_class],
+				    hard_regno + j))
+	  break;
+      if (j >= nregs)
+	return true;
+    }
+  return false;
+}
+
 /* Return true if REG satisfies (or will satisfy) reg class constraint
    CL.  Use elimination first if REG is a hard register.  If REG is a
    reload pseudo created by this constraints pass, assume that it will
@@ -252,7 +280,6 @@ in_class_p (rtx reg, enum reg_class cl, enum reg_class *new_class,
   enum reg_class rclass, common_class;
   machine_mode reg_mode;
   rtx src;
-  int class_size, hard_regno, nregs, i, j;
   int regno = REGNO (reg);
 
   if (new_class != NULL)
@@ -291,26 +318,7 @@ in_class_p (rtx reg, enum reg_class cl, enum reg_class *new_class,
       common_class = ira_reg_class_subset[rclass][cl];
       if (new_class != NULL)
 	*new_class = common_class;
-      if (hard_reg_set_subset_p (reg_class_contents[common_class],
-				 lra_no_alloc_regs))
-	return false;
-      /* Check that there are enough allocatable regs.  */
-      class_size = ira_class_hard_regs_num[common_class];
-      for (i = 0; i < class_size; i++)
-	{
-	  hard_regno = ira_class_hard_regs[common_class][i];
-	  nregs = hard_regno_nregs (hard_regno, reg_mode);
-	  if (nregs == 1)
-	    return true;
-	  for (j = 0; j < nregs; j++)
-	    if (TEST_HARD_REG_BIT (lra_no_alloc_regs, hard_regno + j)
-		|| ! TEST_HARD_REG_BIT (reg_class_contents[common_class],
-					hard_regno + j))
-	      break;
-	  if (j >= nregs)
-	    return true;
-	}
-      return false;
+      return enough_allocatable_hard_regs_p (common_class, reg_mode);
     }
 }
 
@@ -2046,6 +2054,23 @@ update_and_check_small_class_inputs (int nop, int nalt,
   return false;
 }
 
+/* Print operand constraints for alternative ALT_NUMBER of the current
+   insn.  */
+static void
+print_curr_insn_alt (int alt_number)
+{
+  for (int i = 0; i < curr_static_id->n_operands; i++)
+    {
+      const char *p = (curr_static_id->operand_alternative
+		       [alt_number * curr_static_id->n_operands + i].constraint);
+      if (*p == '\0')
+	continue;
+      fprintf (lra_dump_file, "  (%d) ", i);
+      for (; *p != '\0' && *p != ',' && *p != '#'; p++)
+	fputc (*p, lra_dump_file);
+    }
+}
+
 /* Major function to choose the current insn alternative and what
    operands should be reloaded and how.	 If ONLY_ALTERNATIVE is not
    negative we should consider only this alternative.  Return false if
@@ -2145,6 +2170,14 @@ process_alt_operands (int only_alternative)
       if (!TEST_BIT (preferred, nalt))
 	continue;
 
+      if (lra_dump_file != NULL)
+	{
+	  fprintf (lra_dump_file, "         Considering alt=%d of insn %d: ",
+		   nalt, INSN_UID (curr_insn));
+	  print_curr_insn_alt (nalt);
+	  fprintf (lra_dump_file, "\n");
+	}
+      
       bool matching_early_clobber[MAX_RECOG_OPERANDS];
       curr_small_class_check++;
       overall = losers = addr_losers = 0;
@@ -2671,8 +2704,7 @@ process_alt_operands (int only_alternative)
 		{
 		  if (lra_dump_file != NULL)
 		    fprintf (lra_dump_file,
-			     "            alt=%d: Bad operand -- refuse\n",
-			     nalt);
+			     "            Bad operand -- refuse\n");
 		  goto fail;
 		}
 
@@ -2701,8 +2733,7 @@ process_alt_operands (int only_alternative)
 			{
 			  if (lra_dump_file != NULL)
 			    fprintf (lra_dump_file,
-				     "            alt=%d: Wrong mode -- refuse\n",
-				     nalt);
+				     "            Wrong mode -- refuse\n");
 			  goto fail;
 			}
 		    }
@@ -2769,8 +2800,7 @@ process_alt_operands (int only_alternative)
 		      if (lra_dump_file != NULL)
 			fprintf
 			  (lra_dump_file,
-			   "            alt=%d: Strict low subreg reload -- refuse\n",
-			   nalt);
+			   "            Strict low subreg reload -- refuse\n");
 		      goto fail;
 		    }
 		  losers++;
@@ -2832,8 +2862,7 @@ process_alt_operands (int only_alternative)
 		  if (lra_dump_file != NULL)
 		    fprintf
 		      (lra_dump_file,
-		       "            alt=%d: No input/output reload -- refuse\n",
-		       nalt);
+		       "            No input/output reload -- refuse\n");
 		  goto fail;
 		}
 
@@ -2860,9 +2889,9 @@ process_alt_operands (int only_alternative)
 		{
 		  if (lra_dump_file != NULL)
 		    fprintf (lra_dump_file,
-			     "            alt=%d: reload pseudo for op %d "
+			     "            reload pseudo for op %d "
 			     "cannot hold the mode value -- refuse\n",
-			     nalt, nop);
+			     nop);
 		  goto fail;
 		}
 
@@ -2989,7 +3018,7 @@ process_alt_operands (int only_alternative)
 	      
 	      /* If reload requires moving value through secondary
 		 memory, it will need one more insn at least.  */
-	      if (this_alternative != NO_REGS 
+	      if (this_alternative != NO_REGS
 		  && REG_P (op) && (cl = get_reg_class (REGNO (op))) != NO_REGS
 		  && ((curr_static_id->operand[nop].type != OP_OUT
 		       && targetm.secondary_memory_needed (GET_MODE (op), cl,
@@ -3046,8 +3075,8 @@ process_alt_operands (int only_alternative)
             {
               if (lra_dump_file != NULL)
 		fprintf (lra_dump_file,
-			 "            alt=%d,overall=%d,losers=%d -- refuse\n",
-			 nalt, overall, losers);
+			 "            overall=%d,losers=%d -- refuse\n",
+			 overall, losers);
               goto fail;
             }
 
@@ -3056,8 +3085,7 @@ process_alt_operands (int only_alternative)
 	    {
 	      if (lra_dump_file != NULL)
 		fprintf (lra_dump_file,
-			 "            alt=%d, not enough small class regs -- refuse\n",
-			 nalt);
+			 "            not enough small class regs -- refuse\n");
 	      goto fail;
 	    }
 	  curr_alt[nop] = this_alternative;
@@ -3238,8 +3266,8 @@ process_alt_operands (int only_alternative)
 	  overall += LRA_LOSER_COST_FACTOR - 1;
 	}
       if (lra_dump_file != NULL)
-	fprintf (lra_dump_file, "          alt=%d,overall=%d,losers=%d,rld_nregs=%d\n",
-		 nalt, overall, losers, reload_nregs);
+	fprintf (lra_dump_file, "          overall=%d,losers=%d,rld_nregs=%d\n",
+		 overall, losers, reload_nregs);
 
       /* If this alternative can be made to work by reloading, and it
 	 needs less reloading than the others checked so far, record
@@ -4355,18 +4383,9 @@ curr_insn_transform (bool check_only_p)
     {
       const char *p;
 
-      fprintf (lra_dump_file, "	 Choosing alt %d in insn %u:",
+      fprintf (lra_dump_file, "      Choosing alt %d in insn %u:",
 	       goal_alt_number, INSN_UID (curr_insn));
-      for (i = 0; i < n_operands; i++)
-	{
-	  p = (curr_static_id->operand_alternative
-	       [goal_alt_number * n_operands + i].constraint);
-	  if (*p == '\0')
-	    continue;
-	  fprintf (lra_dump_file, "  (%d) ", i);
-	  for (; *p != '\0' && *p != ',' && *p != '#'; p++)
-	    fputc (*p, lra_dump_file);
-	}
+      print_curr_insn_alt (goal_alt_number);
       if (INSN_CODE (curr_insn) >= 0
           && (p = get_insn_name (INSN_CODE (curr_insn))) != NULL)
         fprintf (lra_dump_file, " {%s}", p);
@@ -4564,7 +4583,22 @@ curr_insn_transform (bool check_only_p)
 	      continue;
 	    }
 	  else
-	    continue;
+	    {
+	      enum reg_class rclass, common_class;
+	      
+	      if (REG_P (op) && goal_alt[i] != NO_REGS
+		  && (regno = REGNO (op)) >= new_regno_start
+		  && (rclass = get_reg_class (regno)) == ALL_REGS
+		  && ((common_class = ira_reg_class_subset[rclass][goal_alt[i]])
+		      != NO_REGS)
+		  && common_class != ALL_REGS
+		  && enough_allocatable_hard_regs_p (common_class,
+						     GET_MODE (op)))
+		/* Refine reload pseudo class from chosen alternative
+		   constraint.  */
+		lra_change_class (regno, common_class, "      Change to", true);
+	      continue;
+	    }
 	}
 
       /* Operands that match previous ones have already been handled.  */
@@ -5249,7 +5283,7 @@ lra_constraints (bool first_p)
 		   the equiv.  We could update the equiv insns after
 		   transformations including an equiv insn deletion
 		   but it is not worthy as such cases are extremely
-		   rare.  */ 
+		   rare.  */
 		|| contains_deleted_insn_p (ira_reg_equiv[i].init_insns)
 		/* If it is not a reverse equivalence, we check that a
 		   pseudo in rhs of the init insn is not dying in the
diff --git a/gcc/testsuite/gcc.target/i386/pr110372.c b/gcc/testsuite/gcc.target/i386/pr110372.c
new file mode 100644
index 00000000000..dd7d76a36f3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr110372.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O -mno-sse2" } */
+
+typedef char __attribute__((__vector_size__ (16))) U;
+typedef int __attribute__((__vector_size__ (16))) V;
+
+V v;
+
+U
+foo0 (U u)
+{
+  v *= (V) u & 4;
+  return u;
+}
