diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h --- a/llvm/include/llvm/IR/OptBisect.h +++ b/llvm/include/llvm/IR/OptBisect.h @@ -68,9 +68,11 @@ /// isEnabled should return true before calling shouldRunPass bool isEnabled() const override { return BisectEnabled; } -private: + +protected: bool checkPass(const StringRef PassName, const StringRef TargetDesc); +private: bool BisectEnabled = false; unsigned LastBisectNum = 0; }; diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h --- a/llvm/include/llvm/Passes/StandardInstrumentations.h +++ b/llvm/include/llvm/Passes/StandardInstrumentations.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/OptBisect.h" #include "llvm/IR/PassInstrumentation.h" #include "llvm/IR/PassTimingInfo.h" #include "llvm/IR/ValueHandle.h" @@ -67,6 +68,12 @@ bool shouldRun(StringRef PassID, Any IR); }; +class OptBisectInstrumentation : public OptBisect { +public: + OptBisectInstrumentation() {} + void registerCallbacks(PassInstrumentationCallbacks &PIC); +}; + // Debug logging for transformation and analysis passes. class PrintPassInstrumentation { public: @@ -230,6 +237,7 @@ PrintPassInstrumentation PrintPass; TimePassesHandler TimePasses; OptNoneInstrumentation OptNone; + OptBisectInstrumentation OptBisect; PreservedCFGCheckerInstrumentation PreservedCFGChecker; IRChangePrinter PrintChangedIR; VerifyInstrumentation Verify; diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -540,6 +540,46 @@ return ShouldRun; } +static std::string getBisectDescription(Any IR) { + if (any_isa(IR)) { + const Module *M = any_cast(IR); + assert(M && "module should be valid for printing"); + return "module (" + M->getName().str() + ")"; + } + + if (any_isa(IR)) { + const Function *F = any_cast(IR); + assert(F && "function should be valid for printing"); + return "function (" + F->getName().str() + ")"; + } + + if (any_isa(IR)) { + const LazyCallGraph::SCC *C = any_cast(IR); + assert(C && "scc should be valid for printing"); + return "SCC " + C->getName(); + } + + if (any_isa(IR)) { + return "loop"; + } + + llvm_unreachable("Unknown wrapped IR type"); +} + +void OptBisectInstrumentation::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + if (!isEnabled()) + return; + + std::vector SpecialPasses = {"PassManager", "PassAdaptor"}; + + PIC.registerShouldRunOptionalPassCallback( + [this, SpecialPasses](StringRef PassID, Any IR) { + return isSpecialPass(PassID, SpecialPasses) || + checkPass(PassID, getBisectDescription(IR)); + }); +} + void PrintPassInstrumentation::registerCallbacks( PassInstrumentationCallbacks &PIC) { if (!DebugLogging) @@ -770,6 +810,7 @@ PrintPass.registerCallbacks(PIC); TimePasses.registerCallbacks(PIC); OptNone.registerCallbacks(PIC); + OptBisect.registerCallbacks(PIC); PreservedCFGChecker.registerCallbacks(PIC); PrintChangedIR.registerCallbacks(PIC); if (VerifyEach) diff --git a/llvm/test/Other/opt-bisect-new-pass-manager.ll b/llvm/test/Other/opt-bisect-new-pass-manager.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/opt-bisect-new-pass-manager.ll @@ -0,0 +1,157 @@ +; This file verifies the behavior of the OptBisect class, which is used to +; diagnose optimization related failures. The tests check various +; invocations that result in different sets of optimization passes that +; are run in different ways. +; +; Because the exact set of optimizations that will be run is expected to +; change over time, the checks for disabling passes are written in a +; conservative way that avoids assumptions about which specific passes +; will be disabled. + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=inferattrs -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-MODULE-PASS +; CHECK-MODULE-PASS: BISECT: running pass (1) InferFunctionAttrsPass on module + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=inferattrs -opt-bisect-limit=0 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-MODULE-PASS +; CHECK-LIMIT-MODULE-PASS: BISECT: NOT running pass (1) InferFunctionAttrsPass on module + +; RUN: opt -disable-output -debug-pass-manager \ +; RUN: -passes=inferattrs -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-REQUIRED-PASS +; CHECK-REQUIRED-PASS-NOT: BISECT: {{.*}} VerifierPass +; CHECK-REQUIRED-PASS: Running pass: VerifierPass +; CHECK-REQUIRED-PASS: BISECT: running pass (1) InferFunctionAttrsPass on module +; CHECK-REQUIRED-PASS-NOT: BISECT: {{.*}} VerifierPass +; CHECK-REQUIRED-PASS: Running pass: VerifierPass + +; RUN: opt -disable-output -debug-pass-manager \ +; RUN: -passes=inferattrs -opt-bisect-limit=0 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-REQUIRED-PASS +; CHECK-LIMIT-REQUIRED-PASS-NOT: BISECT: {{.*}} VerifierPass +; CHECK-LIMIT-REQUIRED-PASS: Running pass: VerifierPass +; CHECK-LIMIT-REQUIRED-PASS: BISECT: NOT running pass (1) InferFunctionAttrsPass on module +; CHECK-LIMIT-REQUIRED-PASS-NOT: BISECT: {{.*}} VerifierPass +; CHECK-LIMIT-REQUIRED-PASS: Running pass: VerifierPass + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=early-cse -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-FUNCTION-PASS +; CHECK-FUNCTION-PASS: BISECT: running pass (1) EarlyCSEPass on function (f1) +; CHECK-FUNCTION-PASS: BISECT: running pass (2) EarlyCSEPass on function (f2) +; CHECK-FUNCTION-PASS: BISECT: running pass (3) EarlyCSEPass on function (f3) +; CHECK-FUNCTION-PASS: BISECT: running pass (4) EarlyCSEPass on function (f4) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=early-cse -opt-bisect-limit=2 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-FUNCTION-PASS +; CHECK-LIMIT-FUNCTION-PASS: BISECT: running pass (1) EarlyCSEPass on function (f1) +; CHECK-LIMIT-FUNCTION-PASS: BISECT: running pass (2) EarlyCSEPass on function (f2) +; CHECK-LIMIT-FUNCTION-PASS: BISECT: NOT running pass (3) EarlyCSEPass on function (f3) +; CHECK-LIMIT-FUNCTION-PASS: BISECT: NOT running pass (4) EarlyCSEPass on function (f4) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=function-attrs -opt-bisect-limit=-1 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-CGSCC-PASS +; CHECK-CGSCC-PASS: BISECT: running pass (1) PostOrderFunctionAttrsPass on SCC (f1) +; CHECK-CGSCC-PASS: BISECT: running pass (2) PostOrderFunctionAttrsPass on SCC (f2) +; CHECK-CGSCC-PASS: BISECT: running pass (3) PostOrderFunctionAttrsPass on SCC (f3) +; CHECK-CGSCC-PASS: BISECT: running pass (4) PostOrderFunctionAttrsPass on SCC (f4) + +; RUN: opt -disable-output -disable-verify \ +; RUN: -passes=function-attrs -opt-bisect-limit=3 %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-CGSCC-PASS +; CHECK-LIMIT-CGSCC-PASS: BISECT: running pass (1) PostOrderFunctionAttrsPass on SCC (f1) +; CHECK-LIMIT-CGSCC-PASS: BISECT: running pass (2) PostOrderFunctionAttrsPass on SCC (f2) +; CHECK-LIMIT-CGSCC-PASS: BISECT: running pass (3) PostOrderFunctionAttrsPass on SCC (f3) +; CHECK-LIMIT-CGSCC-PASS: BISECT: NOT running pass (4) PostOrderFunctionAttrsPass on SCC (f4) + +; RUN: opt -disable-output -disable-verify -opt-bisect-limit=-1 \ +; RUN: -passes='inferattrs,cgscc(function-attrs,function(early-cse))' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-MULTI-PASS +; CHECK-MULTI-PASS: BISECT: running pass (1) InferFunctionAttrsPass on module +; CHECK-MULTI-PASS: BISECT: running pass (2) PostOrderFunctionAttrsPass on SCC (f1) +; CHECK-MULTI-PASS: BISECT: running pass (3) EarlyCSEPass on function (f1) +; CHECK-MULTI-PASS: BISECT: running pass (4) PostOrderFunctionAttrsPass on SCC (f2) +; CHECK-MULTI-PASS: BISECT: running pass (5) EarlyCSEPass on function (f2) +; CHECK-MULTI-PASS: BISECT: running pass (6) PostOrderFunctionAttrsPass on SCC (f3) +; CHECK-MULTI-PASS: BISECT: running pass (7) EarlyCSEPass on function (f3) +; CHECK-MULTI-PASS: BISECT: running pass (8) PostOrderFunctionAttrsPass on SCC (f4) +; CHECK-MULTI-PASS: BISECT: running pass (9) EarlyCSEPass on function (f4) + +; RUN: opt -disable-output -disable-verify -opt-bisect-limit=7 \ +; RUN: -passes='inferattrs,cgscc(function-attrs,function(early-cse))' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LIMIT-MULTI-PASS +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (1) InferFunctionAttrsPass on module +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (2) PostOrderFunctionAttrsPass on SCC (f1) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (3) EarlyCSEPass on function (f1) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (4) PostOrderFunctionAttrsPass on SCC (f2) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (5) EarlyCSEPass on function (f2) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (6) PostOrderFunctionAttrsPass on SCC (f3) +; CHECK-LIMIT-MULTI-PASS: BISECT: running pass (7) EarlyCSEPass on function (f3) +; CHECK-LIMIT-MULTI-PASS: BISECT: NOT running pass (8) PostOrderFunctionAttrsPass on SCC (f4) +; CHECK-LIMIT-MULTI-PASS: BISECT: NOT running pass (9) EarlyCSEPass on function (f4) + + +declare i32 @g() + +define void @f1() { +entry: + br label %loop.0 +loop.0: + br i1 undef, label %loop.0.0, label %loop.1 +loop.0.0: + br i1 undef, label %loop.0.0, label %loop.0.1 +loop.0.1: + br i1 undef, label %loop.0.1, label %loop.0 +loop.1: + br i1 undef, label %loop.1, label %loop.1.bb1 +loop.1.bb1: + br i1 undef, label %loop.1, label %loop.1.bb2 +loop.1.bb2: + br i1 undef, label %end, label %loop.1.0 +loop.1.0: + br i1 undef, label %loop.1.0, label %loop.1 +end: + ret void +} + +define i32 @f2() { +entry: + ret i32 0 +} + +define i32 @f3() { +entry: + %temp = call i32 @g() + %icmp = icmp ugt i32 %temp, 2 + br i1 %icmp, label %bb.true, label %bb.false +bb.true: + %temp2 = call i32 @f2() + ret i32 %temp2 +bb.false: + ret i32 0 +} + +; This function is here to verify that opt-bisect can skip all passes for +; functions that contain lifetime intrinsics. +define void @f4() { +entry: + %i = alloca i32, align 4 + %tmp = bitcast i32* %i to i8* + call void @llvm.lifetime.start(i64 4, i8* %tmp) + br label %for.cond + +for.cond: + br i1 undef, label %for.body, label %for.end + +for.body: + br label %for.cond + +for.end: + ret void +} + +declare void @llvm.lifetime.start(i64, i8* nocapture)