From 96afc1b00e06fd26d69e186c68db4e37671207d4 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 13 Oct 2025 21:03:11 -0400 Subject: [PATCH] Add methods to print various SIL things with a SILPrintContext. Use those methods to make the tests I added in #84811 work even in non-asserts builds, since apparently printID does not. --- include/swift/SIL/SILInstruction.h | 4 +- include/swift/SIL/SILModule.h | 2 +- include/swift/SIL/SILNode.h | 3 ++ lib/SIL/IR/SILPrinter.cpp | 20 +++++-- lib/SIL/Utils/BasicBlockUtils.cpp | 7 ++- lib/SIL/Verifier/SILVerifier.cpp | 87 +++++++++++++++--------------- 6 files changed, 70 insertions(+), 53 deletions(-) diff --git a/include/swift/SIL/SILInstruction.h b/include/swift/SIL/SILInstruction.h index 40618b7b580c6..78d1115b52d4b 100644 --- a/include/swift/SIL/SILInstruction.h +++ b/include/swift/SIL/SILInstruction.h @@ -914,6 +914,7 @@ class SILInstruction : public llvm::ilist_node { /// Pretty-print the value. void dump() const; void print(raw_ostream &OS) const; + void print(SILPrintContext &ctx) const; /// Pretty-print the value with DebugInfo. void dump(bool DebugInfo) const; @@ -921,8 +922,9 @@ class SILInstruction : public llvm::ilist_node { /// Pretty-print the value in context, preceded by its operands (if the /// value represents the result of an instruction) and followed by its /// users. - void dumpInContext() const; void printInContext(raw_ostream &OS) const; + void printInContext(SILPrintContext &ctx) const; + void dumpInContext() const; static bool classof(SILNodePointer node) { return node->getKind() >= SILNodeKind::First_SILInstruction && diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index af596894c0cb4..4fb4c6ab3162d 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -1145,7 +1145,7 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const SILModule &M){ void verificationFailure(const Twine &complaint, const SILInstruction *atInstruction, const SILArgument *atArgument, - llvm::function_ref extraContext); + llvm::function_ref extraContext); inline bool SILOptions::supportsLexicalLifetimes(const SILModule &mod) const { switch (mod.getStage()) { diff --git a/include/swift/SIL/SILNode.h b/include/swift/SIL/SILNode.h index 39a358c631a58..ca4220382463a 100644 --- a/include/swift/SIL/SILNode.h +++ b/include/swift/SIL/SILNode.h @@ -33,6 +33,7 @@ class NonSingleValueInstruction; class SILModule; class ValueBase; class SILNode; +class SILPrintContext; class SILValue; /// An enumeration which contains values for all the nodes in SILNodes.def. @@ -430,12 +431,14 @@ class alignas(8) SILNode : /// will be valid SIL assembly; otherwise, it will be an arbitrary /// format suitable for debugging. void print(raw_ostream &OS) const; + void print(SILPrintContext &ctx) const; void dump() const; /// Pretty-print the node in context, preceded by its operands (if the /// value represents the result of an instruction) and followed by its /// users. void printInContext(raw_ostream &OS) const; + void printInContext(SILPrintContext &ctx) const; void dumpInContext() const; // Cast to SingleValueInstruction. This is an implementation detail diff --git a/lib/SIL/IR/SILPrinter.cpp b/lib/SIL/IR/SILPrinter.cpp index 70e67165357ed..4138ef3115552 100644 --- a/lib/SIL/IR/SILPrinter.cpp +++ b/lib/SIL/IR/SILPrinter.cpp @@ -3372,6 +3372,9 @@ void SILNode::dump() const { void SILNode::print(raw_ostream &OS) const { SILPrintContext Ctx(OS); + print(Ctx); +} +void SILNode::print(SILPrintContext &Ctx) const { SILPrinter(Ctx).print(this); } @@ -3391,6 +3394,9 @@ void SingleValueInstruction::dump() const { void SILInstruction::print(raw_ostream &OS) const { SILPrintContext Ctx(OS); + print(Ctx); +} +void SILInstruction::print(SILPrintContext &Ctx) const { SILPrinter(Ctx).print(this); } @@ -3413,7 +3419,9 @@ void SILBasicBlock::dump(bool DebugInfo) const { /// Pretty-print the SILBasicBlock to the designated stream. void SILBasicBlock::print(raw_ostream &OS) const { SILPrintContext Ctx(OS); - + print(Ctx); +} +void SILBasicBlock::print(SILPrintContext &Ctx) const { // Print the debug scope (and compute if we didn't do it already). auto &SM = this->getParent()->getModule().getASTContext().SourceMgr; for (auto &I : *this) { @@ -3424,10 +3432,6 @@ void SILBasicBlock::print(raw_ostream &OS) const { SILPrinter(Ctx).print(this); } -void SILBasicBlock::print(SILPrintContext &Ctx) const { - SILPrinter(Ctx).print(this); -} - void SILBasicBlock::dumpID(bool newline) const { #ifndef NDEBUG printID(llvm::errs(), newline); @@ -4234,6 +4238,9 @@ void SILNode::dumpInContext() const { } void SILNode::printInContext(llvm::raw_ostream &OS) const { SILPrintContext Ctx(OS); + printInContext(Ctx); +} +void SILNode::printInContext(SILPrintContext &Ctx) const { SILPrinter(Ctx).printInContext(this); } @@ -4242,6 +4249,9 @@ void SILInstruction::dumpInContext() const { } void SILInstruction::printInContext(llvm::raw_ostream &OS) const { SILPrintContext Ctx(OS); + printInContext(Ctx); +} +void SILInstruction::printInContext(SILPrintContext &Ctx) const { SILPrinter(Ctx).printInContext(asSILNode()); } diff --git a/lib/SIL/Utils/BasicBlockUtils.cpp b/lib/SIL/Utils/BasicBlockUtils.cpp index ca0d4cecc24b4..2154252012698 100644 --- a/lib/SIL/Utils/BasicBlockUtils.cpp +++ b/lib/SIL/Utils/BasicBlockUtils.cpp @@ -615,13 +615,12 @@ static FunctionTest DeadEndEdgesTest("dead_end_edges", [](auto &function, auto visitingSet = edges.createVisitingSet(/*includeUnreachable*/ true); auto &out = llvm::outs(); + SILPrintContext ctx(out); for (auto &srcBB : function) { for (auto *dstBB : srcBB.getSuccessorBlocks()) { if (auto regionIndex = edges.entersDeadEndRegion(&srcBB, dstBB)) { - srcBB.printID(out, false); - out << " -> "; - dstBB->printID(out, false); - out << " (region " << *regionIndex << "; "; + out << ctx.getID(&srcBB) << " -> " << ctx.getID(dstBB) + << " (region " << *regionIndex << "; "; if (visitingSet.visitEdgeTo(dstBB)) { out << "last edge)"; } else { diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 2498e4701d919..0c8bf89d17c7c 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -98,8 +98,9 @@ extern llvm::cl::opt SILPrintDebugInfo; void swift::verificationFailure(const Twine &complaint, const SILInstruction *atInstruction, const SILArgument *atArgument, - llvm::function_ref extraContext) { + llvm::function_ref extraContext) { llvm::raw_ostream &out = llvm::dbgs(); + SILPrintContext ctx(out); const SILFunction *f = nullptr; StringRef funcName = "?"; @@ -116,14 +117,14 @@ void swift::verificationFailure(const Twine &complaint, out << "SIL verification failed: " << complaint << "\n"; if (extraContext) - extraContext(out); + extraContext(ctx); if (atInstruction) { out << "Verifying instruction:\n"; - atInstruction->printInContext(out); + atInstruction->printInContext(ctx); } else if (atArgument) { out << "Verifying argument:\n"; - atArgument->printInContext(out); + atArgument->printInContext(ctx); } if (ContinueOnFailure) { out << "End Error in function " << funcName << "\n"; @@ -978,7 +979,7 @@ class SILVerifier : public SILVerifierBase { } void _require(bool condition, const Twine &complaint, - llvm::function_ref extraContext + llvm::function_ref extraContext = nullptr) { if (condition) return; @@ -1111,16 +1112,16 @@ class SILVerifier : public SILVerifierBase { /// Assert that two types are equal. void requireSameType(Type type1, Type type2, const Twine &complaint) { _require(type1->isEqual(type2), complaint, - [&](llvm::raw_ostream &out) { - out << " " << type1 << "\n " << type2 << '\n'; + [&](SILPrintContext &ctx) { + ctx.OS() << " " << type1 << "\n " << type2 << '\n'; }); } /// Assert that two types are equal. void requireSameType(SILType type1, SILType type2, const Twine &complaint) { _require(type1 == type2, complaint, - [&](llvm::raw_ostream &out) { - out << " " << type1 << "\n " << type2 << '\n'; + [&](SILPrintContext &ctx) { + ctx.OS() << " " << type1 << "\n " << type2 << '\n'; }); } @@ -1158,15 +1159,15 @@ class SILVerifier : public SILVerifierBase { return; if (!Result.hasPayload()) { - _require(false, what, [&](llvm::raw_ostream &out) { - out << " " << Result.getMessage().data() << '\n' - << " " << type1 << "\n " << type2 << '\n'; + _require(false, what, [&](SILPrintContext &ctx) { + ctx.OS() << " " << Result.getMessage().data() << '\n' + << " " << type1 << "\n " << type2 << '\n'; }); } else { - _require(false, what, [&](llvm::raw_ostream &out) { - out << " " << Result.getMessage().data() - << ".\nParameter: " << Result.getPayload() - << "\n " << type1 << "\n " << type2 << '\n'; + _require(false, what, [&](SILPrintContext &ctx) { + ctx.OS() << " " << Result.getMessage().data() + << ".\nParameter: " << Result.getPayload() + << "\n " << type1 << "\n " << type2 << '\n'; }); } } @@ -1193,7 +1194,9 @@ class SILVerifier : public SILVerifierBase { template T *requireValueKind(SILValue value, const Twine &what) { auto match = dyn_cast(value); - _require(match != nullptr, what, [=](llvm::raw_ostream &out) { out << value; }); + _require(match != nullptr, what, [=](SILPrintContext &ctx) { + value->print(ctx); + }); return match; } @@ -1891,7 +1894,8 @@ class SILVerifier : public SILVerifierBase { if (subs.getGenericSignature().getCanonicalSignature() != fnTy->getInvocationGenericSignature().getCanonicalSignature()) { _require(false, "Substitution map does not match callee in apply instruction", - [&](llvm::raw_ostream &out) { + [&](SILPrintContext &ctx) { + auto &out = ctx.OS(); out << "substitution map's generic signature: "; subs.getGenericSignature()->print(out); out << "\n"; @@ -6803,22 +6807,22 @@ class SILVerifier : public SILVerifierBase { BBState(maybe_movable_ref other) : BBState(std::move(other).construct()) {} - void printStack(llvm::raw_ostream &out, StringRef label) const { - out << label << ": ["; - if (!Stack.empty()) out << "\n"; + void printStack(SILPrintContext &ctx, StringRef label) const { + ctx.OS() << label << ": ["; + if (!Stack.empty()) ctx.OS() << "\n"; for (auto allocation: Stack) { - allocation->print(out); + allocation->print(ctx); } - out << "]\n"; + ctx.OS() << "]\n"; } - void printActiveOps(llvm::raw_ostream &out, StringRef label) const { - out << label << ": ["; - if (!ActiveOps.empty()) out << "\n"; + void printActiveOps(SILPrintContext &ctx, StringRef label) const { + ctx.OS() << label << ": ["; + if (!ActiveOps.empty()) ctx.OS() << "\n"; for (auto op: ActiveOps) { - op->print(out); + op->print(ctx); } - out << "]\n"; + ctx.OS() << "]\n"; } /// Given that we have two edges to the same block or dead-end region, @@ -6877,14 +6881,13 @@ class SILVerifier : public SILVerifierBase { // conservative merge regardless of failure so that we're in a // coherent state in the successor block. auto fail = [&](StringRef complaint, - llvm::function_ref extra + llvm::function_ref extra = nullptr) { verificationFailure(complaint, term, nullptr, - [&](llvm::raw_ostream &out) { - out << "Entering basic block "; - succBB->printID(out, /*newline*/ true); + [&](SILPrintContext &ctx) { + ctx.OS() << "Entering basic block " << ctx.getID(succBB) << "\n"; - if (extra) extra(out); + if (extra) extra(ctx); }); }; @@ -6908,9 +6911,9 @@ class SILVerifier : public SILVerifierBase { if (Stack != otherState.Stack) { if (!isDeadEndEdge) { fail("inconsistent stack states entering basic block", - [&](llvm::raw_ostream &out) { - otherState.printStack(out, "Current stack state"); - printStack(out, "Recorded stack state"); + [&](SILPrintContext &ctx) { + otherState.printStack(ctx, "Current stack state"); + printStack(ctx, "Recorded stack state"); }); } @@ -6923,9 +6926,9 @@ class SILVerifier : public SILVerifierBase { if (ActiveOps != otherState.ActiveOps) { if (!isDeadEndEdge) { fail("inconsistent active-operations sets entering basic block", - [&](llvm::raw_ostream &out) { - otherState.printActiveOps(out, "Current active operations"); - printActiveOps(out, "Recorded active operations"); + [&](SILPrintContext &ctx) { + otherState.printActiveOps(ctx, "Current active operations"); + printActiveOps(ctx, "Recorded active operations"); }); } @@ -7021,9 +7024,9 @@ class SILVerifier : public SILVerifierBase { } else { verificationFailure("deallocating allocation that is not the top of the stack", &i, nullptr, - [&](llvm::raw_ostream &out) { - state.printStack(out, "Current stack state"); - out << "Stack allocation:\n" << *op; + [&](SILPrintContext &ctx) { + state.printStack(ctx, "Current stack state"); + ctx.OS() << "Stack allocation:\n" << *op; // The deallocation is printed out as the focus of the failure. }); }