The following patch solves

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127326

The patch was successfully bootstrapped and tested on x86-64, aarch64, and ppc64le.

commit 95c692c256e70a3acf99248a9769ecc2998e667a
Author: Vladimir N. Makarov <[email protected]>
Date:   Tue Sep 22 13:42:38 2026 -0400

    [PR 127326][LRA]: Implement fallback to memory for memory and reg constraint
    
    LRA correctly works for 'rm' constraint when register can not hold value of
    given mode.  For 'g' constraint, LRA fails as it correctly considers the constraint
    can be satisfied but tries to use hard register which can not be in given
    mode.  The patch fixes this.
    
    gcc/ChangeLog:
    
            PR rtl-optimization/127326
            * lra-constraints.cc (process_alt_operands): Ignore register if
            constraint describes memory and register which can not hold the
            mode value.
    
    gcc/testsuite/ChangeLog:
    
            PR rtl-optimization/127326
            * gcc.target/i386/pr127326.c: New.

diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc
index 16866033d50..117a4771b2a 100644
--- a/gcc/lra-constraints.cc
+++ b/gcc/lra-constraints.cc
@@ -2775,9 +2775,11 @@ process_alt_operands (int only_alternative)
 				  break;
 			      }
 			  }
-			/* Both operands must allow a reload register,
-			   otherwise we cannot make them match.  */
-			if (curr_alt[m] == NO_REGS)
+			/* Both operands must allow a reload register or the
+			   matched operand accepts memory (e.g. 'g' with a mode
+			   that fits no hard reg), in which case the operands
+			   can be matched in memory.  */
+			if (curr_alt[m] == NO_REGS && !curr_alt_offmemok[m])
 			  break;
 			/* Retroactively mark the operand we had to
 			   match as a loser, if it wasn't already and
@@ -2857,6 +2859,11 @@ process_alt_operands (int only_alternative)
 		    this_alternative_set = curr_alt_set[m];
 		    this_alternative_exclude_start_hard_regs
 			= curr_alt_exclude_start_hard_regs[m];
+		    /* If the matched operand permits memory (e.g. 'g'),
+		       so does this one -- they share the same location.
+		       This lets a matching reload go to memory when no
+		       hard reg of the class can hold the operand mode.  */
+		    offmemok = curr_alt_offmemok[m];
 		    winreg = this_alternative != NO_REGS;
 		    break;
 		  }
@@ -3051,6 +3058,21 @@ process_alt_operands (int only_alternative)
 		       [all_this_alternative][this_alternative]);
 		}
 	      this_alternative_win = true;
+	      /* If reg and memory is acceptable here (e.g. 'g') but no hard
+		 reg of the operand class can hold the operand mode, resolve
+		 the operand to memory.  Mark it so that an operand matching
+		 this one can also fall back to memory, otherwise the matched
+		 operand would try to reload into an impossible hard reg and
+		 LRA would cycle (e.g. TDmode "+g" asm on 32-bit x86).  */
+	      if (offmemok && this_alternative != NO_REGS
+		  && hard_reg_set_subset_p (reg_class_contents[this_alternative],
+					    ira_prohibited_class_mode_regs
+					    [this_alternative][mode]
+					    | lra_no_alloc_regs))
+		{
+		  this_alternative = NO_REGS;
+		  this_alternative_offmemok = true;
+		}
 	      if (class_change_p)
 		{
 		  curr_alt_class_change_p = true;
@@ -3203,6 +3225,19 @@ process_alt_operands (int only_alternative)
 				     nop);
 			  reject += 2;
 			}
+		      else if (!prefer_memory_p)
+			{
+			  /* No hard reg of the operand class can hold the
+			     operand mode, but the constraint may still permit
+			     memory (e.g. 'g').  Prefer memory for the operands
+			     before refusing.  */
+			  prefer_memory_p = true;
+			  if (lra_dump_file != NULL)
+			    fprintf (lra_dump_file,
+				     "            Trying now memory for"
+				     " operands\n");
+			  goto repeat;
+			}
 		      else
 			{
 			  if (lra_dump_file != NULL)
diff --git a/gcc/testsuite/gcc.target/i386/pr127326.c b/gcc/testsuite/gcc.target/i386/pr127326.c
new file mode 100644
index 00000000000..ed618cf7092
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr127326.c
@@ -0,0 +1,17 @@
+/* PR rtl-optimization/127326 */
+/* { dg-do compile { target { ia32 } } } */
+/* { dg-options "-O2" } */
+/* { dg-require-effective-target dfp } */
+
+/* On 32-bit x86 no hard register can hold a TDmode value, so the "+g"
+   asm operand has to be matched in memory.  LRA used to reject this with
+   "inconsistent operand constraints in an 'asm'".  */
+
+_Decimal128
+test (void)
+{
+  union { _Decimal128 d; unsigned long long u[2]; } u;
+  u.d = 1.0DL;
+  __asm ("" : "+g" (u.d)); /* { dg-bogus "inconsistent operand constraints" } */
+  return u.d;
+}

Reply via email to