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

--- Comment #7 from Matt Turner <mattst88 at gmail dot com> ---
I traced this further. The thing that puts the pseudo in memory in the first
place is in IRA, and it only happens on the LRA path. For the testcase from my
last comment:

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

the empty struct is a QImode value in pseudo r70. From -fdump-rtl-ira-details:

    -mno-lra:  r70: preferred GENERAL_REGS, alternative NO_REGS,
                    allocno GENERAL_REGS
               Disposition:  0:r70  l0     9

    -mlra:     r70: preferred NO_REGS, alternative NO_REGS, allocno NO_REGS
                 Spill a0(r70,l0)
               Disposition:  0:r70  l0   mem

IRA's own costs for that allocno are

    a0(r70,l0) costs: GENERAL_REGS:4000,4000 MEM:18000,18000
    Pressure: GENERAL_REGS=3

so it is choosing the 18000 option over the 4000 one, in main(), with no
register pressure.

The chain is as follows. (Line numbers are against 170fcbba4c9.)

1. calculate_equiv_gains () is called only under LRA (ira-costs.cc:2092):

       if (ira_use_lra_p && allocno_p && pass == 1)
         calculate_equiv_gains ();

   Under reload it never runs, equiv_savings stays 0, and r70 keeps its hard
   register. That is the entire -mlra / -mno-lra difference at this point.

2. r70 has REG_EQUIV (const_int 0), so equiv_savings > 0 and i_mem_cost is
   forced to 0 (ira-costs.cc:2186ff for the ira_use_lra_p case). Then at
   ira-costs.cc:2247:

       if (best_cost > i_mem_cost && ! non_spilled_static_chain_regno_p (i))
         regno_aclass[i] = NO_REGS;

   4000 > 0, so NO_REGS -> memory. The reasoning is "MEM is free because we
   will rematerialise the constant".

3. LRA then reloads r70's init insn (it has no hard register), which trips
   contains_reloaded_insn_p (lra-constraints.cc:6030) and invalidates the
   equivalence at lra-constraints.cc:6049:

       ira_reg_equiv[i].defined_p = ira_reg_equiv[i].caller_save_p = false;

   Watched in gdb: at lra_constraints entry r70 has defined_p=true,
   profitable_p=false; by the time curr_insn_transform runs it is
   defined_p=false, profitable_p=true, constant=(const_int 0).  get_equiv
   bails on !defined_p (lra-constraints.cc:573), so the rematerialisation
   that justified the spill never happens.

4. r70 therefore has to live in QImode memory for real. 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 LRA cannot store it and loops:

       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 the same for insn 22, 23, 24 ... until "maximum number of generated
   reload insns per insn achieved (90)".

So IRA spills the pseudo for a benefit that LRA then withdraws.
ira-costs.cc:2191 already says

    /* During LRA constraint pass, some equivalences are invalidated.
       Align IRA with LRA here and do not consider those equivalences since
       otherwise those pseudos are spilled.  */

which is exactly this failure mode, but the guard there only covers the
pic_offset_table_rtx / const-pool / symbol_ref invalidation reasons, not
contains_reloaded_insn_p.

Two experiments, both diagnostic only and both reverted. Disabling just the
"else if (ira_use_lra_p)" equiv branch changes nothing: it falls through to the
generic "else if (equiv_savings > 0)" arm, which also sets i_mem_cost = 0, and
it still ICEs. Disabling calculate_equiv_gains () -- the actual LRA-only
trigger -- makes the testcase above, g++.dg/abi/empty21.C and
g++.dg/abi/mangle37.C all compile clean. Obviously not a fix; it would
pessimise every LRA target.

I think there are two separable problems here.

The IRA one looks like a genuine middle-end issue and may not be alpha
specific: a pseudo is sent to memory (cost 18000) instead of a free register
(cost 4000) on the strength of an equivalence that LRA subsequently
invalidates. On most targets that is only a pessimisation, so it would go
unnoticed; on non-BWX alpha it is fatal because QImode memory is not
representable.

The alpha one does not go away even if the above is fixed: under real register
pressure a QI/HI pseudo will spill on non-BWX anyway and hit the same wall. So
the QI/HI secondary reload path has to work under LRA regardless, and that
still runs into what I described last time -- check_and_process_move
(lra-constraints.cc:1508) invokes reload_out<mode> with the pseudo, while
alpha's expander needs a real MEM to compute an address from, and reload always
passed one.

Two questions, for whoever knows LRA better than I do. Should IRA's equiv-gain
path account for the contains_reloaded_insn_p invalidation too, or is the
mismatch expected and simply harmless everywhere except here? And for the alpha
side, should reload_in<mode>/reload_out<mode> be expected to cope with a pseudo
that has not been given its stack slot yet, or should LRA present memory at
that point?

Happy to test patches; I have a cross + qemu setup that reproduces both this
and PR117184 quickly.

Reply via email to