[llvm] [OptBisect] Merge shouldRun logic of -opt-bisect and -opt-disable (PR #177122)

Cristian Assaiante via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 21 01:06:15 PST 2026


https://github.com/cristianassaiante created https://github.com/llvm/llvm-project/pull/177122

Hi everyone,

After the introduction of `-opt-disable` in [81eb7de](https://www.perplexity.ai/search/81eb7defa23dcf48a8e51391543eb210df232440), one of its main limitations has been that it cannot be used together with `-opt-bisect`, since `getGlobalPassGate()` returns either `getOptDisabler()` or `getOptBisector()`, but not both. Allowing them to work simultaneously would be useful for disabling individual passes while still restricting the pipeline. This is especially relevant given the recent updates to `-opt-bisect`, such as interval support added in [7f9a00e](https://www.perplexity.ai/search/7f9a00e59c569110ac2bdaa5f21d26eff9c1de65).

For example, when a defect is caused by a particular pass but its impact is masked by another, it can be difficult to identify the actual culprit through bisecting alone. Being able to disable passes individually while using `-opt-bisect` would make this process much more efficient.

In this PR, I have merged the logic of the two flags so that they can interoperate. Specifically:

- Removed the `OptDisable` class, as its functionality has been integrated into `OptBisect`.

- Removed the `-opt-disable-enable-verbosity` flag, since verbosity now behaves identically to `-opt-bisect`.

- Updated all affected tests and added a new test to verify that both flags work correctly together.

There is one potential change I am considering for clarity but have not yet implemented. Currently, log messages continue to use the `BISECT:` prefix. With the merged behavior, it might make sense to revise this message to better reflect the combined functionality.

>From 76ff44a2aa3cf3398a62775f20541432f097bd8a Mon Sep 17 00:00:00 2001
From: Cristian Assaiante <cristianassaiante at outlook.com>
Date: Fri, 16 Jan 2026 18:31:21 +0100
Subject: [PATCH] [OptBisect] Merge opt-disable and opt-bisect logic

---
 llvm/include/llvm/IR/OptBisect.h          | 45 ++++---------
 llvm/lib/IR/OptBisect.cpp                 | 42 +++----------
 llvm/test/Other/opt-disable-and-bisect.ll | 77 +++++++++++++++++++++++
 llvm/test/Other/opt-disable.ll            | 32 +++++-----
 4 files changed, 112 insertions(+), 84 deletions(-)
 create mode 100644 llvm/test/Other/opt-disable-and-bisect.ll

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 77c866ab76d21..3089fc5922c26 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -39,9 +39,10 @@ class OptPassGate {
 };
 
 /// This class implements a mechanism to disable passes and individual
-/// optimizations at compile time based on a command line option
-/// (-opt-bisect) in order to perform a bisecting search for
-/// optimization-related problems.
+/// optimizations at compile time based on two command line options
+/// (-opt-bisect and -opt-disable) in order to perform a bisecting 
+/// search for optimization-related problems, and/or disable individual
+/// passes or combinations thereof.
 class LLVM_ABI OptBisect : public OptPassGate {
 public:
   /// Default constructor. Initializes the state to "disabled". The bisection
@@ -67,7 +68,9 @@ class LLVM_ABI OptBisect : public OptPassGate {
                      StringRef IRDescription) const override;
 
   /// isEnabled() should return true before calling shouldRunPass().
-  bool isEnabled() const override { return !BisectIntervals.empty(); }
+  bool isEnabled() const override { 
+    return !BisectIntervals.empty() || !DisabledPasses.empty();
+  }
 
   /// Set intervals directly from an IntervalList.
   void setIntervals(IntegerInclusiveIntervalUtils::IntervalList Intervals) {
@@ -80,38 +83,16 @@ class LLVM_ABI OptBisect : public OptPassGate {
     LastBisectNum = 0;
   }
 
-private:
-  mutable int LastBisectNum = 0;
-  IntegerInclusiveIntervalUtils::IntervalList BisectIntervals;
-};
-
-/// This class implements a mechanism to disable passes and individual
-/// optimizations at compile time based on a command line option
-/// (-opt-disable) in order to study how single transformations, or
-/// combinations thereof, affect the IR.
-class LLVM_ABI OptDisable : public OptPassGate {
-public:
-  /// Checks the pass name to determine if the specified pass should run.
-  ///
-  /// It returns true if the pass should run, i.e. if its name is was
-  /// not provided via command line.
-  /// If -opt-disable-enable-verbosity is given, the method prints the
-  /// name of the pass, and whether or not the pass will be executed.
-  ///
-  /// Most passes should not call this routine directly. Instead, it is called
-  /// through helper routines provided by the base classes of the pass. For
-  /// instance, function passes should call FunctionPass::skipFunction().
-  bool shouldRunPass(StringRef PassName,
-                     StringRef IRDescription) const override;
-
   /// Parses the command line argument to extract the names of the passes
   /// to be disabled. Multiple pass names can be provided with comma separation.
-  void setDisabled(StringRef Pass);
-
-  /// isEnabled() should return true before calling shouldRunPass().
-  bool isEnabled() const override { return !DisabledPasses.empty(); }
+  void setDisabled(StringRef Pass) {
+    DisabledPasses.insert(Pass);
+  }
 
 private:
+  mutable int LastBisectNum = 0;
+  IntegerInclusiveIntervalUtils::IntervalList BisectIntervals;
+
   StringSet<> DisabledPasses = {};
 };
 
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index bb9fba66ddf95..2dce82374e640 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -28,11 +28,6 @@ static OptBisect &getOptBisector() {
   return OptBisector;
 }
 
-static OptDisable &getOptDisabler() {
-  static OptDisable OptDisabler;
-  return OptDisabler;
-}
-
 static cl::opt<int> OptBisectLimit(
     "opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional,
     cl::cb<void, int>([](int Limit) {
@@ -80,21 +75,16 @@ static cl::opt<std::string> OptBisectIntervals(
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
-    cl::desc("Show verbose output when opt-bisect-limit is set"), cl::Hidden,
+    cl::desc("Show verbose output when opt-bisect-limit and/or opt-disable are set"), cl::Hidden,
     cl::init(true), cl::Optional);
 
 static cl::list<std::string> OptDisablePasses(
     "opt-disable", cl::Hidden, cl::CommaSeparated, cl::Optional,
     cl::cb<void, std::string>([](const std::string &Pass) {
-      getOptDisabler().setDisabled(Pass);
+      getOptBisector().setDisabled(Pass);
     }),
     cl::desc("Optimization pass(es) to disable (comma-separated list)"));
 
-static cl::opt<bool>
-    OptDisableVerbose("opt-disable-enable-verbosity",
-                      cl::desc("Show verbose output when opt-disable is set"),
-                      cl::Hidden, cl::init(false), cl::Optional);
-
 static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
                              bool Running) {
   StringRef Status = Running ? "" : "NOT ";
@@ -109,35 +99,19 @@ bool OptBisect::shouldRunPass(StringRef PassName,
   int CurBisectNum = ++LastBisectNum;
 
   // Check if current pass number falls within any of the specified intervals.
-  bool ShouldRun =
+  // Since the bisector may be enabled by opt-disable, we also need to check if the
+  // BisectIntervals are empty.
+  bool ShouldRun = BisectIntervals.empty() ||
       IntegerInclusiveIntervalUtils::contains(BisectIntervals, CurBisectNum);
+    
+  // Also check if the pass is disabled via -opt-disable.
+  ShouldRun = ShouldRun && !DisabledPasses.contains(PassName);
 
   if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
   return ShouldRun;
 }
 
-static void printDisablePassMessage(const StringRef &Name, StringRef TargetDesc,
-                                    bool Running) {
-  StringRef Status = Running ? "" : "NOT ";
-  dbgs() << "OptDisable: " << Status << "running pass " << Name << " on "
-         << TargetDesc << "\n";
-}
-
-void OptDisable::setDisabled(StringRef Pass) { DisabledPasses.insert(Pass); }
-
-bool OptDisable::shouldRunPass(StringRef PassName,
-                               StringRef IRDescription) const {
-  assert(isEnabled());
-
-  const bool ShouldRun = !DisabledPasses.contains(PassName);
-  if (OptDisableVerbose)
-    printDisablePassMessage(PassName, IRDescription, ShouldRun);
-  return ShouldRun;
-}
-
 OptPassGate &llvm::getGlobalPassGate() {
-  if (getOptDisabler().isEnabled())
-    return getOptDisabler();
   return getOptBisector();
 }
diff --git a/llvm/test/Other/opt-disable-and-bisect.ll b/llvm/test/Other/opt-disable-and-bisect.ll
new file mode 100644
index 0000000000000..f14712f072b61
--- /dev/null
+++ b/llvm/test/Other/opt-disable-and-bisect.ll
@@ -0,0 +1,77 @@
+; This test uses the same IR functions of the opt-bisect test
+; and it checks if it correctly cohexists with the opt-disable flag.
+; Passes before opt-bisect-limit should be ran unless disabled 
+; by opt-disable. Later passes NOT run.
+
+; RUN: opt -disable-output -disable-verify \
+; RUN:     -opt-disable=inferattrs,function-attrs  \
+; RUN:     -opt-bisect-limit=6 \
+; RUN:     -passes='inferattrs,cgscc(function-attrs,function(early-cse))' %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-MULTI-PASS
+; CHECK-MULTI-PASS: BISECT: NOT running pass (1) inferattrs on [module]
+; CHECK-MULTI-PASS: BISECT: NOT running pass (2) function-attrs on (f1)
+; CHECK-MULTI-PASS: BISECT: running pass (3) early-cse on f1
+; CHECK-MULTI-PASS: BISECT: NOT running pass (4) function-attrs on (f2)
+; CHECK-MULTI-PASS: BISECT: running pass (5) early-cse on f2
+; CHECK-MULTI-PASS: BISECT: NOT running pass (6) function-attrs on (f3)
+; CHECK-MULTI-PASS: BISECT: NOT running pass (7) early-cse on f3
+; CHECK-MULTI-PASS: BISECT: NOT running pass (8) function-attrs on (f4)
+; CHECK-MULTI-PASS: BISECT: NOT running pass (9) early-cse on f4
+
+declare i32 @g()
+
+define void @f1(i1 %arg) {
+entry:
+  br label %loop.0
+loop.0:
+  br i1 %arg, label %loop.0.0, label %loop.1
+loop.0.0:
+  br i1 %arg, label %loop.0.0, label %loop.0.1
+loop.0.1:
+  br i1 %arg, label %loop.0.1, label %loop.0
+loop.1:
+  br i1 %arg, label %loop.1, label %loop.1.bb1
+loop.1.bb1:
+  br i1 %arg, label %loop.1, label %loop.1.bb2
+loop.1.bb2:
+  br i1 %arg, label %end, label %loop.1.0
+loop.1.0:
+  br i1 %arg, 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
+}
+
+define void @f4(i1 %arg) {
+entry:
+  %i = alloca i32, align 4
+  call void @llvm.lifetime.start(i64 4, ptr %i)
+  br label %for.cond
+
+for.cond:
+  br i1 %arg, label %for.body, label %for.end
+
+for.body:
+  br label %for.cond
+
+for.end:
+  ret void
+}
+
+declare void @llvm.lifetime.start(i64, ptr nocapture)
diff --git a/llvm/test/Other/opt-disable.ll b/llvm/test/Other/opt-disable.ll
index 4506042215cbf..c54d2bb2f2019 100644
--- a/llvm/test/Other/opt-disable.ll
+++ b/llvm/test/Other/opt-disable.ll
@@ -1,36 +1,32 @@
 ; This test uses the same IR functions of the opt-bisect test
 ; but it checks the correctness of the -opt-disable flag.
-; -opt-disable-enable-verbosity is required to have output.
 
 ; RUN: opt -disable-output -disable-verify \
-; RUN:     -opt-disable-enable-verbosity \
 ; RUN:     -passes=inferattrs -opt-disable=inferattrs %s 2>&1 \
 ; RUN:     | FileCheck %s --check-prefix=CHECK-MODULE-PASS
-; CHECK-MODULE-PASS: OptDisable: NOT running pass inferattrs on [module]
+; CHECK-MODULE-PASS: BISECT: NOT running pass (1) inferattrs on [module]
 
 ; RUN: opt -disable-output -disable-verify \
-; RUN:     -opt-disable-enable-verbosity \
 ; RUN:     -passes=sroa -opt-disable=sroa %s 2>&1 \
 ; RUN:     | FileCheck %s --check-prefix=CHECK-FUNCTION-PASS
-; CHECK-FUNCTION-PASS: OptDisable: NOT running pass sroa on f1
-; CHECK-FUNCTION-PASS: OptDisable: NOT running pass sroa on f2
-; CHECK-FUNCTION-PASS: OptDisable: NOT running pass sroa on f3
-; CHECK-FUNCTION-PASS: OptDisable: NOT running pass sroa on f4
+; CHECK-FUNCTION-PASS: BISECT: NOT running pass (1) sroa on f1
+; CHECK-FUNCTION-PASS: BISECT: NOT running pass (2) sroa on f2
+; CHECK-FUNCTION-PASS: BISECT: NOT running pass (3) sroa on f3
+; CHECK-FUNCTION-PASS: BISECT: NOT running pass (4) sroa on f4
 
 ; RUN: opt -disable-output -disable-verify \
 ; RUN:     -opt-disable=inferattrs,function-attrs  \
-; RUN:     -opt-disable-enable-verbosity \
 ; RUN:     -passes='inferattrs,cgscc(function-attrs,function(early-cse))' %s 2>&1 \
 ; RUN:     | FileCheck %s --check-prefix=CHECK-MULTI-PASS
-; CHECK-MULTI-PASS: OptDisable: NOT running pass inferattrs on [module]
-; CHECK-MULTI-PASS: OptDisable: NOT running pass function-attrs on (f1)
-; CHECK-MULTI-PASS: OptDisable: running pass early-cse on f1
-; CHECK-MULTI-PASS: OptDisable: NOT running pass function-attrs on (f2)
-; CHECK-MULTI-PASS: OptDisable: running pass early-cse on f2
-; CHECK-MULTI-PASS: OptDisable: NOT running pass function-attrs on (f3)
-; CHECK-MULTI-PASS: OptDisable: running pass early-cse on f3
-; CHECK-MULTI-PASS: OptDisable: NOT running pass function-attrs on (f4)
-; CHECK-MULTI-PASS: OptDisable: running pass early-cse on f4
+; CHECK-MULTI-PASS: BISECT: NOT running pass (1) inferattrs on [module]
+; CHECK-MULTI-PASS: BISECT: NOT running pass (2) function-attrs on (f1)
+; CHECK-MULTI-PASS: BISECT: running pass (3) early-cse on f1
+; CHECK-MULTI-PASS: BISECT: NOT running pass (4) function-attrs on (f2)
+; CHECK-MULTI-PASS: BISECT: running pass (5) early-cse on f2
+; CHECK-MULTI-PASS: BISECT: NOT running pass (6) function-attrs on (f3)
+; CHECK-MULTI-PASS: BISECT: running pass (7) early-cse on f3
+; CHECK-MULTI-PASS: BISECT: NOT running pass (8) function-attrs on (f4)
+; CHECK-MULTI-PASS: BISECT: running pass (9) early-cse on f4
 
 declare i32 @g()
 



More information about the llvm-commits mailing list