[LiveIntervalAnalysis] Speed up creation of live ranges for physical registers

by using a segment set.

The patch addresses a compile-time performance regression in the LiveIntervals
analysis pass (see http://llvm.org/bugs/show_bug.cgi?id=18580). This regression
is especially critical when compiling long functions. Our analysis had shown
that the most of time is taken for generation of live intervals for physical
registers. Insertions in the middle of the array of live ranges cause quadratic
algorithmic complexity, which is apparently the main reason for the slow-down. 

Overview of changes:
- The patch introduces an additional std::set<Segment>* member in LiveRange for
  storing segments in the phase of initial creation. The set is used if this
  member is not NULL, otherwise everything works the old way. 
- The set of operations on LiveRange used during initial creation (i.e. used by
  createDeadDefs and extendToUses) have been reimplemented to use the segment
  set if it is available.
- After a live range is created the contents of the set are flushed to the
  segment vector, because the set is not as efficient as the vector for the
  later uses of the live range. After the flushing, the set is deleted and
  cannot be used again.
- The set is only for live ranges computed in
  LiveIntervalAnalysis::computeLiveInRegUnits() and getRegUnit() but not in
  computeVirtRegs(), because I did not bring any performance benefits to
  computeVirtRegs() and for some examples even brought a slow down.

Patch by Vaidas Gasiunas <vaidas.gasiunas@sap.com>

Differential Revision: http://reviews.llvm.org/D6013


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@228421 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Quentin Colombet 2015-02-06 18:42:41 +00:00
parent b3189eac3f
commit 4c2a2ac196
4 changed files with 347 additions and 153 deletions

View File

@ -27,6 +27,7 @@
#include "llvm/Support/Allocator.h"
#include <cassert>
#include <climits>
#include <set>
namespace llvm {
class CoalescerPair;
@ -194,6 +195,12 @@ namespace llvm {
Segments segments; // the liveness segments
VNInfoList valnos; // value#'s
// The segment set is used temporarily to accelerate initial computation
// of live ranges of physical registers in computeRegUnitRange.
// After that the set is flushed to the segment vector and deleted.
typedef std::set<Segment> SegmentSet;
SegmentSet *segmentSet;
typedef Segments::iterator iterator;
iterator begin() { return segments.begin(); }
iterator end() { return segments.end(); }
@ -211,12 +218,18 @@ namespace llvm {
const_vni_iterator vni_end() const { return valnos.end(); }
/// Constructs a new LiveRange object.
LiveRange() {
LiveRange(bool UseSegmentSet = false) : segmentSet(nullptr) {
if (UseSegmentSet)
segmentSet = new SegmentSet();
}
/// Constructs a new LiveRange object by copying segments and valnos from
/// another LiveRange.
LiveRange(const LiveRange &Other, BumpPtrAllocator &Allocator) {
LiveRange(const LiveRange &Other, BumpPtrAllocator &Allocator)
: segmentSet(nullptr) {
assert(Other.segmentSet == nullptr &&
"Copying of LiveRanges with active SegmentSets is not supported");
// Duplicate valnos.
for (const VNInfo *VNI : Other.valnos) {
createValueCopy(VNI, Allocator);
@ -227,6 +240,8 @@ namespace llvm {
}
}
~LiveRange() { delete segmentSet; }
/// advanceTo - Advance the specified iterator to point to the Segment
/// containing the specified position, or end() if the position is past the
/// end of the range. If no Segment contains this position, but the
@ -437,9 +452,7 @@ namespace llvm {
/// Add the specified Segment to this range, merging segments as
/// appropriate. This returns an iterator to the inserted segment (which
/// may have grown since it was inserted).
iterator addSegment(Segment S) {
return addSegmentFrom(S, segments.begin());
}
iterator addSegment(Segment S);
/// extendInBlock - If this range is live before Kill in the basic block
/// that starts at StartIdx, extend it to be live up to Kill, and return
@ -540,6 +553,12 @@ namespace llvm {
return thisIndex < otherIndex;
}
/// Flush segment set into the regular segment vector.
/// The method is to be called after the live range
/// has been created, if use of the segment set was
/// activated in the constructor of the live range.
void flushSegmentSet();
void print(raw_ostream &OS) const;
void dump() const;
@ -557,10 +576,8 @@ namespace llvm {
void append(const LiveRange::Segment S);
private:
iterator addSegmentFrom(Segment S, iterator From);
void extendSegmentEndTo(iterator I, SlotIndex NewEnd);
iterator extendSegmentStartTo(iterator I, SlotIndex NewStr);
friend class LiveRangeUpdater;
void addSegmentToSet(Segment S);
void markValNoForDeletion(VNInfo *V);
};

View File

@ -27,12 +27,15 @@
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/SlotIndexes.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include <cmath>
#include <iterator>
namespace llvm {
extern cl::opt<bool> UseSegmentSetForPhysRegs;
class AliasAnalysis;
class BitVector;
class BlockFrequency;
@ -377,7 +380,8 @@ namespace llvm {
LiveRange *LR = RegUnitRanges[Unit];
if (!LR) {
// Compute missing ranges on demand.
RegUnitRanges[Unit] = LR = new LiveRange();
// Use segment set to speed-up initial computation of the live range.
RegUnitRanges[Unit] = LR = new LiveRange(UseSegmentSetForPhysRegs);
computeRegUnitRange(*LR, Unit);
}
return *LR;

View File

@ -32,6 +32,272 @@
#include <algorithm>
using namespace llvm;
//===----------------------------------------------------------------------===//
// Implementation of various methods necessary for calculation of live ranges.
// The implementation of the methods abstracts from the concrete type of the
// segment collection.
//
// Implementation of the class follows the Template design pattern. The base
// class contains generic algorithms that call collection-specific methods,
// which are provided in concrete subclasses. In order to avoid virtual calls
// these methods are provided by means of C++ template instantiation.
// The base class calls the methods of the subclass through method impl(),
// which casts 'this' pointer to the type of the subclass.
//
//===----------------------------------------------------------------------===//
template <typename ImplT, typename IteratorT, typename CollectionT>
class CalcLiveRangeUtilBase {
protected:
LiveRange *LR;
protected:
CalcLiveRangeUtilBase(LiveRange *LR) : LR(LR) {}
public:
typedef LiveRange::Segment Segment;
typedef IteratorT iterator;
VNInfo *createDeadDef(SlotIndex Def, VNInfo::Allocator &VNInfoAllocator) {
assert(!Def.isDead() && "Cannot define a value at the dead slot");
iterator I = impl().find(Def);
if (I == segments().end()) {
VNInfo *VNI = LR->getNextValue(Def, VNInfoAllocator);
impl().insertAtEnd(Segment(Def, Def.getDeadSlot(), VNI));
return VNI;
}
Segment *S = segmentAt(I);
if (SlotIndex::isSameInstr(Def, S->start)) {
assert(S->valno->def == S->start && "Inconsistent existing value def");
// It is possible to have both normal and early-clobber defs of the same
// register on an instruction. It doesn't make a lot of sense, but it is
// possible to specify in inline assembly.
//
// Just convert everything to early-clobber.
Def = std::min(Def, S->start);
if (Def != S->start)
S->start = S->valno->def = Def;
return S->valno;
}
assert(SlotIndex::isEarlierInstr(Def, S->start) && "Already live at def");
VNInfo *VNI = LR->getNextValue(Def, VNInfoAllocator);
segments().insert(I, Segment(Def, Def.getDeadSlot(), VNI));
return VNI;
}
VNInfo *extendInBlock(SlotIndex StartIdx, SlotIndex Kill) {
if (segments().empty())
return nullptr;
iterator I =
impl().findInsertPos(Segment(Kill.getPrevSlot(), Kill, nullptr));
if (I == segments().begin())
return nullptr;
--I;
if (I->end <= StartIdx)
return nullptr;
if (I->end < Kill)
extendSegmentEndTo(I, Kill);
return I->valno;
}
/// This method is used when we want to extend the segment specified
/// by I to end at the specified endpoint. To do this, we should
/// merge and eliminate all segments that this will overlap
/// with. The iterator is not invalidated.
void extendSegmentEndTo(iterator I, SlotIndex NewEnd) {
assert(I != segments().end() && "Not a valid segment!");
Segment *S = segmentAt(I);
VNInfo *ValNo = I->valno;
// Search for the first segment that we can't merge with.
iterator MergeTo = std::next(I);
for (; MergeTo != segments().end() && NewEnd >= MergeTo->end; ++MergeTo)
assert(MergeTo->valno == ValNo && "Cannot merge with differing values!");
// If NewEnd was in the middle of a segment, make sure to get its endpoint.
S->end = std::max(NewEnd, std::prev(MergeTo)->end);
// If the newly formed segment now touches the segment after it and if they
// have the same value number, merge the two segments into one segment.
if (MergeTo != segments().end() && MergeTo->start <= I->end &&
MergeTo->valno == ValNo) {
S->end = MergeTo->end;
++MergeTo;
}
// Erase any dead segments.
segments().erase(std::next(I), MergeTo);
}
/// This method is used when we want to extend the segment specified
/// by I to start at the specified endpoint. To do this, we should
/// merge and eliminate all segments that this will overlap with.
iterator extendSegmentStartTo(iterator I, SlotIndex NewStart) {
assert(I != segments().end() && "Not a valid segment!");
Segment *S = segmentAt(I);
VNInfo *ValNo = I->valno;
// Search for the first segment that we can't merge with.
iterator MergeTo = I;
do {
if (MergeTo == segments().begin()) {
S->start = NewStart;
segments().erase(MergeTo, I);
return I;
}
assert(MergeTo->valno == ValNo && "Cannot merge with differing values!");
--MergeTo;
} while (NewStart <= MergeTo->start);
// If we start in the middle of another segment, just delete a range and
// extend that segment.
if (MergeTo->end >= NewStart && MergeTo->valno == ValNo) {
segmentAt(MergeTo)->end = S->end;
} else {
// Otherwise, extend the segment right after.
++MergeTo;
Segment *MergeToSeg = segmentAt(MergeTo);
MergeToSeg->start = NewStart;
MergeToSeg->end = S->end;
}
segments().erase(std::next(MergeTo), std::next(I));
return MergeTo;
}
iterator addSegment(Segment S) {
SlotIndex Start = S.start, End = S.end;
iterator I = impl().findInsertPos(S);
// If the inserted segment starts in the middle or right at the end of
// another segment, just extend that segment to contain the segment of S.
if (I != segments().begin()) {
iterator B = std::prev(I);
if (S.valno == B->valno) {
if (B->start <= Start && B->end >= Start) {
extendSegmentEndTo(B, End);
return B;
}
} else {
// Check to make sure that we are not overlapping two live segments with
// different valno's.
assert(B->end <= Start &&
"Cannot overlap two segments with differing ValID's"
" (did you def the same reg twice in a MachineInstr?)");
}
}
// Otherwise, if this segment ends in the middle of, or right next
// to, another segment, merge it into that segment.
if (I != segments().end()) {
if (S.valno == I->valno) {
if (I->start <= End) {
I = extendSegmentStartTo(I, Start);
// If S is a complete superset of a segment, we may need to grow its
// endpoint as well.
if (End > I->end)
extendSegmentEndTo(I, End);
return I;
}
} else {
// Check to make sure that we are not overlapping two live segments with
// different valno's.
assert(I->start >= End &&
"Cannot overlap two segments with differing ValID's");
}
}
// Otherwise, this is just a new segment that doesn't interact with
// anything.
// Insert it.
return segments().insert(I, S);
}
private:
ImplT &impl() { return *static_cast<ImplT *>(this); }
CollectionT &segments() { return impl().segmentsColl(); }
Segment *segmentAt(iterator I) { return const_cast<Segment *>(&(*I)); }
};
//===----------------------------------------------------------------------===//
// Instantiation of the methods for calculation of live ranges
// based on a segment vector.
//===----------------------------------------------------------------------===//
class CalcLiveRangeUtilVector;
typedef CalcLiveRangeUtilBase<CalcLiveRangeUtilVector, LiveRange::iterator,
LiveRange::Segments> CalcLiveRangeUtilVectorBase;
class CalcLiveRangeUtilVector : public CalcLiveRangeUtilVectorBase {
public:
CalcLiveRangeUtilVector(LiveRange *LR) : CalcLiveRangeUtilVectorBase(LR) {}
private:
friend CalcLiveRangeUtilVectorBase;
LiveRange::Segments &segmentsColl() { return LR->segments; }
void insertAtEnd(const Segment &S) { LR->segments.push_back(S); }
iterator find(SlotIndex Pos) { return LR->find(Pos); }
iterator findInsertPos(Segment S) {
return std::upper_bound(LR->begin(), LR->end(), S.start);
}
};
//===----------------------------------------------------------------------===//
// Instantiation of the methods for calculation of live ranges
// based on a segment set.
//===----------------------------------------------------------------------===//
class CalcLiveRangeUtilSet;
typedef CalcLiveRangeUtilBase<CalcLiveRangeUtilSet,
LiveRange::SegmentSet::iterator,
LiveRange::SegmentSet> CalcLiveRangeUtilSetBase;
class CalcLiveRangeUtilSet : public CalcLiveRangeUtilSetBase {
public:
CalcLiveRangeUtilSet(LiveRange *LR) : CalcLiveRangeUtilSetBase(LR) {}
private:
friend CalcLiveRangeUtilSetBase;
LiveRange::SegmentSet &segmentsColl() { return *LR->segmentSet; }
void insertAtEnd(const Segment &S) {
LR->segmentSet->insert(LR->segmentSet->end(), S);
}
iterator find(SlotIndex Pos) {
iterator I =
LR->segmentSet->upper_bound(Segment(Pos, Pos.getNextSlot(), nullptr));
if (I == LR->segmentSet->begin())
return I;
iterator PrevI = std::prev(I);
if (Pos < (*PrevI).end)
return PrevI;
return I;
}
iterator findInsertPos(Segment S) {
iterator I = LR->segmentSet->upper_bound(S);
if (I != LR->segmentSet->end() && !(S.start < *I))
++I;
return I;
}
};
//===----------------------------------------------------------------------===//
// LiveRange methods
//===----------------------------------------------------------------------===//
LiveRange::iterator LiveRange::find(SlotIndex Pos) {
// This algorithm is basically std::upper_bound.
// Unfortunately, std::upper_bound cannot be used with mixed types until we
@ -52,30 +318,11 @@ LiveRange::iterator LiveRange::find(SlotIndex Pos) {
VNInfo *LiveRange::createDeadDef(SlotIndex Def,
VNInfo::Allocator &VNInfoAllocator) {
assert(!Def.isDead() && "Cannot define a value at the dead slot");
iterator I = find(Def);
if (I == end()) {
VNInfo *VNI = getNextValue(Def, VNInfoAllocator);
segments.push_back(Segment(Def, Def.getDeadSlot(), VNI));
return VNI;
}
if (SlotIndex::isSameInstr(Def, I->start)) {
assert(I->valno->def == I->start && "Inconsistent existing value def");
// It is possible to have both normal and early-clobber defs of the same
// register on an instruction. It doesn't make a lot of sense, but it is
// possible to specify in inline assembly.
//
// Just convert everything to early-clobber.
Def = std::min(Def, I->start);
if (Def != I->start)
I->start = I->valno->def = Def;
return I->valno;
}
assert(SlotIndex::isEarlierInstr(Def, I->start) && "Already live at def");
VNInfo *VNI = getNextValue(Def, VNInfoAllocator);
segments.insert(I, Segment(Def, Def.getDeadSlot(), VNI));
return VNI;
// Use the segment set, if it is available.
if (segmentSet != nullptr)
return CalcLiveRangeUtilSet(this).createDeadDef(Def, VNInfoAllocator);
// Otherwise use the segment vector.
return CalcLiveRangeUtilVector(this).createDeadDef(Def, VNInfoAllocator);
}
// overlaps - Return true if the intersection of the two live ranges is
@ -236,68 +483,18 @@ void LiveRange::RenumberValues() {
}
}
/// This method is used when we want to extend the segment specified by I to end
/// at the specified endpoint. To do this, we should merge and eliminate all
/// segments that this will overlap with. The iterator is not invalidated.
void LiveRange::extendSegmentEndTo(iterator I, SlotIndex NewEnd) {
assert(I != end() && "Not a valid segment!");
VNInfo *ValNo = I->valno;
// Search for the first segment that we can't merge with.
iterator MergeTo = std::next(I);
for (; MergeTo != end() && NewEnd >= MergeTo->end; ++MergeTo) {
assert(MergeTo->valno == ValNo && "Cannot merge with differing values!");
}
// If NewEnd was in the middle of a segment, make sure to get its endpoint.
I->end = std::max(NewEnd, std::prev(MergeTo)->end);
// If the newly formed segment now touches the segment after it and if they
// have the same value number, merge the two segments into one segment.
if (MergeTo != end() && MergeTo->start <= I->end &&
MergeTo->valno == ValNo) {
I->end = MergeTo->end;
++MergeTo;
}
// Erase any dead segments.
segments.erase(std::next(I), MergeTo);
void LiveRange::addSegmentToSet(Segment S) {
CalcLiveRangeUtilSet(this).addSegment(S);
}
/// This method is used when we want to extend the segment specified by I to
/// start at the specified endpoint. To do this, we should merge and eliminate
/// all segments that this will overlap with.
LiveRange::iterator
LiveRange::extendSegmentStartTo(iterator I, SlotIndex NewStart) {
assert(I != end() && "Not a valid segment!");
VNInfo *ValNo = I->valno;
// Search for the first segment that we can't merge with.
iterator MergeTo = I;
do {
if (MergeTo == begin()) {
I->start = NewStart;
segments.erase(MergeTo, I);
return I;
LiveRange::iterator LiveRange::addSegment(Segment S) {
// Use the segment set, if it is available.
if (segmentSet != nullptr) {
addSegmentToSet(S);
return end();
}
assert(MergeTo->valno == ValNo && "Cannot merge with differing values!");
--MergeTo;
} while (NewStart <= MergeTo->start);
// If we start in the middle of another segment, just delete a range and
// extend that segment.
if (MergeTo->end >= NewStart && MergeTo->valno == ValNo) {
MergeTo->end = I->end;
} else {
// Otherwise, extend the segment right after.
++MergeTo;
MergeTo->start = NewStart;
MergeTo->end = I->end;
}
segments.erase(std::next(MergeTo), std::next(I));
return MergeTo;
// Otherwise use the segment vector.
return CalcLiveRangeUtilVector(this).addSegment(S);
}
void LiveRange::append(const Segment S) {
@ -306,69 +503,15 @@ void LiveRange::append(const Segment S) {
segments.push_back(S);
}
LiveRange::iterator LiveRange::addSegmentFrom(Segment S, iterator From) {
SlotIndex Start = S.start, End = S.end;
iterator it = std::upper_bound(From, end(), Start);
// If the inserted segment starts in the middle or right at the end of
// another segment, just extend that segment to contain the segment of S.
if (it != begin()) {
iterator B = std::prev(it);
if (S.valno == B->valno) {
if (B->start <= Start && B->end >= Start) {
extendSegmentEndTo(B, End);
return B;
}
} else {
// Check to make sure that we are not overlapping two live segments with
// different valno's.
assert(B->end <= Start &&
"Cannot overlap two segments with differing ValID's"
" (did you def the same reg twice in a MachineInstr?)");
}
}
// Otherwise, if this segment ends in the middle of, or right next to, another
// segment, merge it into that segment.
if (it != end()) {
if (S.valno == it->valno) {
if (it->start <= End) {
it = extendSegmentStartTo(it, Start);
// If S is a complete superset of a segment, we may need to grow its
// endpoint as well.
if (End > it->end)
extendSegmentEndTo(it, End);
return it;
}
} else {
// Check to make sure that we are not overlapping two live segments with
// different valno's.
assert(it->start >= End &&
"Cannot overlap two segments with differing ValID's");
}
}
// Otherwise, this is just a new segment that doesn't interact with anything.
// Insert it.
return segments.insert(it, S);
}
/// extendInBlock - If this range is live before Kill in the basic
/// block that starts at StartIdx, extend it to be live up to Kill and return
/// the value. If there is no live range before Kill, return NULL.
VNInfo *LiveRange::extendInBlock(SlotIndex StartIdx, SlotIndex Kill) {
if (empty())
return nullptr;
iterator I = std::upper_bound(begin(), end(), Kill.getPrevSlot());
if (I == begin())
return nullptr;
--I;
if (I->end <= StartIdx)
return nullptr;
if (I->end < Kill)
extendSegmentEndTo(I, Kill);
return I->valno;
// Use the segment set, if it is available.
if (segmentSet != nullptr)
return CalcLiveRangeUtilSet(this).extendInBlock(StartIdx, Kill);
// Otherwise use the segment vector.
return CalcLiveRangeUtilVector(this).extendInBlock(StartIdx, Kill);
}
/// Remove the specified segment from this range. Note that the segment must
@ -598,6 +741,17 @@ VNInfo *LiveRange::MergeValueNumberInto(VNInfo *V1, VNInfo *V2) {
return V2;
}
void LiveRange::flushSegmentSet() {
assert(segmentSet != nullptr && "segment set must have been created");
assert(
segments.empty() &&
"segment set can be used only initially before switching to the array");
segments.append(segmentSet->begin(), segmentSet->end());
delete segmentSet;
segmentSet = nullptr;
verify();
}
void LiveInterval::freeSubRange(SubRange *S) {
S->~SubRange();
// Memory was allocated with BumpPtr allocator and is not freed here.
@ -1012,6 +1166,13 @@ static inline bool coalescable(const LiveRange::Segment &A,
void LiveRangeUpdater::add(LiveRange::Segment Seg) {
assert(LR && "Cannot add to a null destination");
// Fall back to the regular add method if the live range
// is using the segment set instead of the segment vector.
if (LR->segmentSet != nullptr) {
LR->addSegmentToSet(Seg);
return;
}
// Flush the state if Start moves backwards.
if (!LastStart.isValid() || LastStart > Seg.start) {
if (isDirty())

View File

@ -67,6 +67,13 @@ static cl::opt<bool> EnableSubRegLiveness(
"enable-subreg-liveness", cl::Hidden, cl::init(true),
cl::desc("Enable subregister liveness tracking."));
namespace llvm {
cl::opt<bool> UseSegmentSetForPhysRegs(
"use-segment-set-for-physregs", cl::Hidden, cl::init(true),
cl::desc(
"Use segment set for the computation of the live ranges of physregs."));
}
void LiveIntervals::getAnalysisUsage(AnalysisUsage &AU) const {
AU.setPreservesCFG();
AU.addRequired<AliasAnalysis>();
@ -268,6 +275,10 @@ void LiveIntervals::computeRegUnitRange(LiveRange &LR, unsigned Unit) {
LRCalc->extendToUses(LR, Reg);
}
}
// Flush the segment set to the segment vector.
if (UseSegmentSetForPhysRegs)
LR.flushSegmentSet();
}
@ -300,7 +311,8 @@ void LiveIntervals::computeLiveInRegUnits() {
unsigned Unit = *Units;
LiveRange *LR = RegUnitRanges[Unit];
if (!LR) {
LR = RegUnitRanges[Unit] = new LiveRange();
// Use segment set to speed-up initial computation of the live range.
LR = RegUnitRanges[Unit] = new LiveRange(UseSegmentSetForPhysRegs);
NewRanges.push_back(Unit);
}
VNInfo *VNI = LR->createDeadDef(Begin, getVNInfoAllocator());