Skip to content

Commit

Permalink
JIT: show profile data for dot flowgraph dumps (#42657)
Browse files Browse the repository at this point in the history
Also, tweak layout a bit, and update so that if a method is jitted
multiple times the files don't overwrite one another. Instead a
suffix is added to indicate the kind of jit codegen.
  • Loading branch information
AndyAyersMS authored Sep 30, 2020
1 parent e82488d commit 1c4910f
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 95 deletions.
28 changes: 19 additions & 9 deletions src/coreclr/src/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4018,29 +4018,39 @@ bool Compiler::compRsvdRegCheck(FrameLayoutState curState)
// compGetTieringName: get a string describing tiered compilation settings
// for this method
//
// Arguments:
// wantShortName - true if a short name is ok (say for using in file names)
//
// Returns:
// String describing tiering decisions for this method, including cases
// where the jit codegen will differ from what the runtime requested.
//
const char* Compiler::compGetTieringName() const
const char* Compiler::compGetTieringName(bool wantShortName) const
{
bool tier0 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0);
bool tier1 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1);
const bool tier0 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0);
const bool tier1 = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER1);
assert(!tier0 || !tier1); // We don't expect multiple TIER flags to be set at one time.

if (tier0)
{
return "Tier-0";
return "Tier0";
}
else if (tier1)
{
return "Tier-1";
if (opts.jitFlags->IsSet(JitFlags::JIT_FLAG_OSR))
{
return "Tier1-OSR";
}
else
{
return "Tier1";
}
}
else if (opts.OptimizationEnabled())
{
if (compSwitchedToOptimized)
{
return "Tier-0 switched to FullOpts";
return wantShortName ? "Tier0-FullOpts" : "Tier-0 switched to FullOpts";
}
else
{
Expand All @@ -4053,11 +4063,11 @@ const char* Compiler::compGetTieringName() const
{
if (compSwitchedToOptimized)
{
return "Tier-0 switched to FullOpts, then to MinOpts";
return wantShortName ? "Tier0-FullOpts-MinOpts" : "Tier-0 switched to FullOpts, then to MinOpts";
}
else
{
return "Tier-1/FullOpts switched to MinOpts";
return wantShortName ? "Tier0-MinOpts" : "Tier-0 switched MinOpts";
}
}
else
Expand All @@ -4071,7 +4081,7 @@ const char* Compiler::compGetTieringName() const
}
else
{
return "Unknown optimization level";
return wantShortName ? "Unknown" : "Unknown optimization level";
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -9039,7 +9039,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#endif
}

const char* compGetTieringName() const;
const char* compGetTieringName(bool wantShortName = false) const;
const char* compGetStressMessage() const;

codeOptimize compCodeOpt()
Expand Down
241 changes: 156 additions & 85 deletions src/coreclr/src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19787,16 +19787,21 @@ FILE* Compiler::fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR typ

ONE_FILE_PER_METHOD:;

escapedString = fgProcessEscapes(info.compFullName, s_EscapeFileMapping);
size_t wCharCount = strlen(escapedString) + wcslen(phaseName) + 1 + strlen("~999") + wcslen(type) + 1;
escapedString = fgProcessEscapes(info.compFullName, s_EscapeFileMapping);

const char* tierName = compGetTieringName(true);
size_t wCharCount =
strlen(escapedString) + wcslen(phaseName) + 1 + strlen("~999") + wcslen(type) + strlen(tierName) + 1;
if (pathname != nullptr)
{
wCharCount += wcslen(pathname) + 1;
}
filename = (LPCWSTR)alloca(wCharCount * sizeof(WCHAR));

if (pathname != nullptr)
{
swprintf_s((LPWSTR)filename, wCharCount, W("%s\\%S-%s.%s"), pathname, escapedString, phaseName, type);
swprintf_s((LPWSTR)filename, wCharCount, W("%s\\%S-%s-%S.%s"), pathname, escapedString, phaseName, tierName,
type);
}
else
{
Expand Down Expand Up @@ -19944,8 +19949,9 @@ bool Compiler::fgDumpFlowGraph(Phases phase)

if (createDotFile)
{
fprintf(fgxFile, "digraph %s\n{\n", info.compMethodName);
fprintf(fgxFile, "/* Method %d, after phase %s */", Compiler::jitTotalMethodCompiled, PhaseNames[phase]);
fprintf(fgxFile, "digraph FlowGraph {\n");
fprintf(fgxFile, " graph [label = \"%s\\nafter\\n%s\"];\n", info.compMethodName, PhaseNames[phase]);
fprintf(fgxFile, " node [shape = \"Box\"];\n");
}
else
{
Expand Down Expand Up @@ -20006,24 +20012,37 @@ bool Compiler::fgDumpFlowGraph(Phases phase)
{
if (createDotFile)
{
// Add constraint edges to try to keep nodes ordered.
// It seems to work best if these edges are all created first.
switch (block->bbJumpKind)
fprintf(fgxFile, " BB%02u [label = \"BB%02u\\n\\n", block->bbNum, block->bbNum);

// "Raw" Profile weight
if (block->hasProfileWeight())
{
case BBJ_COND:
case BBJ_NONE:
assert(block->bbNext != nullptr);
fprintf(fgxFile, " " FMT_BB " -> " FMT_BB "\n", block->bbNum, block->bbNext->bbNum);
break;
default:
// These may or may not have an edge to the next block.
// Add a transparent edge to keep nodes ordered.
if (block->bbNext != nullptr)
{
fprintf(fgxFile, " " FMT_BB " -> " FMT_BB " [arrowtail=none,color=transparent]\n",
block->bbNum, block->bbNext->bbNum);
}
fprintf(fgxFile, "%7.2f", ((double)block->getBBWeight(this)) / BB_UNITY_WEIGHT);
}

// end of block label
fprintf(fgxFile, "\"");

// other node attributes
//
if (block == fgFirstBB)
{
fprintf(fgxFile, ", shape = \"house\"");
}
else if (block->bbJumpKind == BBJ_RETURN)
{
fprintf(fgxFile, ", shape = \"invhouse\"");
}
else if (block->bbJumpKind == BBJ_THROW)
{
fprintf(fgxFile, ", shape = \"trapezium\"");
}
else if (block->bbFlags & BBF_INTERNAL)
{
fprintf(fgxFile, ", shape = \"note\"");
}

fprintf(fgxFile, "];\n");
}
else
{
Expand Down Expand Up @@ -20070,104 +20089,156 @@ bool Compiler::fgDumpFlowGraph(Phases phase)
fprintf(fgxFile, ">");
}

unsigned edgeNum = 1;
BasicBlock* bTarget;
for (bTarget = fgFirstBB; bTarget != nullptr; bTarget = bTarget->bbNext)
if (fgComputePredsDone)
{
double targetWeightDivisor;
if (bTarget->bbWeight == BB_ZERO_WEIGHT)
{
targetWeightDivisor = 1.0;
}
else
unsigned edgeNum = 1;
BasicBlock* bTarget;
for (bTarget = fgFirstBB; bTarget != nullptr; bTarget = bTarget->bbNext)
{
targetWeightDivisor = (double)bTarget->bbWeight;
}

flowList* edge;
for (edge = bTarget->bbPreds; edge != nullptr; edge = edge->flNext, edgeNum++)
{
BasicBlock* bSource = edge->flBlock;
double sourceWeightDivisor;
if (bSource->bbWeight == BB_ZERO_WEIGHT)
double targetWeightDivisor;
if (bTarget->bbWeight == BB_ZERO_WEIGHT)
{
sourceWeightDivisor = 1.0;
targetWeightDivisor = 1.0;
}
else
{
sourceWeightDivisor = (double)bSource->bbWeight;
targetWeightDivisor = (double)bTarget->bbWeight;
}
if (createDotFile)

flowList* edge;
for (edge = bTarget->bbPreds; edge != nullptr; edge = edge->flNext, edgeNum++)
{
// Don't duplicate the edges we added above.
if ((bSource->bbNum == (bTarget->bbNum - 1)) &&
((bSource->bbJumpKind == BBJ_NONE) || (bSource->bbJumpKind == BBJ_COND)))
BasicBlock* bSource = edge->flBlock;
double sourceWeightDivisor;
if (bSource->bbWeight == BB_ZERO_WEIGHT)
{
continue;
}
fprintf(fgxFile, " " FMT_BB " -> " FMT_BB, bSource->bbNum, bTarget->bbNum);
if ((bSource->bbNum > bTarget->bbNum))
{
fprintf(fgxFile, "[arrowhead=normal,arrowtail=none,color=green]\n");
sourceWeightDivisor = 1.0;
}
else
{
fprintf(fgxFile, "\n");
sourceWeightDivisor = (double)bSource->bbWeight;
}
}
else
{
fprintf(fgxFile, "\n <edge");
fprintf(fgxFile, "\n id=\"%d\"", edgeNum);
fprintf(fgxFile, "\n source=\"%d\"", bSource->bbNum);
fprintf(fgxFile, "\n target=\"%d\"", bTarget->bbNum);
if (bSource->bbJumpKind == BBJ_SWITCH)
if (createDotFile)
{
if (edge->flDupCount >= 2)
fprintf(fgxFile, " " FMT_BB " -> " FMT_BB, bSource->bbNum, bTarget->bbNum);

if (bSource->bbNum > bTarget->bbNum)
{
fprintf(fgxFile, "\n switchCases=\"%d\"", edge->flDupCount);
// Lexical backedge
fprintf(fgxFile, " [color=green]\n");
}
if (bSource->bbJumpSwt->getDefault() == bTarget)
else if ((bSource->bbNum + 1) == bTarget->bbNum)
{
fprintf(fgxFile, "\n switchDefault=\"true\"");
// Lexical successor
fprintf(fgxFile, " [color=blue, weight=20]\n");
}
else
{
fprintf(fgxFile, ";\n");
}
}
if (validWeights)
else
{
unsigned edgeWeight = (edge->edgeWeightMin() + edge->edgeWeightMax()) / 2;
fprintf(fgxFile, "\n weight=");
fprintfDouble(fgxFile, ((double)edgeWeight) / weightDivisor);

if (edge->edgeWeightMin() != edge->edgeWeightMax())
fprintf(fgxFile, "\n <edge");
fprintf(fgxFile, "\n id=\"%d\"", edgeNum);
fprintf(fgxFile, "\n source=\"%d\"", bSource->bbNum);
fprintf(fgxFile, "\n target=\"%d\"", bTarget->bbNum);
if (bSource->bbJumpKind == BBJ_SWITCH)
{
fprintf(fgxFile, "\n minWeight=");
fprintfDouble(fgxFile, ((double)edge->edgeWeightMin()) / weightDivisor);
fprintf(fgxFile, "\n maxWeight=");
fprintfDouble(fgxFile, ((double)edge->edgeWeightMax()) / weightDivisor);
if (edge->flDupCount >= 2)
{
fprintf(fgxFile, "\n switchCases=\"%d\"", edge->flDupCount);
}
if (bSource->bbJumpSwt->getDefault() == bTarget)
{
fprintf(fgxFile, "\n switchDefault=\"true\"");
}
}

if (edgeWeight > 0)
if (validWeights)
{
if (edgeWeight < bSource->bbWeight)
unsigned edgeWeight = (edge->edgeWeightMin() + edge->edgeWeightMax()) / 2;
fprintf(fgxFile, "\n weight=");
fprintfDouble(fgxFile, ((double)edgeWeight) / weightDivisor);

if (edge->edgeWeightMin() != edge->edgeWeightMax())
{
fprintf(fgxFile, "\n out=");
fprintfDouble(fgxFile, ((double)edgeWeight) / sourceWeightDivisor);
fprintf(fgxFile, "\n minWeight=");
fprintfDouble(fgxFile, ((double)edge->edgeWeightMin()) / weightDivisor);
fprintf(fgxFile, "\n maxWeight=");
fprintfDouble(fgxFile, ((double)edge->edgeWeightMax()) / weightDivisor);
}
if (edgeWeight < bTarget->bbWeight)

if (edgeWeight > 0)
{
fprintf(fgxFile, "\n in=");
fprintfDouble(fgxFile, ((double)edgeWeight) / targetWeightDivisor);
if (edgeWeight < bSource->bbWeight)
{
fprintf(fgxFile, "\n out=");
fprintfDouble(fgxFile, ((double)edgeWeight) / sourceWeightDivisor);
}
if (edgeWeight < bTarget->bbWeight)
{
fprintf(fgxFile, "\n in=");
fprintfDouble(fgxFile, ((double)edgeWeight) / targetWeightDivisor);
}
}
}
}
if (!createDotFile)
{
fprintf(fgxFile, ">");
fprintf(fgxFile, "\n </edge>");
}
}
if (!createDotFile)
}
}

// For dot, show edges w/o pred lists, and add invisible bbNext links.
//
if (createDotFile)
{
for (BasicBlock* bSource = fgFirstBB; bSource != nullptr; bSource = bSource->bbNext)
{
// Invisible edge for bbNext chain
//
if (bSource->bbNext != nullptr)
{
fprintf(fgxFile, ">");
fprintf(fgxFile, "\n </edge>");
fprintf(fgxFile, " " FMT_BB " -> " FMT_BB " [style=\"invis\", weight=25];\n", bSource->bbNum,
bSource->bbNext->bbNum);
}

if (fgComputePredsDone)
{
// Already emitted pred edges above.
//
continue;
}

// Emit successor edges
//
const unsigned numSuccs = bSource->NumSucc();

for (unsigned i = 0; i < numSuccs; i++)
{
BasicBlock* const bTarget = bSource->GetSucc(i);
fprintf(fgxFile, " " FMT_BB " -> " FMT_BB, bSource->bbNum, bTarget->bbNum);
if (bSource->bbNum > bTarget->bbNum)
{
// Lexical backedge
fprintf(fgxFile, " [color=green]\n");
}
else if ((bSource->bbNum + 1) == bTarget->bbNum)
{
// Lexical successor
fprintf(fgxFile, " [color=blue]\n");
}
else
{
fprintf(fgxFile, ";\n");
}
}
}
}

if (createDotFile)
{
fprintf(fgxFile, "}\n");
Expand Down

0 comments on commit 1c4910f

Please sign in to comment.