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.

Reply via email to