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

Matt Turner <mattst88 at gmail dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mattst88 at gmail dot com

--- Comment #6 from Matt Turner <mattst88 at gmail dot com> ---
Still reproduces on trunk (17.0.0 20260715). No native alpha needed. An x86_64
-> alpha-linux-gnu cross with a baseline (non-BWX) configuration is enough,
since this is a compile-time ICE.

Minimal testcase, g++ -O2 on a non-BWX alpha:

```
struct A { };
void f (int i, ...);
int main () { f (1, A ()); f (1, A ()); }
```

internal compiler error: maximum number of generated reload insns per insn
achieved (90)
0x12e7200 lra_constraints(bool)
        ../../gcc/lra-constraints.cc:6090

One call compiles fine; two are needed so the value is live across the first
call. An empty struct is a QImode value, so this is a QImode pseudo that has to
go to memory.

The matrix is clean:

ev4  (non-BWX) + LRA     ICE
ev4  (non-BWX) + reload  ok
ev56 (BWX)     + LRA     ok
ev56 (BWX)     + reload  ok

In the wild it shows up across g++.dg (abi/empty21.C, abi/mangle37.C,
abi/mangle-ttp1.C, cpp0x/nondeduced1.C, ...). gcc.c-torture/compile is
unaffected.
What happens

Considering alt=0 of insn 19:   (0) =r  (1) rJ
   0 Operand reload: losers++
Choosing alt 0 in insn 19:  (0) =r  (1) rJ {*movqi}
Creating newreg=74 from oldreg=70, assigning class GENERAL_REGS to r74
   19: r74:QI=0
  Inserting insn reload after:
   22: r70:QI=r74:QI
... and then the same for insn 22, forever.

r70 must live in memory, so it never satisfies =r. On non-BWX *movqi has no
memory alternative at all (the m constraints are gated isa "bwx", and
input_operand rejects a QImode MEM), so the only way to spill a QImode pseudo
is via TARGET_SECONDARY_RELOAD, i.e. the reload_in<mode> / reload_out<mode>
optabs.

alpha_secondary_reload gates the QI/HI branch on any_memory_operand, whose "a
spilled pseudo counts as memory" test is (predicates.md):

if (reload_in_progress && REG_P (op))
  {
    unsigned regno = REGNO (op);
    if (HARD_REGISTER_NUM_P (regno))
      return false;
    else
      return reg_renumber[regno] < 0;
  }

reload_in_progress is false under LRA, so the hook never offers the secondary
reload and LRA loops making register copies.

Extending that test to lra_in_progress (comment #5's "obvious fix") does get
LRA to use the secondary reload, and lands exactly on the second ICE this PR
already mentions:

internal compiler error: in get_unaligned_address, at
config/alpha/alpha.cc:1594

#0  get_unaligned_address (ref=(reg:QI 70 [ D.2447 ]))
#1  gen_reload_outqi (...) at config/alpha/alpha.md:4428
#2  check_and_process_move (...) at lra-constraints.cc:1508
#3  curr_insn_transform (...) at lra-constraints.cc:4790

get_unaligned_address asserts MEM_P (ref), but LRA passes the pseudo. In
check_and_process_move:

if (dregno >= 0)
  reg_renumber [dregno] = -1;                 /* "treat this as memory" */
...
emit_insn (GEN_FCN (sri.icode) (new_reg != NULL_RTX ? new_reg : dest, src,
scratch_reg));

LRA deliberately clears reg_renumber so the target reports the operand as
memory, then invokes the reload optab with the pseudo. Reload always passes a
real MEM there, and alpha's expanders need an address, which a pseudo does not
have yet. LRA has not substituted its stack slot at this point.

There is also a semantic mismatch worth noting: under reload,
reg_renumber[regno] < 0 at the time the hook is called means definitely spilled
(reload assigns first). Under LRA it means no hard register yet, which is true
of many pseudos mid-pass. So any_memory_operand cannot simply switch on
lra_in_progress in general, even though in this one call site LRA is explicitly
asking for that answer.

So the question is which side should move: should alpha's
reload_in<mode>/reload_out<mode> be able to cope with a not-yet-materialized
pseudo, or should LRA present memory here? CC'ing Vlad.

PR117184 (BWX/ev56, m2 bootstrap) turned out to be unrelated and is separately
fixed: LRA sets frame_pointer_needed in setup_can_eliminate without calling
df_set_regs_ever_live for the hard frame pointer the way
ira_setup_eliminable_regset does, so alpha's prologue skipped saving $15 while
the epilogue still restored it from offset 0.

Reply via email to