Spilling to register for a given register class

Hello, for an architecture that doesn’t have a good way to load/store a given register class to memory, is it instead easy to spill/fill from another register class instead?
e.g.

  • storeRegToStack/loadRegFromStack use a pseudo instruction and add virtual register operand is not supported (spill optimization doesn’t seem to like this).
  • AMDGPU backend seems to do sth. similar?

The only way to safely do it seems to use register scavenger to get a temp register, and spill this in eliminateFrameIndex? Is there an obvious way to spill to a register instead? Thanks in advance for any hints

Hi Hendrik,

This question is a recurring one. Check for instance https://lists.llvm.org/pipermail/llvm-dev/2016-February/095436.html for a related conversation.

What this conservation boils down to is that you can achieve that by providing a larger register class that contains the union of the registers that are used with where they can be spilled.

For instance, let say you have a register class GPR that can be spilled into SPR.
You would create three register classes: GPR, SPR and GPR_union_SPR. GPR_union_SPR is never explicitly used in any real instruction (i.e., it does not appear in any MC description), but will give a way to regalloc to relax the constraints on available registers when doing live-range splitting.

Let say you have the following code:
V1(gpr) = op
… // <— too high gpr pressure
= op V1(gpr)

What RA will do is first split the live range of V1:

V1(gpr) = op
V2 = copy V1
… // <— too high gpr pressure
V3(gpr) = copy V2
= op V3(gpr)

Now, V2 does not need to be constrained on gpr anymore and will end up using GPR_union_SPR. So effectively, if there is no GPR available for V2, an SPR will be used and thus V1 will be “spilled” to a GPR.

Disclaimer: The live-range splitting may act up and it may not be as straight forward to apply this solution but the idea remains valid.

AMDGPU does something a bit different IIRC, it basically runs the allocator several times:

  • First they allocate GPRs and spill them into SPR, since SPR registers are not taken into account during this iteration there is no issue for creating new live ranges during spilling for these ones
  • Second they allocate SPRs and they get spilled to memory.

Cheers,
-Quentin

Ok, thanks. Except the question was meant slightly different. Less w.r.t. organizing the register classes, and more w.r.t. implementation. I’ve noticed for instance that when trying to model this straight forwardly by writing a vreg from spills and reading this from fills (not further elaborated here), that the spiller can’t handle vreg def-use pairs: there are assertions making sure a spill does not have any uses , e.g. see InlineSpiller.cpp, allDefsAreDead() calls. This made me wonder if this is supported natively at all.

For the register class union solution, what I was saying is if you have the larger union register class, the spilling happens automatically via live-range splitting (i.e., we don’t hit the inline spiller).

For the iterative regalloc idea (where you may hit the inline spiller problem), you would need to look at AMDGPU. I don’t remember how it works on top of my head.

Ack’d, thanks