Index: src/IceCfgNode.cpp |
diff --git a/src/IceCfgNode.cpp b/src/IceCfgNode.cpp |
index 19c5d22d731879dff9ab258632028102dc234317..f69d3c1a5e22a632397e3d275e900505c92b74e1 100644 |
--- a/src/IceCfgNode.cpp |
+++ b/src/IceCfgNode.cpp |
@@ -828,21 +828,26 @@ void emitLiveRangesEnded(Ostream &Str, const Cfg *Func, const Inst *Instr, |
return; |
bool First = true; |
Variable *Dest = Instr->getDest(); |
- if (Dest && Dest->hasReg()) |
+ // Normally we increment the live count for the dest register. But |
+ // we shouldn't if the instruction's IsDestNonKillable flag is set, |
+ // because this means that the target lowering created this |
+ // instruction as a non-SSA assignment; i.e., a different, previous |
+ // instruction started the dest variable's live range. |
+ if (!Instr->isDestNonKillable() && Dest && Dest->hasReg()) |
++LiveRegCount[Dest->getRegNum()]; |
for (SizeT I = 0; I < Instr->getSrcSize(); ++I) { |
Operand *Src = Instr->getSrc(I); |
SizeT NumVars = Src->getNumVars(); |
for (SizeT J = 0; J < NumVars; ++J) { |
const Variable *Var = Src->getVar(J); |
- bool ShouldEmit = Instr->isLastUse(Var); |
- if (Var->hasReg()) { |
+ bool ShouldReport = Instr->isLastUse(Var); |
+ if (ShouldReport && Var->hasReg()) { |
// Don't report end of live range until the live count reaches 0. |
SizeT NewCount = --LiveRegCount[Var->getRegNum()]; |
if (NewCount) |
- ShouldEmit = false; |
+ ShouldReport = false; |
} |
- if (ShouldEmit) { |
+ if (ShouldReport) { |
if (First) |
Str << " \t# END="; |
else |
@@ -885,6 +890,10 @@ void CfgNode::emit(Cfg *Func) const { |
bool DecorateAsm = |
Liveness && Func->getContext()->getFlags().getDecorateAsm(); |
Str << getAsmName() << ":\n"; |
+ // LiveRegCount keeps track of the number of currently live |
+ // variables that each register is assigned to. Normally that would |
+ // be only 0 or 1, but the register allocator's AllowOverlap |
+ // inference allows it to be greater than 1 for short periods. |
std::vector<SizeT> LiveRegCount(Func->getTarget()->getNumRegisters()); |
if (DecorateAsm) { |
const bool IsLiveIn = true; |
@@ -900,8 +909,21 @@ void CfgNode::emit(Cfg *Func) const { |
for (const Inst &I : Insts) { |
if (I.isDeleted()) |
continue; |
- if (I.isRedundantAssign()) |
+ if (I.isRedundantAssign()) { |
+ // Usually, redundant assignments end the live range of the src |
+ // variable and begin the live range of the dest variable, with |
+ // no net effect on the liveness of their register. However, if |
+ // the register allocator infers the AllowOverlap condition, |
+ // then this may be a redundant assignment that does not end the |
+ // src variable's live range, in which case the active variable |
+ // count for that register needs to be bumped. That normally |
+ // would have happened as part of emitLiveRangesEnded(), but |
+ // that isn't called for redundant assignments. |
+ Variable *Dest = I.getDest(); |
+ if (DecorateAsm && Dest->hasReg() && !I.isLastUse(I.getSrc(0))) |
+ ++LiveRegCount[Dest->getRegNum()]; |
continue; |
+ } |
I.emit(Func); |
if (DecorateAsm) |
emitLiveRangesEnded(Str, Func, &I, LiveRegCount); |