This is an archive of the discontinued LLVM Phabricator instance.

Support for function-live-in virtual registers
AbandonedPublic

Authored by sunfish on Nov 17 2015, 9:29 AM.

Details

Reviewers
None
Summary

The WebAssembly virtual ISA has an infinite virtual register set, and function arguments are passed in registers. We're currently using pseudo-instructions (eg. ARGUMENT_i32) inserted into the entry block which provide register definitions, however it's awkward to prevent other instructions from being scheduled above them, and subsequently to prevent register coloring from clobbering the argument registers before they are properly defined.

The patch here provides a different option: make it possible to have live-in virtual registers. This way, their liveness is naturally correct and liveness-oriented passes tend to handle them correctly by default, and it eliminates the need for the awkward pseudo-instructions.

The tricky part is that such virtual registers no longer have a defining instruction, which some parts of CodeGen currently assume, particularly the passes that operate on SSA form. To address this, this patch adds a getVRegDefMBB(), for use when code is calling getVRegDef() just to get the block the register is defined in (and this is what motivates making MachineRegisterInfo's MachineFunction non-const), and adds special checks when code is calling getVRegDef() because it actually wants the defining instruction.

Diff Detail

Repository
rL LLVM

Event Timeline

sunfish updated this revision to Diff 40402.Nov 17 2015, 9:29 AM
sunfish retitled this revision from to Support for function-live-in virtual registers.
sunfish updated this object.
sunfish set the repository for this revision to rL LLVM.
sunfish added a subscriber: llvm-commits.

Hi Dan,

I am puzzled by the following statement:

however it's awkward to prevent other instructions from being scheduled above them, and subsequently to prevent register coloring from clobbering the argument registers before they are properly defined.

Since you have an infinite number of registers, how do you end up with other registers clobbering the arguments registers?
Are these arguments registers actual physical registers?!

To be frank, I do not like the approach that’s why I would prefer to understand what are the constraints of the problem first.

That being said, let me comment a bit on the proposal. You are restricting the live-in information to the entry block and that sounds wrong. Indeed, I guess if you go down that road, you want to model arguments of landing pad the same way and the related blocks are not the entry block anymore!

Thanks,
-Quentin

sunfish added a comment.EditedNov 19 2015, 4:43 PM

Since you have an infinite number of registers, how do you end up with other registers > clobbering the arguments registers?
Are these arguments registers actual physical registers?!

We have an infinite number of registers, but it's still desirable to minimize the total number of registers, so we're doing coloring. It's similar to stack slot coloring; there are an unbounded number of stack slots, but it's desirable to minimize the total size of the stack.

To be frank, I do not like the approach that’s why I would prefer to understand what are the
constraints of the problem first.

We have arguments which are virtual registers. They are conceptually live-in to the function. How should we model this?

Right now we are modelling this with instructions in the entry block that define the values. This is not robust because the scheduler can move them below other instructions. Modelling them as live-in to the function would be a very natural way to express what we mean here.

That being said, let me comment a bit on the proposal. You are restricting the live-in information to the entry block and that sounds wrong. Indeed, I guess if you go down that road, you want to model arguments of landing pad the same way and the related blocks are not the entry block anymore!

I don't understand. The patch here models the registers as live-in to the function, using the same live-in mechanism that already exists in CodeGen for physical registers. Registers that are live-in to a function are naturally live-in to the entry block.

Hi Dan,

We have an infinite number of registers, but it's still desirable to minimize the total number of registers, so we're doing coloring.

So you technically don’t have a clobbering problem. You would just like to have them appear at the beginning of the function so that it is easier for the consumers of the web assembly IR to lower the ABI properly.

What am I saying is that the constraints you have are soft. You could imagine a different way of telling the consumers of the web assembly IR what are the arguments of the function.
By the way, if you get rid of those ARGUMENT instructions, how do the consumers of the web assembly IR know what they have to lower for the ABI?

I don't understand. The patch here models the registers as live-in to the function[...]. Registers that are live-in to a function are naturally live-in to the entry block.

Yes, but what I am saying is that you are restricting the notion of live-in for a virtual register to the entry block, whereas the one for physreg applies to all blocks. This is this asymmetry that I feel is wrong.
Wouldn’t it make sense to instead generalize the notion of physreg for your goals?

I think that if I know how the consumer of the web assembly lower the ABI (I.e., my first question), it would be easier to advice on the best way to tackle this.

What do you think?

Cheers,
-Quentin

So you technically don’t have a clobbering problem. You would just like to have them appear at the beginning of the function so that it is easier for the consumers of the web assembly IR to lower the ABI properly.

We actually want to color these registers along with the other registers, and this requires us to have precise liveness information.

What am I saying is that the constraints you have are soft. You could imagine a different way of telling the consumers of the web assembly IR what are the arguments of the function.
By the way, if you get rid of those ARGUMENT instructions, how do the consumers of the web assembly IR know what they have to lower for the ABI?

The signature of a function is explicit, so a consumer knows how many arguments there are. In a function with N arguments, the first N register numbers are the arguments (the numbering is not LLVM's virtual register numbering). Other than being initialized to the incoming argument values, these registers thereafter are identical to regular registers.

Also, if there is another way to solve the problem, I'd be very interested to hear more about it.

Yes, but what I am saying is that you are restricting the notion of live-in for a virtual register to the entry block, whereas the one for physreg applies to all blocks. This is this asymmetry that I feel is wrong.
Wouldn’t it make sense to instead generalize the notion of physreg for your goals?

Perhaps this assymetry could be said to be a reflection of the assymetry in the way LiveVariables and LiveIntervals track liveness of virtual versus physical registers.

Generalizing the concept of physregs to support variable numbers of physregs would be another possible solution. Would you prefer that? I expect it would require much more invasive changes.

Hi Dan,

Also, if there is another way to solve the problem, I'd be very interested to hear more about it.

I am not sure what to suggest given that I do not know all the parameters of the problem.
In particular, what does it mean for WebAssembly to color?

You are saying that you have an infinite number of physical registers, but on the other hands we don't support having a variable number of physical registers. How does this work?

My problem with the approach is that the scope seems so narrow that it feels to me like a hack.
E.g.,

  1. / Return the machine basic block that contains the definition of the / specified virtual register or null if none is found. This assumes that /// the code is in SSA form, so there should only be one definition. MachineBasicBlock *getVRegDefMBB(unsigned Reg) const;

What happen when the code is not in SSA anymore?
Say you have:
foo(v1)
if ()

v3 = ...
v2 = phi(v1, v3)

Which gives after coalescing:
foo(v1)
if ()

v1 = ...

How do you represent that?

In particular, we will see only one def if we start iterating over the def-uses.

The only place where virtual registers can be live-in are function entry and this is never called out and it is not future proof IMHO.
For instance, what will happen when you will want to model things like:
invoke foo landing pad bb1

The virtual registers for the landing pad (i.e., the arguments of the funclet) would need to be live-in of bb1 only, if you want a precise liveness.

All of these sounds to me like we do want physical register semantics, but we are not willing to pay the price for it.

So yes, I would certainly prefer having a variable number of physical registers and I do understand this is much more involved. For now, the proposed approach looks more error prone to me than the current implementation with the ARGUMENT instruction, where the scope is limited to web assembly and where the generic passes know how to deal with it (modulo the placement problem).

What about an attribute or something that says don't move those instructions?
I am guessing we must have something similar for barrier like instruction.

Perhaps this assymetry could be said to be a reflection of the assymetry in the way LiveVariables and LiveIntervals track liveness of virtual versus physical registers.

To be fair, those two liveness tracking are used differently and I think it would be nice to kill the LiveVariables at some point.

Generalizing the concept of physregs to support variable numbers of physregs would be another possible solution. Would you prefer that?

I admit I would prefer that.

Cheers,
-Quentin

sunfish updated this revision to Diff 40829.Nov 20 2015, 2:27 PM

Make getVRegDefMBB return NULL if a register is live-in and has other defs besides, meaning it is not in SSA form. This is more consistent with the existing getVRegDef.

I am not sure what to suggest given that I do not know all the parameters of the problem.
In particular, what does it mean for WebAssembly to color?

It's the same thing as lib/CodeGen/StackSlotColoring.cpp, but with virtual registers instead of stack slots. Here's the code:

http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/WebAssembly/WebAssemblyRegColoring.cpp?revision=253626&view=markup

The only tricky part is moving ARGUMENT instructions back to the top of the function and recomputing liveness, and that's the very code that the patch here is seeking to eliminate.

You are saying that you have an infinite number of physical registers, but on the other hands we don't support having a variable number of physical registers. How does this work?

The machine has infinite registers. We currently model them in LLVM with virtual registers.

Return the machine basic block that contains the definition of the / specified virtual register or null if none is found. This assumes that /// the code is in SSA form, so there should only be one definition. MachineBasicBlock *getVRegDefMBB(unsigned Reg) const;

What happen when the code is not in SSA anymore?

getVRegDefMBB is modeled directly after getVRegDef which already exists and already works this way. All uses of getVRegDefMBB were previously users of getVRegDef.

That said, this did prompt me to notice a bug; getVRegDef returns nullptr if the register has multiple defines; I've now updated the patch to make getVRegDefMBB check the same condition.

The only place where virtual registers can be live-in are function entry and this is never called out and it is not future proof IMHO.
For instance, what will happen when you will want to model things like:
invoke foo landing pad bb1

The virtual registers for the landing pad (i.e., the arguments of the funclet) would need to be live-in of bb1 only, if you want a precise liveness.

This is a basic asymmetry that already exists; machine basic blocks don't track virtual registers in their live-in sets.

Also, the return value of the call is not live across the unwind edge, so we're not going to be tempted to specially create newly live virtual registers along that edge.

All of these sounds to me like we do want physical register semantics, but we are not willing to pay the price for it.

What do you mean by "physical register semantics"?

So yes, I would certainly prefer having a variable number of physical registers and I do understand this is much more involved. For now, the proposed approach looks more error prone to me than the current implementation with the ARGUMENT instruction, where the scope is limited to web assembly and where the generic passes know how to deal with it (modulo the placement problem).

What about an attribute or something that says don't move those instructions?
I am guessing we must have something similar for barrier like instruction.

It's not easy to do, because it isn't enough to say "this instruction can't move", because we also need to say "other instructions can't move past this one, even if they have no side effects". And if we say that, then we have to think about whether it's valid to do MachineLICM, MachineCSE, or even register allocation itself in the vicinity of such a powerful instruction. It would end up being a fairly complex mechanism.

Perhaps this assymetry could be said to be a reflection of the assymetry in the way LiveVariables and LiveIntervals track liveness of virtual versus physical registers.

To be fair, those two liveness tracking are used differently and I think it would be nice to kill the LiveVariables at some point.

The following asymmetry in the MIR data structures already exists:

      Machine  MBB     MF
      Operand  LiveIn  LiveIn
     +-------+-------+-------+
Phys |   Y   |  Y    |   Y   |
Virt |   Y   |  N    |   N   |

My change would change the Func LiveIn column for virtual registers. This isn't introducing any new asymetry.

In fact, it's increasing symmetry because a register lifetime can start either at an instruction or before the function starts.

sunfish updated this revision to Diff 40842.Nov 20 2015, 4:09 PM

getVRegDef actually asserts if there isn't exactly one def, rather than returning nullptr, so make getVRegDefMBB do that too.

Hi Dan,

Thanks for your patience, taking some time to catch up :).

What do you mean by "physical register semantics”?

I mean having a register live-in of a function. This is not something that, at least, I expect from a virtual register.
It may be quite of a stretch, but the reason is because those live-ins have in fact several definitions, i.e., the setting of the arguments in each caller, whereas virtual registers are supposed to have just one (in the case of live-ins of a function, a reasonable representation, though not practical, would be a phi of each argument from the callers). The physical registers are IMO a cheap and accurate way to pieces the different live-ranges together.
That is why I believe this is better represented if it stays in the "physical register” semantic world.

This is a basic asymmetry that already exists; machine basic blocks don't track virtual registers in their live-in sets.

That is what I am saying and here, to me, we are making things worse, because we do not have such tracking but for the entry block. The fact that you said it is the live-in of the function instead of the live-in of the basic block is a subtle difference that may bit us.

My change would change the Func LiveIn column for virtual registers. This isn't introducing any new asymmetry.

Agree, but I don’t like the distinction between live-in of basic block and live-in of function. Right now, both physical and virtual registers are consistent with this respect, i.e., both have either have basic block *and* function live-in semantic available or basic block *and* function live-in semantic *not* available!

In fact, it's increasing symmetry because a register lifetime can start either at an instruction or before the function starts.

I disagree, this introduces a more subtle dissymmetry.

To summarize, I really believe the way to go is to being able to change the number of physical registers dynamically, without impacting the compile time of the targets that know this number statically. Another option could be to change the way we represent phi to go toward a swift IR like representation, i.e., arguments of basic block and use that on the entry block.

I reckon both options are involved, but I guess we are not in a hurry, so we can shot for the proper fix :).
Personally I prefer the physical registers approach.

Cheers,
-Quentin

sunfish abandoned this revision.Sep 28 2016, 4:11 PM

Closing the phabricator entry for this patch, which is no longer active.