https://gcc.gnu.org/g:481222de94ad9eac9ecdb5f3fea7f82d5fda7443

commit r17-4580-g481222de94ad9eac9ecdb5f3fea7f82d5fda7443
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:
---
 gcc/lra-constraints.cc                   | 41 +++++++++++++++++++++++++++++---
 gcc/testsuite/gcc.target/i386/pr127326.c | 17 +++++++++++++
 2 files changed, 55 insertions(+), 3 deletions(-)

diff --git a/gcc/lra-constraints.cc b/gcc/lra-constraints.cc
index 0f9bb3da6df1..72430d517321 100644
--- a/gcc/lra-constraints.cc
+++ b/gcc/lra-constraints.cc
@@ -2776,9 +2776,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
@@ -2858,6 +2860,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;
                  }
@@ -3053,6 +3060,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;
@@ -3205,6 +3227,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 000000000000..ed618cf70928
--- /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