Index: llvm/include/llvm/Passes/StandardInstrumentations.h =================================================================== --- llvm/include/llvm/Passes/StandardInstrumentations.h +++ llvm/include/llvm/Passes/StandardInstrumentations.h @@ -26,6 +26,14 @@ class Module; +/// This class abstracts implementations of standard instrumentation classes. +class Instrument { +public: + virtual ~Instrument(){}; +}; + +// TODO: Make PrintIRInstrumentation an Instrument and move it to +// StandardInstrumentations.cpp. /// Instrumentation to print IR before/after passes. /// /// Needs state to be able to print module after pass that invalidates IR unit @@ -58,6 +66,7 @@ class StandardInstrumentations { PrintIRInstrumentation PrintIR; TimePassesHandler TimePasses; + std::vector> Instruments; public: StandardInstrumentations() = default; Index: llvm/lib/Passes/StandardInstrumentations.cpp =================================================================== --- llvm/lib/Passes/StandardInstrumentations.cpp +++ llvm/lib/Passes/StandardInstrumentations.cpp @@ -24,10 +24,17 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" +#define DEBUG_TYPE "instrumentation" using namespace llvm; namespace { +static cl::opt VerifyPreservedCFG("verify-cfg-preserved", cl::Hidden, +#ifdef NDEBUG + cl::init(false)); +#else + cl::init(true)); +#endif /// Extracting Module out of \p IR unit. Also fills a textual description /// of \p IR for use in header when printing. @@ -246,9 +253,101 @@ }); } } +class PreservedCFGChecker : public Instrument { + using CFG = DenseMap>; + SmallVector, 8> GraphStackBefore; + + CFG buildGraph(const Function *F) { + CFG Result; + for (const auto &BB : *F) { + auto &SuccessorsList = Result[&BB]; // Create an empty list. + for (auto Succ : successors(&BB)) + SuccessorsList.emplace_back(Succ); + } + return Result; + } + + void printCFGdiff(const CFG &Before, const CFG &After) const { + if (Before.size() != After.size()) + dbgs() << "Different number of basic blocks: before=" << Before.size() + << ", after=" << After.size() << "\n"; + for (auto &BB : Before) { + auto BA = After.find(BB.first); + if (BA == After.end()) { + dbgs() << "Block " << BB.first->getName() << " is removed\n"; + continue; + } + + if (BA->second == BB.second) + continue; + + dbgs() << "Different lists of successors of block " << BB.first->getName() + << ":\n"; + dbgs() << "- before: "; + for (auto &Succ : BB.second) + dbgs() << Succ->getName() << ", "; + dbgs() << "\n"; + dbgs() << "- after: "; + for (auto &Succ : BA->second) + dbgs() << Succ->getName() << ", "; + dbgs() << "\n"; + } + + for (auto &BA : After) { + auto BB = Before.find(BA.first); + if (BB == Before.end()) + dbgs() << "Block " << BA.first->getName() << " is added\n"; + } + } + +public: + ~PreservedCFGChecker() = default; + + static bool isNeeded() { return VerifyPreservedCFG; } + + void registerCallbacks(PassInstrumentationCallbacks &PIC) { + PIC.registerBeforePassCallback([this](StringRef P, Any IR) { + if (any_isa(IR)) + GraphStackBefore.emplace_back( + buildGraph(any_cast(IR))); + else + GraphStackBefore.emplace_back(None); + return true; + }); + PIC.registerAfterPassCallback( + [this](StringRef P, Any IR, const PreservedAnalyses &PassPA) { + auto GraphBefore = GraphStackBefore.pop_back_val(); + + if (!PassPA.allAnalysesInSetPreserved(CFGAnalyses::ID())) + return; + + if (any_isa(IR)) { + CFG GraphAfter = buildGraph(any_cast(IR)); + if (GraphAfter == *GraphBefore) + return; + + LLVM_DEBUG( + dbgs() + << "Error: " << P + << " reported it preserved CFG, but a change is detected:\n"); + LLVM_DEBUG(printCFGdiff(*GraphBefore, GraphAfter)); + report_fatal_error(Twine("Preserved CFG changed by ", P)); + } + }); + PIC.registerAfterPassInvalidatedCallback( + [this](StringRef P, const PreservedAnalyses &PassPA) { + (void)GraphStackBefore.pop_back_val(); + }); + } +}; void StandardInstrumentations::registerCallbacks( PassInstrumentationCallbacks &PIC) { PrintIR.registerCallbacks(PIC); TimePasses.registerCallbacks(PIC); + if (PreservedCFGChecker::isNeeded()) { + PreservedCFGChecker *PCC = new PreservedCFGChecker(); + PCC->registerCallbacks(PIC); + Instruments.emplace_back(PCC); + } }