Commit 3d44fe8c by Jim Stichnoth

Subzero: Decorate the text asm output with register availability info.

The -asm-verbose flag adds comments to the text asm output about register availability. Specifically, it prints the registers in use at the beginning and end of each block, and it prints which registers' live ranges end at each instruction. This is extremely helpful when studying the output to find opportunities to improve the code quality. BUG= none R=jvoung@chromium.org Review URL: https://codereview.chromium.org/682983004
parent 8835576b
...@@ -365,20 +365,15 @@ bool Cfg::validateLiveness() const { ...@@ -365,20 +365,15 @@ bool Cfg::validateLiveness() const {
return Valid; return Valid;
} }
// Deletes redundant assignments like "var=var". This includes
// architecturally redundant moves like "var1:eax=var2:eax". As such,
// this needs to be done very late in the translation to avoid
// liveness inconsistencies.
void Cfg::deleteRedundantAssignments() {
for (CfgNode *Node : Nodes) {
// Ignore Phi instructions.
for (Inst *I : Node->getInsts())
if (I->isRedundantAssign())
I->setDeleted();
}
}
void Cfg::contractEmptyNodes() { void Cfg::contractEmptyNodes() {
// If we're decorating the asm output with register liveness info,
// this information may become corrupted or incorrect after
// contracting nodes that contain only redundant assignments. As
// such, we disable this pass when DecorateAsm is specified. This
// may make the resulting code look more branchy, but it should have
// no effect on the register assignments.
if (Ctx->getFlags().DecorateAsm)
return;
for (CfgNode *Node : Nodes) { for (CfgNode *Node : Nodes) {
Node->contractIfEmpty(); Node->contractIfEmpty();
} }
...@@ -397,6 +392,12 @@ void Cfg::doBranchOpt() { ...@@ -397,6 +392,12 @@ void Cfg::doBranchOpt() {
void Cfg::emit() { void Cfg::emit() {
TimerMarker T(TimerStack::TT_emit, this); TimerMarker T(TimerStack::TT_emit, this);
if (Ctx->getFlags().DecorateAsm) {
renumberInstructions();
getVMetadata()->init(VMK_Uses);
liveness(Liveness_Basic);
dump("After recomputing liveness for -decorate-asm");
}
Ostream &Str = Ctx->getStrEmit(); Ostream &Str = Ctx->getStrEmit();
if (!Ctx->testAndSetHasEmittedFirstMethod()) { if (!Ctx->testAndSetHasEmittedFirstMethod()) {
// Print a helpful command for assembling the output. // Print a helpful command for assembling the output.
......
...@@ -121,7 +121,6 @@ public: ...@@ -121,7 +121,6 @@ public:
void livenessLightweight(); void livenessLightweight();
void liveness(LivenessMode Mode); void liveness(LivenessMode Mode);
bool validateLiveness() const; bool validateLiveness() const;
void deleteRedundantAssignments();
void contractEmptyNodes(); void contractEmptyNodes();
void doBranchOpt(); void doBranchOpt();
......
...@@ -756,9 +756,12 @@ void CfgNode::contractIfEmpty() { ...@@ -756,9 +756,12 @@ void CfgNode::contractIfEmpty() {
return; return;
Inst *Branch = NULL; Inst *Branch = NULL;
for (Inst *I : Insts) { for (Inst *I : Insts) {
if (!I->isDeleted() && !I->isUnconditionalBranch()) if (I->isDeleted())
return; continue;
if (I->isUnconditionalBranch())
Branch = I; Branch = I;
else if (!I->isRedundantAssign())
return;
} }
Branch->setDeleted(); Branch->setDeleted();
assert(OutEdges.size() == 1); assert(OutEdges.size() == 1);
...@@ -798,9 +801,73 @@ void CfgNode::doBranchOpt(const CfgNode *NextNode) { ...@@ -798,9 +801,73 @@ void CfgNode::doBranchOpt(const CfgNode *NextNode) {
// ======================== Dump routines ======================== // // ======================== Dump routines ======================== //
namespace {
// Helper functions for emit().
void emitRegisterUsage(Ostream &Str, const Cfg *Func, const CfgNode *Node,
bool IsLiveIn, std::vector<SizeT> &LiveRegCount) {
Liveness *Liveness = Func->getLiveness();
const LivenessBV *Live;
if (IsLiveIn) {
Live = &Liveness->getLiveIn(Node);
Str << "\t\t\t\t# LiveIn=";
} else {
Live = &Liveness->getLiveOut(Node);
Str << "\t\t\t\t# LiveOut=";
}
if (!Live->empty()) {
bool First = true;
for (SizeT i = 0; i < Live->size(); ++i) {
if ((*Live)[i]) {
Variable *Var = Liveness->getVariable(i, Node);
if (Var->hasReg()) {
if (IsLiveIn)
++LiveRegCount[Var->getRegNum()];
if (!First)
Str << ",";
First = false;
Var->emit(Func);
}
}
}
}
Str << "\n";
}
void emitLiveRangesEnded(Ostream &Str, const Cfg *Func, const Inst *Instr,
std::vector<SizeT> &LiveRegCount) {
bool First = true;
Variable *Dest = Instr->getDest();
if (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);
if (Var->hasReg()) {
if (Instr->isLastUse(Var) &&
--LiveRegCount[Var->getRegNum()] == 0) {
if (First)
Str << " \t# END=";
else
Str << ",";
Var->emit(Func);
First = false;
}
}
}
}
}
} // end of anonymous namespace
void CfgNode::emit(Cfg *Func) const { void CfgNode::emit(Cfg *Func) const {
Func->setCurrentNode(this); Func->setCurrentNode(this);
Ostream &Str = Func->getContext()->getStrEmit(); Ostream &Str = Func->getContext()->getStrEmit();
Liveness *Liveness = Func->getLiveness();
bool DecorateAsm = Liveness && Func->getContext()->getFlags().DecorateAsm;
if (Func->getEntryNode() == this) { if (Func->getEntryNode() == this) {
Str << Func->getContext()->mangleName(Func->getFunctionName()) << ":\n"; Str << Func->getContext()->mangleName(Func->getFunctionName()) << ":\n";
} }
...@@ -809,6 +876,10 @@ void CfgNode::emit(Cfg *Func) const { ...@@ -809,6 +876,10 @@ void CfgNode::emit(Cfg *Func) const {
Assembler *Asm = Func->getAssembler<Assembler>(); Assembler *Asm = Func->getAssembler<Assembler>();
Asm->BindCfgNodeLabel(getIndex()); Asm->BindCfgNodeLabel(getIndex());
} }
std::vector<SizeT> LiveRegCount(Func->getTarget()->getNumRegisters());
if (DecorateAsm)
emitRegisterUsage(Str, Func, this, true, LiveRegCount);
for (InstPhi *Phi : Phis) { for (InstPhi *Phi : Phis) {
if (Phi->isDeleted()) if (Phi->isDeleted())
continue; continue;
...@@ -819,10 +890,18 @@ void CfgNode::emit(Cfg *Func) const { ...@@ -819,10 +890,18 @@ void CfgNode::emit(Cfg *Func) const {
for (Inst *I : Insts) { for (Inst *I : Insts) {
if (I->isDeleted()) if (I->isDeleted())
continue; continue;
if (I->isRedundantAssign()) {
Variable *Dest = I->getDest();
if (DecorateAsm && Dest->hasReg() && !I->isLastUse(I->getSrc(0)))
++LiveRegCount[Dest->getRegNum()];
continue;
}
if (Func->useIntegratedAssembler()) { if (Func->useIntegratedAssembler()) {
I->emitIAS(Func); I->emitIAS(Func);
} else { } else {
I->emit(Func); I->emit(Func);
if (DecorateAsm)
emitLiveRangesEnded(Str, Func, I, LiveRegCount);
Str << "\n"; Str << "\n";
} }
// Update emitted instruction count, plus fill/spill count for // Update emitted instruction count, plus fill/spill count for
...@@ -841,6 +920,8 @@ void CfgNode::emit(Cfg *Func) const { ...@@ -841,6 +920,8 @@ void CfgNode::emit(Cfg *Func) const {
} }
} }
} }
if (DecorateAsm)
emitRegisterUsage(Str, Func, this, false, LiveRegCount);
} }
void CfgNode::dump(Cfg *Func) const { void CfgNode::dump(Cfg *Func) const {
......
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
namespace Ice { namespace Ice {
// TODO(stichnot) Move more command line flags into ClFlags.
class ClFlags { class ClFlags {
public: public:
ClFlags() ClFlags()
: DisableInternal(false), SubzeroTimingEnabled(false), : DisableInternal(false), SubzeroTimingEnabled(false),
DisableTranslation(false), FunctionSections(false), DataSections(false), DisableTranslation(false), FunctionSections(false), DataSections(false),
UseIntegratedAssembler(false), UseSandboxing(false), DumpStats(false), UseIntegratedAssembler(false), UseSandboxing(false),
PhiEdgeSplit(false), DecorateAsm(false), DumpStats(false),
AllowUninitializedGlobals(false), TimeEachFunction(false), AllowUninitializedGlobals(false), TimeEachFunction(false),
DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""), DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
VerboseFocusOn(""), TranslateOnly("") {} VerboseFocusOn(""), TranslateOnly("") {}
...@@ -36,6 +36,7 @@ public: ...@@ -36,6 +36,7 @@ public:
bool UseIntegratedAssembler; bool UseIntegratedAssembler;
bool UseSandboxing; bool UseSandboxing;
bool PhiEdgeSplit; bool PhiEdgeSplit;
bool DecorateAsm;
bool DumpStats; bool DumpStats;
bool AllowUninitializedGlobals; bool AllowUninitializedGlobals;
bool TimeEachFunction; bool TimeEachFunction;
......
...@@ -153,6 +153,7 @@ public: ...@@ -153,6 +153,7 @@ public:
return false; return false;
} }
virtual SizeT getNumRegisters() const = 0;
// Returns a variable pre-colored to the specified physical // Returns a variable pre-colored to the specified physical
// register. This is generally used to get very direct access to // register. This is generally used to get very direct access to
// the register such as in the prolog or epilog or for marking // the register such as in the prolog or epilog or for marking
......
...@@ -392,7 +392,6 @@ void TargetX8632::translateO2() { ...@@ -392,7 +392,6 @@ void TargetX8632::translateO2() {
return; return;
Func->dump("After stack frame mapping"); Func->dump("After stack frame mapping");
Func->deleteRedundantAssignments();
Func->contractEmptyNodes(); Func->contractEmptyNodes();
Func->reorderNodes(); Func->reorderNodes();
...@@ -435,8 +434,6 @@ void TargetX8632::translateOm1() { ...@@ -435,8 +434,6 @@ void TargetX8632::translateOm1() {
return; return;
Func->dump("After stack frame mapping"); Func->dump("After stack frame mapping");
Func->deleteRedundantAssignments();
// Nop insertion // Nop insertion
if (shouldDoNopInsertion()) { if (shouldDoNopInsertion()) {
Func->doNopInsertion(); Func->doNopInsertion();
......
...@@ -35,6 +35,7 @@ public: ...@@ -35,6 +35,7 @@ public:
void translateO2() override; void translateO2() override;
bool doBranchOpt(Inst *I, const CfgNode *NextNode) override; bool doBranchOpt(Inst *I, const CfgNode *NextNode) override;
SizeT getNumRegisters() const override { return RegX8632::Reg_NUM; }
Variable *getPhysicalRegister(SizeT RegNum, Type Ty = IceType_void) override; Variable *getPhysicalRegister(SizeT RegNum, Type Ty = IceType_void) override;
IceString getRegName(SizeT RegNum, Type Ty) const override; IceString getRegName(SizeT RegNum, Type Ty) const override;
llvm::SmallBitVector getRegisterSet(RegSetMask Include, llvm::SmallBitVector getRegisterSet(RegSetMask Include,
......
...@@ -120,6 +120,10 @@ EnablePhiEdgeSplit("phi-edge-split", ...@@ -120,6 +120,10 @@ EnablePhiEdgeSplit("phi-edge-split",
cl::desc("Enable edge splitting for Phi lowering"), cl::desc("Enable edge splitting for Phi lowering"),
cl::init(true)); cl::init(true));
static cl::opt<bool> DecorateAsm(
"asm-verbose",
cl::desc("Decorate textual asm output with register liveness info"));
static cl::opt<bool> static cl::opt<bool>
DumpStats("stats", DumpStats("stats",
cl::desc("Print statistics after translating each function")); cl::desc("Print statistics after translating each function"));
...@@ -256,6 +260,7 @@ int main(int argc, char **argv) { ...@@ -256,6 +260,7 @@ int main(int argc, char **argv) {
Flags.UseIntegratedAssembler = UseIntegratedAssembler; Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseSandboxing = UseSandboxing; Flags.UseSandboxing = UseSandboxing;
Flags.PhiEdgeSplit = EnablePhiEdgeSplit; Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
Flags.DecorateAsm = DecorateAsm;
Flags.DumpStats = DumpStats; Flags.DumpStats = DumpStats;
Flags.AllowUninitializedGlobals = AllowUninitializedGlobals; Flags.AllowUninitializedGlobals = AllowUninitializedGlobals;
Flags.TimeEachFunction = TimeEachFunction; Flags.TimeEachFunction = TimeEachFunction;
......
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