On Fri, Jun 26, 2026 at 01:09:55PM -0400, Vladimir Makarov wrote:
>
> On 6/24/26 3:04 AM, Stefan Schulze Frielinghaus wrote:
> > Hi all,
> >
> > Live range splitting for hard regs which are assigned to some pseudo is
> > already implemented. However, live ranges of hard regs which are live
> > due to hard register assignments are not split so far which may result
> > in unsatisfiable allocations.
>
>
> As I understand, there was a consensus that we should avoid to use hard regs
> wherever it is possible. It is better to create new moves involving hard
> regs and pseudos and use pseudos instead of the hard reg wherever it is
> possible. RA will usually assign the pseudo to the hard reg when it is
> possible effectively removing such moves.
>
> It simplifies work of RA and makes it more stable. It also simplifies and
> improves optimizations before RA.
>
> Hard reg life should be minimal (e.g. from assigning to arg reg to the call)
> or only in places of usage of local register variable or in asm. And
> optimizations should not make life longer. For example, Segher Boessenkool
> worked on preventing hard reg propagation in combiner. Life of arg regs was
> also implemented to be shorter in the function prologues.
>
> That said, unfortunately there is still code where hard reg usage is not
> minimized, especially code originating from machine-dependent parts. And it
> was a problem especially for targets (e.g. x86) with insns requiring
> specific regs. It was a problem for the reload pass and early LRA.
> Compilation failed with message about impossibility to find hard reg for a
> reload pseudo.
>
> But hard reg life split was implemented in LRA long ago (see
> lra-constraints.cc:split_if_necessary). First splitting for pseudos was
> implemented and on its base splitting of hard reg was implemented too.
> Maybe the implementation does not fully and optimally solve the problem but
> I did not see PRs about this for a long time.
>
> Sorry, I don't think the proposed work is necessary. Even if we could
> implement splitting more optimally, as I wrote, we should focus on
> minimizing hard reg life instead.
Fair enough. This idea was rather a (last?) straw. That being said it
is unclear to me how advisable it is to use hard register constraints in
a backend without having a fallback for the worst case (however I haven't
looked into split_if_necessary so far and if it could help for
PR125780). So maybe the takeaway is to wait until [1] becomes the
default which would at least rule out ICEs due to register asm leaving
any other ICE due to hard register usages under our control (backend +
optimizations).
Thanks for considering!
Cheers,
Stefan
>
>
>
> > For example, assume that
> > FIRST_PSEUDO_REGISTER == 100
> >
> > 1: r5=...
> > 2: r101=exp(r100)
> > ...
> > 9: // some user of r5
> >
> > Furthermore, assume that in insn 2 the input operand is constrained to
> > hard register r5 (e.g. via a hard register constraint, a single register
> > constraint as e.g. {a,b,c,d,S,D} from x86_64, or even via a new
> > dependent register filter assuming there was another input operand
> > resulting in r100 being constrained to r5).
> >
> > In those cases we need to temporarily free hard register r5 for insn 2.
> > I have been playing with an implementation where we end up with:
> >
> > 1: r5=...
> > 3: r102=r5 // free r5
> > 4: r103=r100 // assign r5 to input reload r103
> > 2: r104=exp(r103)
> > 6: r101=r104
> > 5: r5=r102 // restore r5
> > ...
> > 9: // some user of r5
> >
> > The implementation is naive in the sense that it is not checked for an
> > optimal restore point for r5, i.e., insns 3 and 5 are always emitted;
> > independent of subsequent insns and whether they run into the same
> > problem w.r.t. r5 which could in the worst case lead to many unnecessary
> > "in-between" reloads.
> >
> > However, before diving into those missed optimizations, I would rather
> > like to fix the place where such reloads should be introduced. At the
> > moment I decided to check during curr_insn_transform() whether the
> > currently selected alternative makes use of a hard register constraint
> > and whether this particular hard register is live. If so, then create a
> > new reload and emit moves before/after. The obvious problem with this
> > is, there is no liveness information at this point in time. We could
> > compute liveness just prior constraint handling (that's what I'm doing
> > in my hack), however, from the liveness information we cannot
> > distinguish why a hard register is live, i.e., whether it is live
> > because we assigned it to a pseudo or whether it is live due to a hard
> > register assignment as e.g. given in insn 1 above. In the former case I
> > think it would be better to rely on the current split mechanism for
> > pseudos.
> >
> > Of course, we might unnecessarily introduce save/restores, if
> > implemented via curr_insn_transfrom(), in case a single alternative
> > contains not only a hard register constraint but also another constraint
> > which is chosen in the end which in turn may result in r5 not being used
> > rendering the save/restore unnecessary. This would be another missed
> > optimization. Again, for the moment, I'm focusing on getting rid of
> > ICEs. Speaking of ICEs my current implementation hack also does not
> > differentiate between spilling to registers vs. memory which may lead to
> > other ICEs in the end in case of high register pressure. I haven't
> > looked into that so far.
> >
> > Before making any substantial changes: any thoughts about this? Maybe
> > there is a better place or mechanism to handle this at all? I'm not
> > expecting that we often hit those cases for most targets. Especially,
> > since on x86_64 with single register constraints I'm not aware of a real
> > world example which fails due to this problem (even though some of them
> > may have been obscured over the years by subsequent fixes in numerous
> > passes). That being said, for a first implementation, I would rather
> > aim for a computational inexpensive solution instead of trying to emit
> > optimal code in each and every scenario which is why I started by simply
> > carrying hard registers around a single instructions. Maybe some sort
> > of light-liveness which considers only hard register assignments, which
> > isn't influenced by pseudo allocation and is therefore to some degree
> > static, would be enough.
> >
> As I know there was a consensus that we should avoid to use hard regs where
> it is possible. Hard reg life should be minimal (e.g. from assigning to arg
> reg to the call). And optimizations should not make life longer. For
> example, Segher Boessenkool worked on preventing hard reg propagation in
> combiner.
>