Jakob's review of the basic register allocator.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@117384 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Andrew Trick 2010-10-26 18:34:01 +00:00
parent 6915cdab8f
commit e16eecc323
4 changed files with 160 additions and 113 deletions

View File

@ -21,6 +21,9 @@
using namespace llvm;
// Merge a LiveInterval's segments. Guarantee no overlaps.
//
// Consider coalescing adjacent segments to save space, even though it makes
// extraction more complicated.
void LiveIntervalUnion::unify(LiveInterval &lvr) {
// Add this live virtual register to the union
LiveVirtRegs::iterator pos = std::upper_bound(lvrs_.begin(), lvrs_.end(),
@ -34,19 +37,21 @@ void LiveIntervalUnion::unify(LiveInterval &lvr) {
LiveSegment segment(lvrI->start, lvrI->end, lvr);
segPos = segments_.insert(segPos, segment);
assert(*segPos == segment && "need equal val for equal key");
#ifndef NDEBUG
// check for overlap (inductively)
if (segPos != segments_.begin()) {
SegmentIter prevPos = segPos;
--prevPos;
assert(prevPos->end <= segment.start && "overlapping segments" );
}
SegmentIter nextPos = segPos;
++nextPos;
if (nextPos != segments_.end())
assert(segment.end <= nextPos->start && "overlapping segments" );
#endif // NDEBUG
}
}
namespace {
// Keep LVRs sorted for fast membership test and extraction.
struct LessReg
: public std::binary_function<LiveInterval*, LiveInterval*, bool> {
bool operator()(const LiveInterval *left, const LiveInterval *right) const {
return left->reg < right->reg;
}
};
// Low-level helper to find the first segment in the range [segI,segEnd) that
// intersects with a live virtual register segment, or segI.start >= lvr.end
//
@ -67,10 +72,8 @@ struct LessReg
// Assuming intervals are disjoint, if an intersection exists, it must be the
// segment found or immediately behind it. We continue reverse iterating to
// return the first overlap.
//
// FIXME: support extract(), handle tombstones of extracted lvrs.
typedef LiveIntervalUnion::SegmentIter SegmentIter;
SegmentIter upperBound(SegmentIter segBegin,
static SegmentIter upperBound(SegmentIter segBegin,
SegmentIter segEnd,
const LiveRange &lvrSeg) {
assert(lvrSeg.end > segBegin->start && "segment iterator precondition");
@ -84,7 +87,6 @@ SegmentIter upperBound(SegmentIter segBegin,
}
return segI;
}
} // end anonymous namespace
// Private interface accessed by Query.
//

View File

@ -23,13 +23,16 @@
namespace llvm {
// A LiveSegment is a copy of a LiveRange object used within
// LiveIntervalUnion. LiveSegment additionally contains a pointer to its
// original live virtual register (LiveInterval). This allows quick lookup of
// the live virtual register as we iterate over live segments in a union. Note
// that LiveRange is misnamed and actually represents only a single contiguous
// interval within a virtual register's liveness. To limit confusion, in this
// file we refer it as a live segment.
/// A LiveSegment is a copy of a LiveRange object used within
/// LiveIntervalUnion. LiveSegment additionally contains a pointer to its
/// original live virtual register (LiveInterval). This allows quick lookup of
/// the live virtual register as we iterate over live segments in a union. Note
/// that LiveRange is misnamed and actually represents only a single contiguous
/// interval within a virtual register's liveness. To limit confusion, in this
/// file we refer it as a live segment.
///
/// Note: This currently represents a half-open interval [start,end).
/// If LiveRange is modified to represent a closed interval, so should this.
struct LiveSegment {
SlotIndex start;
SlotIndex end;
@ -46,16 +49,10 @@ struct LiveSegment {
return !operator==(ls);
}
bool operator<(const LiveSegment &ls) const {
return start < ls.start || (start == ls.start && end < ls.end);
}
// Order segments by starting point only--we expect them to be disjoint.
bool operator<(const LiveSegment &ls) const { return start < ls.start; }
};
/// Compare a live virtual register segment to a LiveIntervalUnion segment.
inline bool overlap(const LiveRange &lvrSeg, const LiveSegment &liuSeg) {
return lvrSeg.start < liuSeg.end && liuSeg.start < lvrSeg.end;
}
inline bool operator<(SlotIndex V, const LiveSegment &ls) {
return V < ls.start;
}
@ -64,6 +61,11 @@ inline bool operator<(const LiveSegment &ls, SlotIndex V) {
return ls.start < V;
}
/// Compare a live virtual register segment to a LiveIntervalUnion segment.
inline bool overlap(const LiveRange &lvrSeg, const LiveSegment &liuSeg) {
return lvrSeg.start < liuSeg.end && liuSeg.start < lvrSeg.end;
}
/// Union of live intervals that are strong candidates for coalescing into a
/// single register (either physical or virtual depending on the context). We
/// expect the constituent live intervals to be disjoint, although we may
@ -100,13 +102,18 @@ private:
public:
// default ctor avoids placement new
LiveIntervalUnion() : repReg_(0) {}
// Initialize the union by associating it with a representative register
// number.
void init(unsigned repReg) { repReg_ = repReg; }
// Iterate over all segments in the union of live virtual registers ordered
// by their starting position.
SegmentIter begin() { return segments_.begin(); }
SegmentIter end() { return segments_.end(); }
/// FIXME: !!!!!!!!!!! Keeps a non-const ref
// Add a live virtual register to this union and merge its segments.
// Holds a nonconst reference to the LVR for later maniplution.
void unify(LiveInterval &lvr);
// FIXME: needed by RegAllocGreedy

View File

@ -37,17 +37,29 @@
#ifndef LLVM_CODEGEN_REGALLOCBASE
#define LLVM_CODEGEN_REGALLOCBASE
#include "LiveIntervalUnion.h"
#include "VirtRegMap.h"
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/ADT/OwningPtr.h"
#include <vector>
#include <queue>
namespace llvm {
template<typename T> class SmallVectorImpl;
class TargetRegisterInfo;
class VirtRegMap;
class LiveIntervals;
// Heuristic that determines the priority of assigning virtual to physical
// registers. The main impact of the heuristic is expected to be compile time.
// The default is to simply compare spill weights.
struct LessSpillWeightPriority
: public std::binary_function<LiveInterval,LiveInterval, bool> {
bool operator()(const LiveInterval *left, const LiveInterval *right) const {
return left->weight < right->weight;
}
};
// Forward declare a priority queue of live virtual registers. If an
// implementation needs to prioritize by anything other than spill weight, then
// this will become an abstract base class with virtual calls to push/get.
class LiveVirtRegQueue;
/// RegAllocBase provides the register allocation driver and interface that can
/// be extended to add interesting heuristics.
@ -58,9 +70,6 @@ class VirtRegMap;
/// standard comparator.
class RegAllocBase {
protected:
typedef SmallVector<LiveInterval*, 4> LiveVirtRegs;
typedef LiveVirtRegs::iterator LVRIter;
// Array of LiveIntervalUnions indexed by physical register.
class LIUArray {
unsigned nRegs_;
@ -92,18 +101,20 @@ protected:
// A RegAlloc pass should call this before allocatePhysRegs.
void init(const TargetRegisterInfo &tri, VirtRegMap &vrm, LiveIntervals &lis);
// The top-level driver. Specialize with the comparator that determines the
// priority of assigning live virtual registers. The output is a VirtRegMap
// that us updated with physical register assignments.
template<typename LICompare>
void allocatePhysRegs(LICompare liCompare);
// The top-level driver. The output is a VirtRegMap that us updated with
// physical register assignments.
//
// If an implementation wants to override the LiveInterval comparator, we
// should modify this interface to allow passing in an instance derived from
// LiveVirtRegQueue.
void allocatePhysRegs();
// A RegAlloc pass should override this to provide the allocation heuristics.
// Each call must guarantee forward progess by returning an available
// PhysReg or new set of split LiveVirtRegs. It is up to the splitter to
// Each call must guarantee forward progess by returning an available PhysReg
// or new set of split live virtual registers. It is up to the splitter to
// converge quickly toward fully spilled live ranges.
virtual unsigned selectOrSplit(LiveInterval &lvr,
LiveVirtRegs &splitLVRs) = 0;
SmallVectorImpl<LiveInterval*> &splitLVRs) = 0;
// A RegAlloc pass should call this when PassManager releases its memory.
virtual void releaseMemory();
@ -113,69 +124,9 @@ protected:
bool checkPhysRegInterference(LiveIntervalUnion::Query &query, unsigned preg);
private:
template<typename PQ>
void seedLiveVirtRegs(PQ &lvrQ);
void seedLiveVirtRegs(LiveVirtRegQueue &lvrQ);
};
// Heuristic that determines the priority of assigning virtual to physical
// registers. The main impact of the heuristic is expected to be compile time.
// The default is to simply compare spill weights.
struct LessSpillWeightPriority
: public std::binary_function<LiveInterval,LiveInterval, bool> {
bool operator()(const LiveInterval *left, const LiveInterval *right) const {
return left->weight < right->weight;
}
};
// Visit all the live virtual registers. If they are already assigned to a
// physical register, unify them with the corresponding LiveIntervalUnion,
// otherwise push them on the priority queue for later assignment.
template<typename PQ>
void RegAllocBase::seedLiveVirtRegs(PQ &lvrQ) {
for (LiveIntervals::iterator liItr = lis_->begin(), liEnd = lis_->end();
liItr != liEnd; ++liItr) {
unsigned reg = liItr->first;
LiveInterval &li = *liItr->second;
if (TargetRegisterInfo::isPhysicalRegister(reg)) {
physReg2liu_[reg].unify(li);
}
else {
lvrQ.push(&li);
}
}
}
// Top-level driver to manage the queue of unassigned LiveVirtRegs and call the
// selectOrSplit implementation.
template<typename LICompare>
void RegAllocBase::allocatePhysRegs(LICompare liCompare) {
typedef std::priority_queue
<LiveInterval*, std::vector<LiveInterval*>, LICompare> LiveVirtRegQueue;
LiveVirtRegQueue lvrQ(liCompare);
seedLiveVirtRegs(lvrQ);
while (!lvrQ.empty()) {
LiveInterval *lvr = lvrQ.top();
lvrQ.pop();
LiveVirtRegs splitLVRs;
unsigned availablePhysReg = selectOrSplit(*lvr, splitLVRs);
if (availablePhysReg) {
assert(splitLVRs.empty() && "inconsistent splitting");
assert(!vrm_->hasPhys(lvr->reg) && "duplicate vreg in interval unions");
vrm_->assignVirt2Phys(lvr->reg, availablePhysReg);
physReg2liu_[availablePhysReg].unify(*lvr);
}
else {
for (LVRIter lvrI = splitLVRs.begin(), lvrEnd = splitLVRs.end();
lvrI != lvrEnd; ++lvrI ) {
assert(TargetRegisterInfo::isVirtualRegister((*lvrI)->reg) &&
"expect split value in virtual register");
lvrQ.push(*lvrI);
}
}
}
}
} // end namespace llvm
#endif // !defined(LLVM_CODEGEN_REGALLOCBASE)

View File

@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "regalloc"
#include "LiveIntervalUnion.h"
#include "RegAllocBase.h"
#include "RenderMachineFunction.h"
#include "Spiller.h"
@ -33,6 +34,14 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "VirtRegMap.h"
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include <vector>
#include <queue>
using namespace llvm;
static RegisterRegAlloc basicRegAlloc("basic", "basic register allocator",
@ -72,7 +81,8 @@ public:
virtual void releaseMemory();
virtual unsigned selectOrSplit(LiveInterval &lvr, LiveVirtRegs &splitLVRs);
virtual unsigned selectOrSplit(LiveInterval &lvr,
SmallVectorImpl<LiveInterval*> &splitLVRs);
/// Perform register allocation.
virtual bool runOnMachineFunction(MachineFunction &mf);
@ -101,7 +111,7 @@ INITIALIZE_PASS_DEPENDENCY(RenderMachineFunction)
#endif
INITIALIZE_PASS_END(RABasic, "basic-regalloc",
"Basic Register Allocator", false, false)
#endif // INITIALIZE_PASS
#endif // disable INITIALIZE_PASS
RABasic::RABasic(): MachineFunctionPass(ID) {
initializeLiveIntervalsPass(*PassRegistry::getPassRegistry());
@ -168,6 +178,79 @@ void RegAllocBase::releaseMemory() {
physReg2liu_.clear();
}
namespace llvm {
/// This class defines a queue of live virtual registers prioritized by spill
/// weight. The heaviest vreg is popped first.
///
/// Currently, this is trivial wrapper that gives us an opaque type in the
/// header, but we may later give it a virtual interface for register allocators
/// to override the priority queue comparator.
class LiveVirtRegQueue {
typedef std::priority_queue
<LiveInterval*, std::vector<LiveInterval*>, LessSpillWeightPriority> PQ;
PQ pq_;
public:
// Is the queue empty?
bool empty() { return pq_.empty(); }
// Get the highest priority lvr (top + pop)
LiveInterval *get() {
LiveInterval *lvr = pq_.top();
pq_.pop();
return lvr;
}
// Add this lvr to the queue
void push(LiveInterval *lvr) {
pq_.push(lvr);
}
};
} // end namespace llvm
// Visit all the live virtual registers. If they are already assigned to a
// physical register, unify them with the corresponding LiveIntervalUnion,
// otherwise push them on the priority queue for later assignment.
void RegAllocBase::seedLiveVirtRegs(LiveVirtRegQueue &lvrQ) {
for (LiveIntervals::iterator liItr = lis_->begin(), liEnd = lis_->end();
liItr != liEnd; ++liItr) {
unsigned reg = liItr->first;
LiveInterval &li = *liItr->second;
if (TargetRegisterInfo::isPhysicalRegister(reg)) {
physReg2liu_[reg].unify(li);
}
else {
lvrQ.push(&li);
}
}
}
// Top-level driver to manage the queue of unassigned LiveVirtRegs and call the
// selectOrSplit implementation.
void RegAllocBase::allocatePhysRegs() {
LiveVirtRegQueue lvrQ;
seedLiveVirtRegs(lvrQ);
while (!lvrQ.empty()) {
LiveInterval *lvr = lvrQ.get();
typedef SmallVector<LiveInterval*, 4> LVRVec;
LVRVec splitLVRs;
unsigned availablePhysReg = selectOrSplit(*lvr, splitLVRs);
if (availablePhysReg) {
assert(splitLVRs.empty() && "inconsistent splitting");
assert(!vrm_->hasPhys(lvr->reg) && "duplicate vreg in interval unions");
vrm_->assignVirt2Phys(lvr->reg, availablePhysReg);
physReg2liu_[availablePhysReg].unify(*lvr);
}
else {
for (LVRVec::iterator lvrI = splitLVRs.begin(), lvrEnd = splitLVRs.end();
lvrI != lvrEnd; ++lvrI) {
assert(TargetRegisterInfo::isVirtualRegister((*lvrI)->reg) &&
"expect split value in virtual register");
lvrQ.push(*lvrI);
}
}
}
}
// Check if this live virtual reg interferes with a physical register. If not,
// then check for interference on each register that aliases with the physical
// register.
@ -201,7 +284,8 @@ bool RegAllocBase::checkPhysRegInterference(LiveIntervalUnion::Query &query,
// available register. So, the number of interference tests in the worst case is
// |vregs| * |machineregs|. And since the number of interference tests is
// minimal, there is no value in caching them.
unsigned RABasic::selectOrSplit(LiveInterval &lvr, LiveVirtRegs &splitLVRs) {
unsigned RABasic::selectOrSplit(LiveInterval &lvr,
SmallVectorImpl<LiveInterval*> &splitLVRs) {
// Check for an available reg in this class.
const TargetRegisterClass *trc = mri_->getRegClass(lvr.reg);
for (TargetRegisterClass::iterator trcI = trc->allocation_order_begin(*mf_),
@ -238,7 +322,7 @@ bool RABasic::runOnMachineFunction(MachineFunction &mf) {
spiller_.reset(createSpiller(*this, *mf_, *vrm_));
allocatePhysRegs(LessSpillWeightPriority());
allocatePhysRegs();
// Diagnostic output before rewriting
DEBUG(dbgs() << "Post alloc VirtRegMap:\n" << *vrm_ << "\n");
@ -249,6 +333,9 @@ bool RABasic::runOnMachineFunction(MachineFunction &mf) {
// Run rewriter
std::auto_ptr<VirtRegRewriter> rewriter(createVirtRegRewriter());
rewriter->runOnMachineFunction(*mf_, *vrm_, lis_);
// The pass output is in VirtRegMap. Release all the transient data.
releaseMemory();
return true;
}