Commit e5b73e6e by Jim Stichnoth

Subzero: Clean up live range construction.

Moves the deletion of newly dead instructions into the main liveness() routine. The old livenessPostProcess() routine is renamed and now used purely for live range construction. The hack is removed in which live in-args have a custom live range segment added to avoid an artifact of the live ranges. It is replaced with a gentler hack that extends the instruction numbering range of the initial basic block to avoid the artifact. Since special live range segments no longer need to be prepended, the live range representation is simplified and we can always assume that segments are being appended, never prepended (and as before, never added to the middle). Some magic constants involving special instruction numbers are replaced with symbolic constants. BUG= none R=jvoung@chromium.org Review URL: https://codereview.chromium.org/802003003
parent 3281748c
...@@ -26,7 +26,8 @@ namespace Ice { ...@@ -26,7 +26,8 @@ namespace Ice {
Cfg::Cfg(GlobalContext *Ctx) Cfg::Cfg(GlobalContext *Ctx)
: Ctx(Ctx), FunctionName(""), ReturnType(IceType_void), : Ctx(Ctx), FunctionName(""), ReturnType(IceType_void),
IsInternalLinkage(false), HasError(false), FocusedTiming(false), IsInternalLinkage(false), HasError(false), FocusedTiming(false),
ErrorMessage(""), Entry(NULL), NextInstNumber(1), Live(nullptr), ErrorMessage(""), Entry(NULL), NextInstNumber(Inst::NumberInitial),
Live(nullptr),
Target(TargetLowering::createLowering(Ctx->getTargetArch(), this)), Target(TargetLowering::createLowering(Ctx->getTargetArch(), this)),
VMetadata(new VariablesMetadata(this)), VMetadata(new VariablesMetadata(this)),
TargetAssembler( TargetAssembler(
...@@ -96,7 +97,7 @@ void Cfg::computePredecessors() { ...@@ -96,7 +97,7 @@ void Cfg::computePredecessors() {
void Cfg::renumberInstructions() { void Cfg::renumberInstructions() {
TimerMarker T(TimerStack::TT_renumberInstructions, this); TimerMarker T(TimerStack::TT_renumberInstructions, this);
NextInstNumber = 1; NextInstNumber = Inst::NumberInitial;
for (CfgNode *Node : Nodes) for (CfgNode *Node : Nodes)
Node->renumberInstructions(); Node->renumberInstructions();
} }
...@@ -256,7 +257,6 @@ void Cfg::liveness(LivenessMode Mode) { ...@@ -256,7 +257,6 @@ void Cfg::liveness(LivenessMode Mode) {
llvm::BitVector NeedToProcess(Nodes.size(), true); llvm::BitVector NeedToProcess(Nodes.size(), true);
while (NeedToProcess.any()) { while (NeedToProcess.any()) {
// Iterate in reverse topological order to speed up convergence. // Iterate in reverse topological order to speed up convergence.
// TODO(stichnot): Use llvm::make_range with LLVM 3.5.
for (auto I = Nodes.rbegin(), E = Nodes.rend(); I != E; ++I) { for (auto I = Nodes.rbegin(), E = Nodes.rend(); I != E; ++I) {
CfgNode *Node = *I; CfgNode *Node = *I;
if (NeedToProcess[Node->getIndex()]) { if (NeedToProcess[Node->getIndex()]) {
...@@ -276,38 +276,45 @@ void Cfg::liveness(LivenessMode Mode) { ...@@ -276,38 +276,45 @@ void Cfg::liveness(LivenessMode Mode) {
for (Variable *Var : Variables) for (Variable *Var : Variables)
Var->resetLiveRange(); Var->resetLiveRange();
} }
// Collect timing for just the portion that constructs the live // Make a final pass over each node to delete dead instructions,
// range intervals based on the end-of-live-range computation, for a // collect the first and last instruction numbers, and add live
// finer breakdown of the cost. // range segments for that node.
TimerMarker T1(TimerStack::TT_liveRange, this); for (CfgNode *Node : Nodes) {
// Make a final pass over instructions to delete dead instructions InstNumberT FirstInstNum = Inst::NumberSentinel;
// and build each Variable's live range. InstNumberT LastInstNum = Inst::NumberSentinel;
for (CfgNode *Node : Nodes) for (auto I = Node->getPhis().begin(), E = Node->getPhis().end(); I != E;
Node->livenessPostprocess(Mode, getLiveness()); ++I) {
if (Mode == Liveness_Intervals) { I->deleteIfDead();
// Special treatment for live in-args. Their liveness needs to if (Mode == Liveness_Intervals && !I->isDeleted()) {
// extend beyond the beginning of the function, otherwise an arg if (FirstInstNum == Inst::NumberSentinel)
// whose only use is in the first instruction will end up having FirstInstNum = I->getNumber();
// the trivial live range [1,1) and will *not* interfere with assert(I->getNumber() > LastInstNum);
// other arguments. So if the first instruction of the method is LastInstNum = I->getNumber();
// "r=arg1+arg2", both args may be assigned the same register.
for (SizeT I = 0; I < Args.size(); ++I) {
Variable *Arg = Args[I];
if (!Arg->getLiveRange().isEmpty()) {
// Add live range [-1,0) with weight 0. TODO: Here and below,
// use better symbolic constants along the lines of
// Inst::NumberDeleted and Inst::NumberSentinel instead of -1
// and 0.
Arg->addLiveRange(-1, 0, 0);
} }
// Do the same for i64 args that may have been lowered into i32 }
// Lo and Hi components. for (auto I = Node->getInsts().begin(), E = Node->getInsts().end(); I != E;
Variable *Lo = Arg->getLo(); ++I) {
if (Lo && !Lo->getLiveRange().isEmpty()) I->deleteIfDead();
Lo->addLiveRange(-1, 0, 0); if (Mode == Liveness_Intervals && !I->isDeleted()) {
Variable *Hi = Arg->getHi(); if (FirstInstNum == Inst::NumberSentinel)
if (Hi && !Hi->getLiveRange().isEmpty()) FirstInstNum = I->getNumber();
Hi->addLiveRange(-1, 0, 0); assert(I->getNumber() > LastInstNum);
LastInstNum = I->getNumber();
}
}
if (Mode == Liveness_Intervals) {
// Special treatment for live in-args. Their liveness needs to
// extend beyond the beginning of the function, otherwise an arg
// whose only use is in the first instruction will end up having
// the trivial live range [2,2) and will *not* interfere with
// other arguments. So if the first instruction of the method
// is "r=arg1+arg2", both args may be assigned the same
// register. This is accomplished by extending the entry
// block's instruction range from [2,n) to [1,n) which will
// transform the problematic [2,2) live ranges into [1,2).
if (FirstInstNum == Inst::NumberInitial)
FirstInstNum = Inst::NumberExtended;
Node->livenessAddIntervals(getLiveness(), FirstInstNum, LastInstNum);
} }
} }
} }
......
...@@ -639,40 +639,16 @@ bool CfgNode::liveness(Liveness *Liveness) { ...@@ -639,40 +639,16 @@ bool CfgNode::liveness(Liveness *Liveness) {
return Changed; return Changed;
} }
// Now that basic liveness is complete, remove dead instructions that // Once basic liveness is complete, compute actual live ranges. It is
// were tentatively marked as dead, and compute actual live ranges. // assumed that within a single basic block, a live range begins at
// It is assumed that within a single basic block, a live range begins // most once and ends at most once. This is certainly true for pure
// at most once and ends at most once. This is certainly true for // SSA form. It is also true once phis are lowered, since each
// pure SSA form. It is also true once phis are lowered, since each
// assignment to the phi-based temporary is in a different basic // assignment to the phi-based temporary is in a different basic
// block, and there is a single read that ends the live in the basic // block, and there is a single read that ends the live in the basic
// block that contained the actual phi instruction. // block that contained the actual phi instruction.
void CfgNode::livenessPostprocess(LivenessMode Mode, Liveness *Liveness) { void CfgNode::livenessAddIntervals(Liveness *Liveness, InstNumberT FirstInstNum,
InstNumberT FirstInstNum = Inst::NumberSentinel; InstNumberT LastInstNum) {
InstNumberT LastInstNum = Inst::NumberSentinel; TimerMarker T1(TimerStack::TT_liveRange, Func);
// Process phis in any order. Process only Dest operands.
for (auto I = Phis.begin(), E = Phis.end(); I != E; ++I) {
I->deleteIfDead();
if (I->isDeleted())
continue;
if (FirstInstNum == Inst::NumberSentinel)
FirstInstNum = I->getNumber();
assert(I->getNumber() > LastInstNum);
LastInstNum = I->getNumber();
}
// Process instructions
for (auto I = Insts.begin(), E = Insts.end(); I != E; ++I) {
I->deleteIfDead();
if (I->isDeleted())
continue;
if (FirstInstNum == Inst::NumberSentinel)
FirstInstNum = I->getNumber();
assert(I->getNumber() > LastInstNum);
LastInstNum = I->getNumber();
}
if (Mode != Liveness_Intervals)
return;
TimerMarker T1(TimerStack::TT_liveRangeCtor, Func);
SizeT NumVars = Liveness->getNumVarsInNode(this); SizeT NumVars = Liveness->getNumVarsInNode(this);
LivenessBV &LiveIn = Liveness->getLiveIn(this); LivenessBV &LiveIn = Liveness->getLiveIn(this);
......
...@@ -80,7 +80,8 @@ public: ...@@ -80,7 +80,8 @@ public:
void genCode(); void genCode();
void livenessLightweight(); void livenessLightweight();
bool liveness(Liveness *Liveness); bool liveness(Liveness *Liveness);
void livenessPostprocess(LivenessMode Mode, Liveness *Liveness); void livenessAddIntervals(Liveness *Liveness, InstNumberT FirstInstNum,
InstNumberT LastInstNum);
void contractIfEmpty(); void contractIfEmpty();
void doBranchOpt(const CfgNode *NextNode); void doBranchOpt(const CfgNode *NextNode);
void emit(Cfg *Func) const; void emit(Cfg *Func) const;
......
...@@ -69,7 +69,12 @@ public: ...@@ -69,7 +69,12 @@ public:
InstNumberT getNumber() const { return Number; } InstNumberT getNumber() const { return Number; }
void renumber(Cfg *Func); void renumber(Cfg *Func);
enum { NumberDeleted = -1, NumberSentinel = 0 }; enum {
NumberDeleted = -1,
NumberSentinel = 0,
NumberInitial = 2,
NumberExtended = NumberInitial - 1
};
bool isDeleted() const { return Deleted; } bool isDeleted() const { return Deleted; }
void setDeleted() { Deleted = true; } void setDeleted() { Deleted = true; }
......
...@@ -33,25 +33,14 @@ bool operator==(const RegWeight &A, const RegWeight &B) { ...@@ -33,25 +33,14 @@ bool operator==(const RegWeight &A, const RegWeight &B) {
} }
void LiveRange::addSegment(InstNumberT Start, InstNumberT End) { void LiveRange::addSegment(InstNumberT Start, InstNumberT End) {
if (Range.empty()) { if (!Range.empty()) {
Range.push_back(RangeElementType(Start, End)); // Check for merge opportunity.
return; InstNumberT CurrentEnd = Range.back().second;
} assert(Start >= CurrentEnd);
// Special case for faking in-arg liveness. if (Start == CurrentEnd) {
if (End < Range.front().first) { Range.back().second = End;
assert(Start < 0); return;
// This is inefficient with Range as a std::vector, but there are }
// generally very few arguments compared to the total number of
// variables with non-empty live ranges.
Range.insert(Range.begin(), RangeElementType(Start, End));
return;
}
InstNumberT CurrentEnd = Range.back().second;
assert(Start >= CurrentEnd);
// Check for merge opportunity.
if (Start == CurrentEnd) {
Range.back().second = End;
return;
} }
Range.push_back(RangeElementType(Start, End)); Range.push_back(RangeElementType(Start, End));
} }
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
X(initUnhandled) \ X(initUnhandled) \
X(linearScan) \ X(linearScan) \
X(liveRange) \ X(liveRange) \
X(liveRangeCtor) \
X(liveness) \ X(liveness) \
X(livenessLightweight) \ X(livenessLightweight) \
X(llvmConvert) \ X(llvmConvert) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment