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; +}
