[llvm-commits] [llvm] r82734 - in /llvm/trunk/lib: CodeGen/PrologEpilogInserter.cpp CodeGen/PrologEpilogInserter.h Target/ARM/ARMBaseRegisterInfo.cpp Target/ARM/Thumb1RegisterInfo.cpp
Jakob Stoklund Olesen
stoklund at 2pi.dk
Mon Sep 28 11:23:47 PDT 2009
On 28/09/2009, at 18.25, Jim Grosbach wrote:
> On Sep 25, 2009, at 12:47 AM, Jakob Stoklund Olesen wrote:
>> It seems that for now, we must require that live ranges of virtual
>> registers are disjoint. Otherwise we would need more than one
>> emergency spill slot in the worst case.
>>
>> With only one virtual register live at a time, the map is indeed
>> overkill.
>>
>> It would also be a good idea to enforce the one-virtual-register-at-
>> a-time rule with asserts here.
>
> Agreed to both. As indicated by the comment, I was thinking towards
> doing something fancier when we start to re-use these. Allowing more
> than one to be live so long as the emergency spill slot is unused,
> or something like that. Thus, I didn't want to encode too many
> strong assumption in place that would make that harder.
It is definitely possible to do something fancier here. The thing to
remember, though, is that this pass must work /every/ time. A clever
trick that relies on a register or the emergency spill slot being
available will maybe work 99% of the time, but we must have a fallback
solution for the last 1%. Therefore, if virtual registers overlap, we
must have two (or more) emergency spill slots.
In a situation like this, I think we need to make clear rules for the
scavenger clients: If your virtual registers don't overlap, the
scavenger will work every time. Clients must follow the rules,
otherwise there will be weird functions that don't compile.
If a client needs more flexibility, we can change the rules. For
instance: "You must give the scavenger as many emergency spill slots
as the maximum possible number of simultaneous virtual registers". I
don't think that is needed for now.
Fancy code can give us better performance, for instance by reducing
the number of emergency spills, but we must have a worst-case
fallback always.
> Upon reflection, however, I don't think that's a direction we should
> go. If we want the compiler to be that much smarter about reusing
> the constants, we should work towards letting the real register
> allocator do the work. This pass should be a quick and simple one to
> handle obvious cases and shouldn't try to get too smart.
That is probably true. We can improve performance by using available
callee-saved registers, and optimizing emergency spill locations. If
things get more complicated, it is probably a good idea to involve the
register allocator somehow.
[...]
>> Ideally, I would like to get rid of FindUnusedReg(), so you could
>> just say scavengeRegister() here. The ARMLoadStoreOptimizer is the
>> only one to use FindUnusedReg() without immediately calling
>> scavengerRegister() when it fails. I am no sure if that is
>> intentional.
>
> I've been thinking about that, too. I can see the benefit in asking
> it for a register, but only if a spill isn't necessary. For example,
> when loading a constant, materializing the constant directly may be
> more expensive than a load, but still cheaper if spills are
> necessary. Nothing is being that smart currently, though. In
> practice, it looks like one could call scavengeRegister() directly
> without first calling FindUnusedReg() and get the single-call
> interface you're looking for.
I think that scavengerRegister should implicitly call FindUnusedReg so
that clients don't have to worry about it. I can see a case for still
needing a raw FindUnusedReg, yes.
But the needed function is probably really
FindUnusedRegThatIsAlsoNotUsedByThisInstructionRange().
Such clever clients must always be able to handle FindUnusedReg
returning 0. It looks like the ARMLoadStoreOptimizer handles that
correctly.
>> There is another issue here. FindUnusedReg() will give you a
>> register that is unused /at the current position/. You really need
>> a register that is unused in the whole live range of the virtual
>> register you are replacing. scavengeRegister() won't return a
>> register that is used by the instruction at I, but it looks like we
>> need a variant that can exclude from whole range of instructions.
>>
>> The rule should be: "NewReg must not be defined by any instruction
>> in the range, except for the last. NewReg must not be an
>> EarlyClobber operand on the last instruction."
>
> In the general case, yes, that's true. In the cases we're dealing
> with, however, we don't need to be that careful. The register is
> defined, then used in the next instruction or two. We're splitting
> out a single instruction referencing the frame when the constant
> offset is too large. Either we're adjusting the stack point by a
> constant value and need a register to hold the constant, or we're
> accessing a stack slot and need a register to hold the offset to the
> slot. Either way, if the register live range is such that it's
> insufficient to check if the register is available at the current
> location, then it's a flaw in the way the back end is using this pass.
An example from the Blackfin backend. Storing a 16-bit subregister to
a stack slot:
STORE16fi P0.L, <fi#-1>, 0
With the new vreg code, eliminateFrameIndex will produce:
%reg2000 = LOADuimm16 100
%reg2000 = ADDpp %reg2000, %FP
STORE16pi %reg2000, %P0.L
Now we need to replace the vreg with a scavenged register. Assume all
P-class registers are used, so we must scavenge one. If
scavengeRegister() chooses to scavenge P0, we are dead - the code
would become
STORE32pi %FP, emergengy-slot, P0
%P0 = LOADuimm16 100
%P0 = ADDpp %P0, %FP
STORE16pi %P0, %P0.L
LOAD32pi %P0, %FP, emergency-slot
This disaster won't actually happen because the heuristics in
RegScavenger::findSurvivorReg() choose the candidate that is untouched
for the longest time. But that is just a heuristic, we shouldn't rely
on that.
I think scavengeRegister needs to examine all three instructions (the
live range of %reg2000) to avoid such disasters.
> That said, that's an assumption worth enforcing. Perhaps in the
> loop, if we have an active scavenged register, we should add an
> assert() that the current instruction does not reference it. The
> reference we're looking for will still be a virtual register, so it
> won't fire on that. Hmmm.
That is a very good idea. It is simple to do, and you are scanning
operands anyway.
[...]
>> Yeah, I also want to get rid of forward() as a public method. Since
>> scavengeRegister takes an iterator argument that /must/ be next(MI)
>> anyway, there is really no point to having it.
>
> I'm a bit leery of merging it with scavengeRegister(). It feels more
> appropriate to keep those bits of functionality separate since
> they're logically distinct. I can envision wanting to do things like
> tracking to a point in the block and only conditionally scavenging a
> register before proceeding.
>>
>> Batch forwarding over a range of instructions is going to be faster
>> as well.
>
> forward(MachineBasicBlock::iterator I) just loops over calls to
> forward() currently. Any performance improvements available from
> merging the forwarding into scavengeRegister() should be equally
> applicable as improvements to the current implementation, I would
> think.
Yes. The performance improvement I am after mostly involves the four
BitVectors in RegScavenger::forward(). They currently cost four
mallocs and frees per instruction. By swapping forward() and forward
(MI), we could make a tighter loop around the core logic, and only
allocate BitVectors once per bulk skip.
> Can you elaborate a bit on why you want to remove forward()? I like
> the idea of removing methods that aren't really necessary, but I'm
> not sure that's the case here. Are there other motivations?
Forget about performance. The impact of those four BitVectors probably
can't be measured.
I want to eliminate redundancy from the API. Right now, you have to
call forward(MI) to advance the scavenger to your position. Then you
call scavengeRegister(RC, I, 0) where I /must/ be equal to next(MI).
It is an error to pass any other iterator, so what is the point of
having the argument?
So you can choose to remove the iterator argument from scavengeRegister
(), or you can get rid of forward() (as a public method, anyway). I am
shooting for forward() because the semantics are already a bit weird.
Calling forward(MI) places the scavenger /after/ MI while BuildMI()
inserts instructions /before/ the iterator. That is why you are
calling forward() at the bottom of you loop, rather than at the top.
There are good reasons for this, but it is still weird.
By removing forward(), we push the weirdness into the RegScavenger.
If the choose to retain FindUnusedReg, it would need to take an
iterator argument as well.
But given the discussion above, I think that both methods should take
two iterator arguments specifying a range of instructions that must
not be using the returned register.
More information about the llvm-commits
mailing list