https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117185
Matt Turner <mattst88 at gmail dot com> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |bergner at gcc dot gnu.org,
| |vmakarov at redhat dot com
--- Comment #8 from Matt Turner <mattst88 at gmail dot com> ---
CC'ing Vlad and Peter -- this has reached a design question about how LRA
invokes a target's secondary-reload optabs that I can't resolve from the code
alone, and I'd rather build whichever fix you'd accept than guess.
Status: the two prerequisites are now on trunk -- the PR117184 hard-frame-
pointer fix in setup_can_eliminate, and the alpha cannot_copy_insn_p change.
A patch to enable LRA by default on BWX alpha (gated on TARGET_BWX, with
-mno-lra as an escape hatch) is written and posted but not yet in. Non-BWX is
the remaining hard part, and this PR is that part.
Line numbers below are against trunk 170fcbba4c9 (2026-07-15).
Background: what makes non-BWX special
On a non-BWX alpha there are no byte/word memory instructions, so *movqi and
*movhi have no memory alternative at all -- the 'm' alternatives are gated on
isa "bwx". The only way a QImode or HImode pseudo can be spilled is through
TARGET_SECONDARY_RELOAD, i.e. the reload_in<mode> / reload_out<mode> optabs,
which expand to an aligned or unaligned ldl_l/mskbl/insbl sequence. Those
expanders need a MEM to compute an address from (get_unaligned_address /
get_aligned_mem, which assert MEM_P). Under reload they always got one.
The wall under LRA
For an empty-struct-by-value testcase (QImode pseudo r70):
struct A { };
void f (int i, ...);
int main () { f (1, A ()); f (1, A ()); }
-mcpu=ev4: internal compiler error: maximum number of generated reload
insns per insn achieved (90), in lra_constraints
alpha_secondary_reload offers reload_in/out<mode> only when the operand passes
any_memory_operand. That predicate treats a spilled pseudo as memory only
under reload_in_progress, so under LRA it returns false, the hook is never
offered, and LRA loops making register copies of a value that cannot live in a
register.
If I extend any_memory_operand / the secondary-reload gates to lra_in_progress
so the hook *is* offered, I land on the second ICE this PR already mentions:
internal compiler error: in get_unaligned_address, at
config/alpha/alpha.cc
#0 get_unaligned_address (ref=(reg:QI 70))
#1 gen_reload_outqi (...)
#2 check_and_process_move (...) at lra-constraints.cc:1508
The core question
In check_and_process_move, reg_renumber[dregno] is set to -1 (line 1487) and
then the secondary-reload optab is invoked with the operand:
emit_insn (GEN_FCN (sri.icode) (new_reg != NULL_RTX ? new_reg : dest,
src, scratch_reg)); // line 1508
Here src/dest is the *pseudo*, not its spill slot. reload always passed a real
MEM at this point; LRA passes the pseudo, which has no stack slot assigned yet,
so alpha's expander has no address to work with.
Which side is expected to move? Should a target's reload_in<mode> /
reload_out<mode> be able to cope with a not-yet-materialized pseudo, or should
LRA substitute the pseudo's memory before invoking the secondary-reload optab
(as reload effectively did)? If LRA presents the MEM, alpha's existing
expanders work unchanged -- and since these are fresh stack slots they are
naturally aligned, so it even takes the cheap aligned path.
One thing that stops me from just widening the predicate: reg_renumber[regno]
< 0 means "definitely spilled" under reload but "no hard reg yet" under LRA,
which is true of many pseudos mid-pass. So any_memory_operand cannot simply
switch on lra_in_progress in general; it needs to see real memory, not guess
from reg_renumber.
The two directions I see for the alpha side, depending on your answer:
A. Keep the secondary-reload optabs and teach the path to run under LRA,
relying on LRA to present the spill-slot MEM to reload_in/out<mode>.
B. Drop the reload_in/out<mode> route on non-BWX and instead give *movqi /
*movhi real memory alternatives as define_insn_and_split carrying a
(clobber (match_scratch:TI)) that LRA fills, so ordinary reg<->mem
spilling works with no secondary reload.
B is more in the modern LRA spirit but is a larger .md rewrite (it has to
reproduce the aligned/unaligned and TARGET_SAFE_BWA cases the expanders handle
today). A is smaller but hinges entirely on the question above.
A secondary, separable issue (IRA)
The testcase above spills r70 with *zero* register pressure
(GENERAL_REGS:4000 vs MEM:18000, Pressure=3). calculate_equiv_gains (LRA-only,
ira-costs.cc:2092) forces i_mem_cost = 0 for r70 because it has a REG_EQUIV
(const_int 0), so regno_aclass becomes NO_REGS. LRA then reloads r70's init
insn, which trips contains_reloaded_insn_p (lra-constraints.cc:6030) and
invalidates the equivalence, so get_equiv (line 566) bails on !defined_p and
the rematerialization that justified the spill never happens. The guard at
ira-costs.cc:2186 already documents "During LRA constraint pass, some
equivalences are invalidated", but only covers the pic/const-pool/symbol_ref
reasons, not contains_reloaded_insn_p. Should that guard also account for the
contains_reloaded_insn_p case?
I treat this as secondary because a genuine-pressure QI/HI spill will hit the
same wall regardless, so the secondary-reload path has to work under LRA either
way; fixing IRA only removes the unnecessary spills.
Happy to test patches -- I have an x86_64 -> alpha-linux-gnu cross plus
qemu-alpha that reproduces this and PR117184 in minutes, and a native ev7 box
for bootstrap.