Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: show profile data for dot flowgraph dumps #42657

Merged
merged 2 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions src/coreclr/src/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4026,29 +4026,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 @@ -4061,11 +4071,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 @@ -4079,7 +4089,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 @@ -9043,7 +9043,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 @@ -20199,16 +20199,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 @@ -20356,8 +20361,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 @@ -20418,24 +20424,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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I realize that it may not always be the best layout, I find it so much easier to reason about these graphs when the nodes are in sequential order by bbNum. IIRC this was an imperfect mechanism for doing so, but it mostly worked.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you'd like to see a spine of blocks in increasing bbNum order? I can add the invisible edges back.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that would be my preference, unless there's consensus against it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Internal blocks like a loop preheader block have a high number at the are allocated as the highest block number. (Until we renumber the blocks)

Copy link
Contributor

@briansull briansull Sep 29, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference is to have the fall through block always be a short straight line down to the next block:

block A  ------ taken branch
     |
     |
block B  ------- taken branch

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can do invisible edges to encourage bbNext order, that will give bbNum order if you've recently renumbered, and bbNext order otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds great to me.

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 @@ -20482,104 +20501,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