Skip to content

[nfc][opts] Add a few pretty-stack traces to performance diagnostics. #67341

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

Merged
Merged
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
81 changes: 78 additions & 3 deletions lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,41 @@ using namespace swift;

namespace {

class PrettyStackTracePerformanceDiagnostics
: public llvm::PrettyStackTraceEntry {
const SILNode *node;
const char *action;

public:
PrettyStackTracePerformanceDiagnostics(const char *action, SILNodePointer node)
: node(node), action(action) {}

virtual void print(llvm::raw_ostream &OS) const override {
OS << "While " << action << " -- visiting node ";
node->print(OS);

if (auto inst = dyn_cast<SILInstruction>(node)) {
OS << "While visiting instruction in function ";
inst->getFunction()->print(OS);
}
}
};

class PrettyStackTraceSILGlobal
: public llvm::PrettyStackTraceEntry {
const SILGlobalVariable *node;
const char *action;

public:
PrettyStackTraceSILGlobal(const char *action, SILGlobalVariable *node)
: node(node), action(action) {}

virtual void print(llvm::raw_ostream &OS) const override {
OS << "While " << action << " -- visiting node ";
node->print(OS);
}
};

/// Issues performance diagnostics for functions which are annotated with
/// performance annotations, like @_noLocks, @_noAllocation.
///
Expand Down Expand Up @@ -156,6 +191,9 @@ bool PerformanceDiagnostics::visitFunction(SILFunction *function,
if (visitCallee(&inst, bca->getCalleeList(as), perfConstr, parentLoc))
return true;
} else if (auto *bi = dyn_cast<BuiltinInst>(&inst)) {
PrettyStackTracePerformanceDiagnostics stackTrace(
"visitFunction::BuiltinInst (once, once with context)", &inst);

switch (bi->getBuiltinInfo().ID) {
case BuiltinValueKind::Once:
case BuiltinValueKind::OnceWithContext:
Expand Down Expand Up @@ -226,6 +264,9 @@ bool PerformanceDiagnostics::checkClosureValue(SILValue closure,
// the call site.
return false;
} else {
PrettyStackTracePerformanceDiagnostics stackTrace(
"validating closure (function ref, callee) - unkown callee", callInst);

diagnose(LocWithParent(callInst->getLoc().getSourceLoc(), parentLoc), diag::performance_unknown_callees);
return true;
}
Expand All @@ -249,6 +290,8 @@ bool PerformanceDiagnostics::visitCallee(SILInstruction *callInst,
loc = parentLoc;

if (callees.isIncomplete()) {
PrettyStackTracePerformanceDiagnostics stackTrace("incomplete", callInst);

diagnose(*loc, diag::performance_unknown_callees);
return true;
}
Expand All @@ -263,6 +306,8 @@ bool PerformanceDiagnostics::visitCallee(SILInstruction *callInst,
return false;

if (!callee->isDefinition()) {
PrettyStackTracePerformanceDiagnostics stackTrace("incomplete", callInst);

diagnose(*loc, diag::performance_callee_unavailable);
return true;
}
Expand Down Expand Up @@ -310,6 +355,8 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
if (impact & RuntimeEffect::Casting) {
// TODO: be more specific on casting.
// E.g. distinguish locking and allocating dynamic casts, etc.
PrettyStackTracePerformanceDiagnostics stackTrace("bad cast", inst);

diagnose(loc, diag::performance_dynamic_casting);
return true;
}
Expand All @@ -319,21 +366,30 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,

// Try to give a good error message by looking which type of code it is.
switch (inst->getKind()) {
case SILInstructionKind::KeyPathInst:
case SILInstructionKind::KeyPathInst: {
PrettyStackTracePerformanceDiagnostics stackTrace("key path", inst);
diagnose(loc, diag::performance_metadata, "using KeyPath");
break;
}
case SILInstructionKind::AllocGlobalInst:
case SILInstructionKind::GlobalValueInst:
case SILInstructionKind::GlobalValueInst: {
PrettyStackTracePerformanceDiagnostics stackTrace(
"AllocGlobalInst | GlobalValueInst", inst);
diagnose(loc, diag::performance_metadata, "global or static variables");
break;
}
case SILInstructionKind::PartialApplyInst: {
PrettyStackTracePerformanceDiagnostics stackTrace(
"PartialApplyInst", inst);
diagnose(loc, diag::performance_metadata,
"generic closures or local functions");
break;
}
case SILInstructionKind::ApplyInst:
case SILInstructionKind::TryApplyInst:
case SILInstructionKind::BeginApplyInst: {
PrettyStackTracePerformanceDiagnostics stackTrace(
"ApplyInst | TryApplyInst | BeginApplyInst", inst);
diagnose(loc, diag::performance_metadata, "generic function calls");
break;
}
Expand All @@ -345,16 +401,23 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
// We didn't recognize the instruction, so try to give an error message
// based on the involved type.
if (impactType) {
PrettyStackTracePerformanceDiagnostics stackTrace(
"impactType (unrecognized instruction)", inst);
diagnose(loc, diag::performance_metadata_type, impactType.getASTType());
break;
}
// The default error message.
PrettyStackTracePerformanceDiagnostics stackTrace(
"default error (fallthrough, unkown inst)", inst);
diagnose(loc, diag::performance_metadata, "this code pattern");
break;
}
return true;
}
if (impact & RuntimeEffect::Allocating) {
PrettyStackTracePerformanceDiagnostics stackTrace(
"found allocation effect", inst);

switch (inst->getKind()) {
case SILInstructionKind::BeginApplyInst:
// Not all begin_applys necessarily allocate. But it's difficult to
Expand All @@ -375,6 +438,9 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
return true;
}
if (impact & RuntimeEffect::Deallocating) {
PrettyStackTracePerformanceDiagnostics stackTrace(
"found deallocation effect", inst);

if (impactType) {
switch (inst->getKind()) {
case SILInstructionKind::StoreInst:
Expand All @@ -395,6 +461,9 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
return true;
}
if (impact & RuntimeEffect::ObjectiveC) {
PrettyStackTracePerformanceDiagnostics stackTrace(
"found objc effect", inst);

diagnose(loc, diag::performance_objectivec);
return true;
}
Expand All @@ -403,6 +472,9 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
return false;

// Handle locking-only effects.

PrettyStackTracePerformanceDiagnostics stackTrace(
"found locking or ref counting effect", inst);

if (impact & RuntimeEffect::Locking) {
if (inst->getFunction()->isGlobalInit()) {
Expand Down Expand Up @@ -447,7 +519,7 @@ void PerformanceDiagnostics::checkNonAnnotatedFunction(SILFunction *function) {
}
}

void PerformanceDiagnostics::diagnose(LocWithParent loc, Diagnostic &&D) {
void PerformanceDiagnostics::diagnose(LocWithParent loc, Diagnostic &&D) {
// Start with a valid location in the call tree.
LocWithParent *validLoc = &loc;
while (!validLoc->loc.isValid() && validLoc->parent) {
Expand Down Expand Up @@ -484,6 +556,9 @@ class PerformanceDiagnosticsPass : public SILModuleTransform {
// Check that @_section, @_silgen_name is only on constant globals
for (SILGlobalVariable &g : module->getSILGlobals()) {
if (!g.getStaticInitializerValue() && g.mustBeInitializedStatically()) {
PrettyStackTraceSILGlobal stackTrace(
"global inst", &g);

auto *decl = g.getDecl();
if (g.getSectionAttr()) {
module->getASTContext().Diags.diagnose(
Expand Down