[llvm] [OptBisect] Add function filtering support to opt-bisect (PR #222123)

Daniel Donenfeld via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 07:17:15 PDT 2026


https://github.com/daniel-donenfeld updated https://github.com/llvm/llvm-project/pull/222123

>From d1fe09515c84b2558e8051a9e146a8be61371429 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Wed, 2 Sep 2026 19:36:25 +0000
Subject: [PATCH 1/5] Add two additional control to opt-bisect. The first
 extends -opt-disable to accept pass-number intervals, including -1 to disable
 all passes. The second restricts the behavior of opt-bisect to only the
 functions passed to -opt-bisect-funcs. Added a lit test to check the verbose
 output using the new flags.

---
 llvm/include/llvm/IR/OptBisect.h              |  40 ++++++-
 llvm/lib/Analysis/LoopPass.cpp                |   6 +-
 llvm/lib/Analysis/RegionPass.cpp              |   6 +-
 llvm/lib/IR/OptBisect.cpp                     |  52 ++++++++-
 llvm/lib/IR/Pass.cpp                          |   4 +-
 llvm/lib/Passes/StandardInstrumentations.cpp  |   6 +-
 llvm/test/Other/opt-bisect-skip-and-filter.ll | 107 ++++++++++++++++++
 7 files changed, 208 insertions(+), 13 deletions(-)
 create mode 100644 llvm/test/Other/opt-bisect-skip-and-filter.ll

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 2cfb612020ae7..7198daf507dfd 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -14,6 +14,7 @@
 #ifndef LLVM_IR_OPTBISECT_H
 #define LLVM_IR_OPTBISECT_H
 
+#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Compiler.h"
@@ -31,6 +32,11 @@ class OptPassGate {
   /// over.
   virtual bool shouldRunPass(StringRef PassName,
                              StringRef IRDescription) const {
+    return shouldRunPass(PassName, IRDescription, "");
+  }
+
+  virtual bool shouldRunPass(StringRef PassName, StringRef IRDescription,
+                             StringRef FuncName) const {
     return true;
   }
 
@@ -68,17 +74,20 @@ class LLVM_ABI OptBisect : public OptPassGate {
   /// 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;
+  bool shouldRunPass(StringRef PassName, StringRef IRDescription,
+                     StringRef FuncName) const override;
 
   /// isEnabled() should return true before calling shouldRunPass().
   bool isEnabled() const override {
-    return !BisectIntervals.empty() || !DisabledPasses.empty();
+    return !BisectIntervals.empty() || !DisabledPasses.empty() ||
+           !DisabledIntervals.empty();
   }
 
   void reset() override {
     clearIntervals();
     DisabledPasses.clear();
+    clearDisabledIntervals();
+    clearEnabledFuncs();
   }
 
   /// Set intervals directly from an IntervalList.
@@ -96,11 +105,36 @@ class LLVM_ABI OptBisect : public OptPassGate {
   /// to be disabled. Multiple pass names can be provided with comma separation.
   void setDisabled(StringRef Pass) { DisabledPasses.insert(Pass); }
 
+  /// Set intervals directly from an IntervalList.
+  void
+  setDisabledIntervals(IntegerInclusiveIntervalUtils::IntervalList Intervals) {
+    DisabledIntervals.append(Intervals);
+  }
+
+  /// Clear all disabled intervals.
+  void clearDisabledIntervals() {
+    DisabledIntervals.clear();
+    LastDisableNum = 0;
+  }
+
+  /// Add a function name to the set of functions enabled for opt bisect.
+  void setEnabledFunc(StringRef FuncName) {
+    OptBisectFuncNames.insert(FuncName);
+  }
+
+  /// Clear the set of functions enabled for opt bisect, this reenables all
+  /// functions.
+  void clearEnabledFuncs() { OptBisectFuncNames.clear(); }
+
 private:
   mutable int LastBisectNum = 0;
   IntegerInclusiveIntervalUtils::IntervalList BisectIntervals;
 
   StringSet<> DisabledPasses = {};
+  IntegerInclusiveIntervalUtils::IntervalList DisabledIntervals;
+  mutable int LastDisableNum = 0;
+
+  StringSet<> OptBisectFuncNames = {};
 };
 
 /// Singleton instance of the OptPassGate class, so multiple pass managers don't
diff --git a/llvm/lib/Analysis/LoopPass.cpp b/llvm/lib/Analysis/LoopPass.cpp
index 85c15c5721cd6..4e5d0e4a03a0a 100644
--- a/llvm/lib/Analysis/LoopPass.cpp
+++ b/llvm/lib/Analysis/LoopPass.cpp
@@ -372,9 +372,13 @@ bool LoopPass::skipLoop(const Loop *L) const {
     return false;
   // Check the opt bisect limit.
   const OptPassGate &Gate = F->getContext().getOptPassGate();
+
   if (Gate.isEnabled() &&
-      !Gate.shouldRunPass(this->getPassName(), getDescription(*L)))
+      !Gate.shouldRunPass(this->getPassName(), getDescription(*L),
+                          F->getName())) {
     return true;
+  }
+
   // Check for the OptimizeNone attribute.
   if (F->hasOptNone()) {
     // FIXME: Report this to dbgs() only once per function.
diff --git a/llvm/lib/Analysis/RegionPass.cpp b/llvm/lib/Analysis/RegionPass.cpp
index ae1d84659de86..6ebd1ef2cf261 100644
--- a/llvm/lib/Analysis/RegionPass.cpp
+++ b/llvm/lib/Analysis/RegionPass.cpp
@@ -283,9 +283,11 @@ static std::string getDescription(const Region &R) {
 bool RegionPass::skipRegion(Region &R) const {
   Function &F = *R.getEntry()->getParent();
   const OptPassGate &Gate = F.getContext().getOptPassGate();
-  if (Gate.isEnabled() &&
-      !Gate.shouldRunPass(this->getPassName(), getDescription(R)))
+
+  if (Gate.isEnabled() && !Gate.shouldRunPass(this->getPassName(),
+                                              getDescription(R), F.getName())) {
     return true;
+  }
 
   if (F.hasOptNone()) {
     // Report this only once per function.
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 39562664e609c..3c44a411ff21c 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -79,13 +79,45 @@ static cl::opt<bool> OptBisectVerbose(
         "Show verbose output when opt-bisect-limit and/or opt-disable are set"),
     cl::Hidden, cl::init(true), cl::Optional);
 
+// Augment -opt-disable to support intervals in addition to pass names
 static cl::list<std::string> OptDisablePasses(
-    "opt-disable", cl::Hidden, cl::CommaSeparated, cl::Optional,
-    cl::cb<void, std::string>([](const std::string &Pass) {
-      getOptBisector().setDisabled(Pass);
+    "opt-disable", cl::Hidden, cl::Optional,
+    cl::cb<void, std::string>([](const std::string &PassOrIntervalStr) {
+      if (PassOrIntervalStr == "-1") {
+        // -1 means disable all passes.
+        getOptBisector().setDisabledIntervals(
+            {{1, std::numeric_limits<int>::max()}});
+        return;
+      }
+
+      // decide whether to parse this as an interval string or pass name
+      if (isdigit(PassOrIntervalStr[0])) {
+        auto Intervals =
+            IntegerInclusiveIntervalUtils::parseIntervals(PassOrIntervalStr);
+        if (!Intervals) {
+          handleAllErrors(Intervals.takeError(), [&](const StringError &E) {
+            errs() << "Error: Invalid interval specification for -opt-disable: "
+                   << PassOrIntervalStr << " (" << E.getMessage() << ")\n";
+          });
+          exit(1);
+        }
+        getOptBisector().setDisabledIntervals(std::move(*Intervals));
+      } else {
+        for (StringRef PassName : llvm::split(PassOrIntervalStr, ','))
+          getOptBisector().setDisabled(PassName);
+      }
     }),
     cl::desc("Optimization pass(es) to disable (comma-separated list)"));
 
+static cl::list<std::string> OptBisectFuncsList(
+    "opt-bisect-funcs", cl::value_desc("function names"), cl::CommaSeparated,
+    cl::cb<void, std::string>([](const std::string &FuncName) {
+      getOptBisector().setEnabledFunc(FuncName);
+    }),
+    cl::Hidden,
+    cl::desc("Only perform opt bisect for functions that are included in this "
+             "list and if empty, apply to all functions."));
+
 static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
                              bool Running) {
   StringRef Status = Running ? "" : "NOT ";
@@ -93,8 +125,8 @@ static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
          << " on " << TargetDesc << '\n';
 }
 
-bool OptBisect::shouldRunPass(StringRef PassName,
-                              StringRef IRDescription) const {
+bool OptBisect::shouldRunPass(StringRef PassName, StringRef IRDescription,
+                              StringRef FuncName) const {
   assert(isEnabled());
 
   int CurBisectNum = ++LastBisectNum;
@@ -108,6 +140,16 @@ bool OptBisect::shouldRunPass(StringRef PassName,
 
   // Also check if the pass is disabled via -opt-disable.
   ShouldRun = ShouldRun && !DisabledPasses.contains(PassName);
+  // Also check if the pass is disabled by interval.
+  const int CurDisableNum = ++LastDisableNum;
+  ShouldRun = ShouldRun && !IntegerInclusiveIntervalUtils::contains(
+                               DisabledIntervals, CurDisableNum);
+
+  // If passed a function name, check if the function is enabled for bisection
+  // via opt-bisect-funcs
+  bool SkipGate = !FuncName.empty() && !OptBisectFuncNames.empty() &&
+                  !OptBisectFuncNames.contains(FuncName);
+  ShouldRun = ShouldRun && !SkipGate;
 
   if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
diff --git a/llvm/lib/IR/Pass.cpp b/llvm/lib/IR/Pass.cpp
index 08d58379b7e69..c1ce3aef10b57 100644
--- a/llvm/lib/IR/Pass.cpp
+++ b/llvm/lib/IR/Pass.cpp
@@ -197,8 +197,10 @@ bool FunctionPass::skipFunction(const Function &F) const {
   if (PassName.empty())
     PassName = this->getPassName();
 
-  if (Gate.isEnabled() && !Gate.shouldRunPass(PassName, getDescription(F)))
+  if (Gate.isEnabled() &&
+      !Gate.shouldRunPass(PassName, getDescription(F), F.getName())) {
     return true;
+  }
 
   if (F.hasOptNone()) {
     LLVM_DEBUG(dbgs() << "Skipping pass '" << getPassName() << "' on function "
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 9a2225807145a..f0e05c345de02 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -1053,8 +1053,12 @@ bool OptPassGateInstrumentation::shouldRun(StringRef PassName, IRUnitRef IR) {
   if (isIgnored(PassName))
     return true;
 
+  StringRef FuncName;
+  if (const auto *F = dyn_cast<Function>(IR))
+    FuncName = F->getName();
+
   bool ShouldRun =
-      Context.getOptPassGate().shouldRunPass(PassName, getIRName(IR));
+      Context.getOptPassGate().shouldRunPass(PassName, getIRName(IR), FuncName);
   if (!ShouldRun && !this->HasWrittenIR && !OptBisectPrintIRPath.empty()) {
     // FIXME: print IR if limit is higher than number of opt-bisect
     // invocations
diff --git a/llvm/test/Other/opt-bisect-skip-and-filter.ll b/llvm/test/Other/opt-bisect-skip-and-filter.ll
new file mode 100644
index 0000000000000..76486da6d3e43
--- /dev/null
+++ b/llvm/test/Other/opt-bisect-skip-and-filter.ll
@@ -0,0 +1,107 @@
+; Tests that verify functionality for -opt-disable and -opt-bisect-funcs
+
+; If enabling bisect function filtering, do not skip Module Passes.
+; RUN: opt -disable-output -disable-verify -passes=inferattrs \
+; RUN:     -opt-bisect-funcs=f1 -opt-bisect-limit=-1 -opt-bisect-verbose=true %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-MODULE-PASS-FILTER
+; CHECK-MODULE-PASS-FILTER: BISECT: running pass (1) inferattrs on [module]
+; CHECK-MODULE-PASS-FILTER-NOT: BISECT: NOT running
+
+; RUN: opt -disable-output -disable-verify \
+; RUN:     -passes=early-cse -opt-bisect-limit=-1 -opt-bisect-funcs=f1 -opt-bisect-verbose=true %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-FUNCTION-PASS-FILTER
+; CHECK-FUNCTION-PASS-FILTER: BISECT: running pass (1) early-cse on f1
+; CHECK-FUNCTION-PASS-FILTER: BISECT: NOT running pass (2) early-cse on f2
+; CHECK-FUNCTION-PASS-FILTER: BISECT: NOT running pass (3) early-cse on f3
+; CHECK-FUNCTION-PASS-FILTER: BISECT: NOT running pass (4) early-cse on f4
+
+; RUN: opt -disable-output -disable-verify \
+; RUN:     -passes=early-cse -opt-bisect-limit=2 -opt-bisect-funcs=f1,f2,f3 -opt-bisect-verbose=true %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-LIMIT-FUNCTION-PASS-FILTER
+; CHECK-LIMIT-FUNCTION-PASS-FILTER: BISECT: running pass (1) early-cse on f1
+; CHECK-LIMIT-FUNCTION-PASS-FILTER: BISECT: running pass (2) early-cse on f2
+; CHECK-LIMIT-FUNCTION-PASS-FILTER: BISECT: NOT running pass (3) early-cse on f3
+; CHECK-LIMIT-FUNCTION-PASS-FILTER: BISECT: NOT running pass (4) early-cse on f4
+
+; RUN: opt -disable-output -disable-verify \
+; RUN:     -passes=function-attrs -opt-bisect-limit=-1 -opt-bisect-funcs=f2 -opt-bisect-verbose=true %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-CGSCC-PASS-FILTER
+; CHECK-CGSCC-PASS-FILTER: BISECT: running pass (1) function-attrs on (f1)
+; CHECK-CGSCC-PASS-FILTER-NOT: BISECT: Skip bisecting pass 'function-attrs' on
+; CHECK-CGSCC-PASS-FILTER: BISECT: running pass (2) function-attrs on (f2)
+; CHECK-CGSCC-PASS-FILTER: BISECT: running pass (3) function-attrs on (f3)
+; CHECK-CGSCC-PASS-FILTER: BISECT: running pass (4) function-attrs on (f4)
+
+
+; RUN: opt -disable-output -disable-verify -opt-disable=3,7 \
+; RUN:     -passes='inferattrs,cgscc(function-attrs,function(early-cse))' -opt-bisect-verbose=true %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
+; CHECK-DISABLE-PASS: BISECT: running pass (1) inferattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (2) function-attrs on (f1)
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) early-cse on f1
+; CHECK-DISABLE-PASS: BISECT: running pass (4) function-attrs on (f2)
+; CHECK-DISABLE-PASS: BISECT: running pass (5) early-cse on f2
+; CHECK-DISABLE-PASS: BISECT: running pass (6) function-attrs on (f3)
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on f3
+; CHECK-DISABLE-PASS: BISECT: running pass (8) function-attrs on (f4)
+; CHECK-DISABLE-PASS: BISECT: running pass (9) early-cse on 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
+  call void @llvm.lifetime.start(i64 4, ptr %i)
+  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, ptr nocapture)

>From f6dcdc2b6199ea172f55b42ee3fc0f481fe655f8 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Tue, 8 Sep 2026 20:27:36 +0000
Subject: [PATCH 2/5] Fix undef issues in test

---
 llvm/test/Other/opt-bisect-skip-and-filter.ll | 21 ++++++++++---------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/llvm/test/Other/opt-bisect-skip-and-filter.ll b/llvm/test/Other/opt-bisect-skip-and-filter.ll
index 76486da6d3e43..78a261cbbe6ca 100644
--- a/llvm/test/Other/opt-bisect-skip-and-filter.ll
+++ b/llvm/test/Other/opt-bisect-skip-and-filter.ll
@@ -48,23 +48,24 @@
 
 declare i32 @g()
 
-define void @f1() {
+define void @f1(i1 %cond0, i1 %cond1, i1 %cond2, i1 %cond3, i1 %cond4,
+                i1 %cond5, i1 %cond6) {
 entry:
   br label %loop.0
 loop.0:
-  br i1 undef, label %loop.0.0, label %loop.1
+  br i1 %cond0, label %loop.0.0, label %loop.1
 loop.0.0:
-  br i1 undef, label %loop.0.0, label %loop.0.1
+  br i1 %cond1, label %loop.0.0, label %loop.0.1
 loop.0.1:
-  br i1 undef, label %loop.0.1, label %loop.0
+  br i1 %cond2, label %loop.0.1, label %loop.0
 loop.1:
-  br i1 undef, label %loop.1, label %loop.1.bb1
+  br i1 %cond3, label %loop.1, label %loop.1.bb1
 loop.1.bb1:
-  br i1 undef, label %loop.1, label %loop.1.bb2
+  br i1 %cond4, label %loop.1, label %loop.1.bb2
 loop.1.bb2:
-  br i1 undef, label %end, label %loop.1.0
+  br i1 %cond5, label %end, label %loop.1.0
 loop.1.0:
-  br i1 undef, label %loop.1.0, label %loop.1
+  br i1 %cond6, label %loop.1.0, label %loop.1
 end:
   ret void
 }
@@ -88,14 +89,14 @@ bb.false:
 
 ; This function is here to verify that opt-bisect can skip all passes for
 ; functions that contain lifetime intrinsics.
-define void @f4() {
+define void @f4(i1 %cond) {
 entry:
   %i = alloca i32, align 4
   call void @llvm.lifetime.start(i64 4, ptr %i)
   br label %for.cond
 
 for.cond:
-  br i1 undef, label %for.body, label %for.end
+  br i1 %cond, label %for.body, label %for.end
 
 for.body:
   br label %for.cond

>From 75ef565f872abb1d6d2877b485bf7ce2ed6679c2 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Wed, 9 Sep 2026 15:05:52 +0000
Subject: [PATCH 3/5] Add interval test for opt-disable

---
 llvm/test/Other/opt-bisect-skip-and-filter.ll | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/llvm/test/Other/opt-bisect-skip-and-filter.ll b/llvm/test/Other/opt-bisect-skip-and-filter.ll
index 78a261cbbe6ca..0f8c57bc8e163 100644
--- a/llvm/test/Other/opt-bisect-skip-and-filter.ll
+++ b/llvm/test/Other/opt-bisect-skip-and-filter.ll
@@ -46,6 +46,19 @@
 ; CHECK-DISABLE-PASS: BISECT: running pass (8) function-attrs on (f4)
 ; CHECK-DISABLE-PASS: BISECT: running pass (9) early-cse on f4
 
+; RUN: opt -disable-output -disable-verify -opt-disable=3-7 \
+; RUN:     -passes='inferattrs,cgscc(function-attrs,function(early-cse))' -opt-bisect-verbose=true %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-DISABLE-INTERVAL
+; CHECK-DISABLE-INTERVAL: BISECT: running pass (1) inferattrs on [module]
+; CHECK-DISABLE-INTERVAL: BISECT: running pass (2) function-attrs on (f1)
+; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (3) early-cse on f1
+; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (4) function-attrs on (f2)
+; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (5) early-cse on f2
+; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (6) function-attrs on (f3)
+; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (7) early-cse on f3
+; CHECK-DISABLE-INTERVAL: BISECT: running pass (8) function-attrs on (f4)
+; CHECK-DISABLE-INTERVAL: BISECT: running pass (9) early-cse on f4
+
 declare i32 @g()
 
 define void @f1(i1 %cond0, i1 %cond1, i1 %cond2, i1 %cond3, i1 %cond4,

>From 978fd263620ec00130700a03080f5c398abaf320 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Wed, 16 Sep 2026 15:30:00 +0000
Subject: [PATCH 4/5] Remove disable intervals support

---
 llvm/include/llvm/IR/OptBisect.h              | 19 +----------
 llvm/lib/IR/OptBisect.cpp                     | 33 ++-----------------
 ...skip-and-filter.ll => opt-bisect-funcs.ll} | 28 +---------------
 3 files changed, 5 insertions(+), 75 deletions(-)
 rename llvm/test/Other/{opt-bisect-skip-and-filter.ll => opt-bisect-funcs.ll} (64%)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 7198daf507dfd..8688457002615 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -14,7 +14,6 @@
 #ifndef LLVM_IR_OPTBISECT_H
 #define LLVM_IR_OPTBISECT_H
 
-#include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Compiler.h"
@@ -79,14 +78,12 @@ class LLVM_ABI OptBisect : public OptPassGate {
 
   /// isEnabled() should return true before calling shouldRunPass().
   bool isEnabled() const override {
-    return !BisectIntervals.empty() || !DisabledPasses.empty() ||
-           !DisabledIntervals.empty();
+    return !BisectIntervals.empty() || !DisabledPasses.empty();
   }
 
   void reset() override {
     clearIntervals();
     DisabledPasses.clear();
-    clearDisabledIntervals();
     clearEnabledFuncs();
   }
 
@@ -105,18 +102,6 @@ class LLVM_ABI OptBisect : public OptPassGate {
   /// to be disabled. Multiple pass names can be provided with comma separation.
   void setDisabled(StringRef Pass) { DisabledPasses.insert(Pass); }
 
-  /// Set intervals directly from an IntervalList.
-  void
-  setDisabledIntervals(IntegerInclusiveIntervalUtils::IntervalList Intervals) {
-    DisabledIntervals.append(Intervals);
-  }
-
-  /// Clear all disabled intervals.
-  void clearDisabledIntervals() {
-    DisabledIntervals.clear();
-    LastDisableNum = 0;
-  }
-
   /// Add a function name to the set of functions enabled for opt bisect.
   void setEnabledFunc(StringRef FuncName) {
     OptBisectFuncNames.insert(FuncName);
@@ -131,8 +116,6 @@ class LLVM_ABI OptBisect : public OptPassGate {
   IntegerInclusiveIntervalUtils::IntervalList BisectIntervals;
 
   StringSet<> DisabledPasses = {};
-  IntegerInclusiveIntervalUtils::IntervalList DisabledIntervals;
-  mutable int LastDisableNum = 0;
 
   StringSet<> OptBisectFuncNames = {};
 };
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 3c44a411ff21c..972ab636ec7a4 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -79,33 +79,10 @@ static cl::opt<bool> OptBisectVerbose(
         "Show verbose output when opt-bisect-limit and/or opt-disable are set"),
     cl::Hidden, cl::init(true), cl::Optional);
 
-// Augment -opt-disable to support intervals in addition to pass names
 static cl::list<std::string> OptDisablePasses(
-    "opt-disable", cl::Hidden, cl::Optional,
-    cl::cb<void, std::string>([](const std::string &PassOrIntervalStr) {
-      if (PassOrIntervalStr == "-1") {
-        // -1 means disable all passes.
-        getOptBisector().setDisabledIntervals(
-            {{1, std::numeric_limits<int>::max()}});
-        return;
-      }
-
-      // decide whether to parse this as an interval string or pass name
-      if (isdigit(PassOrIntervalStr[0])) {
-        auto Intervals =
-            IntegerInclusiveIntervalUtils::parseIntervals(PassOrIntervalStr);
-        if (!Intervals) {
-          handleAllErrors(Intervals.takeError(), [&](const StringError &E) {
-            errs() << "Error: Invalid interval specification for -opt-disable: "
-                   << PassOrIntervalStr << " (" << E.getMessage() << ")\n";
-          });
-          exit(1);
-        }
-        getOptBisector().setDisabledIntervals(std::move(*Intervals));
-      } else {
-        for (StringRef PassName : llvm::split(PassOrIntervalStr, ','))
-          getOptBisector().setDisabled(PassName);
-      }
+    "opt-disable", cl::Hidden, cl::CommaSeparated, cl::Optional,
+    cl::cb<void, std::string>([](const std::string &Pass) {
+      getOptBisector().setDisabled(Pass);
     }),
     cl::desc("Optimization pass(es) to disable (comma-separated list)"));
 
@@ -140,10 +117,6 @@ bool OptBisect::shouldRunPass(StringRef PassName, StringRef IRDescription,
 
   // Also check if the pass is disabled via -opt-disable.
   ShouldRun = ShouldRun && !DisabledPasses.contains(PassName);
-  // Also check if the pass is disabled by interval.
-  const int CurDisableNum = ++LastDisableNum;
-  ShouldRun = ShouldRun && !IntegerInclusiveIntervalUtils::contains(
-                               DisabledIntervals, CurDisableNum);
 
   // If passed a function name, check if the function is enabled for bisection
   // via opt-bisect-funcs
diff --git a/llvm/test/Other/opt-bisect-skip-and-filter.ll b/llvm/test/Other/opt-bisect-funcs.ll
similarity index 64%
rename from llvm/test/Other/opt-bisect-skip-and-filter.ll
rename to llvm/test/Other/opt-bisect-funcs.ll
index 0f8c57bc8e163..076ecb28abb69 100644
--- a/llvm/test/Other/opt-bisect-skip-and-filter.ll
+++ b/llvm/test/Other/opt-bisect-funcs.ll
@@ -1,4 +1,4 @@
-; Tests that verify functionality for -opt-disable and -opt-bisect-funcs
+; Tests that verify functionality for -opt-bisect-funcs
 
 ; If enabling bisect function filtering, do not skip Module Passes.
 ; RUN: opt -disable-output -disable-verify -passes=inferattrs \
@@ -33,32 +33,6 @@
 ; CHECK-CGSCC-PASS-FILTER: BISECT: running pass (4) function-attrs on (f4)
 
 
-; RUN: opt -disable-output -disable-verify -opt-disable=3,7 \
-; RUN:     -passes='inferattrs,cgscc(function-attrs,function(early-cse))' -opt-bisect-verbose=true %s 2>&1 \
-; RUN:     | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
-; CHECK-DISABLE-PASS: BISECT: running pass (1) inferattrs on [module]
-; CHECK-DISABLE-PASS: BISECT: running pass (2) function-attrs on (f1)
-; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) early-cse on f1
-; CHECK-DISABLE-PASS: BISECT: running pass (4) function-attrs on (f2)
-; CHECK-DISABLE-PASS: BISECT: running pass (5) early-cse on f2
-; CHECK-DISABLE-PASS: BISECT: running pass (6) function-attrs on (f3)
-; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on f3
-; CHECK-DISABLE-PASS: BISECT: running pass (8) function-attrs on (f4)
-; CHECK-DISABLE-PASS: BISECT: running pass (9) early-cse on f4
-
-; RUN: opt -disable-output -disable-verify -opt-disable=3-7 \
-; RUN:     -passes='inferattrs,cgscc(function-attrs,function(early-cse))' -opt-bisect-verbose=true %s 2>&1 \
-; RUN:     | FileCheck %s --check-prefix=CHECK-DISABLE-INTERVAL
-; CHECK-DISABLE-INTERVAL: BISECT: running pass (1) inferattrs on [module]
-; CHECK-DISABLE-INTERVAL: BISECT: running pass (2) function-attrs on (f1)
-; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (3) early-cse on f1
-; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (4) function-attrs on (f2)
-; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (5) early-cse on f2
-; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (6) function-attrs on (f3)
-; CHECK-DISABLE-INTERVAL: BISECT: NOT running pass (7) early-cse on f3
-; CHECK-DISABLE-INTERVAL: BISECT: running pass (8) function-attrs on (f4)
-; CHECK-DISABLE-INTERVAL: BISECT: running pass (9) early-cse on f4
-
 declare i32 @g()
 
 define void @f1(i1 %cond0, i1 %cond1, i1 %cond2, i1 %cond3, i1 %cond4,

>From b65688f36b57768876bb6594c0b30cae15994f04 Mon Sep 17 00:00:00 2001
From: Daniel Donenfeld <ddonenfeld at nvidia.com>
Date: Mon, 21 Sep 2026 14:15:10 +0000
Subject: [PATCH 5/5] Address feedback

---
 llvm/include/llvm/IR/OptBisect.h | 8 ++------
 llvm/lib/IR/OptBisect.cpp        | 2 +-
 llvm/lib/IR/Pass.cpp             | 3 ++-
 3 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 8688457002615..6e0bce76512d7 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -28,12 +28,8 @@ class OptPassGate {
   virtual ~OptPassGate() = default;
 
   /// IRDescription is a textual description of the IR unit the pass is running
-  /// over.
-  virtual bool shouldRunPass(StringRef PassName,
-                             StringRef IRDescription) const {
-    return shouldRunPass(PassName, IRDescription, "");
-  }
-
+  /// over. FuncName is the name of the function or the empty string if a module
+  /// level pass is being run.
   virtual bool shouldRunPass(StringRef PassName, StringRef IRDescription,
                              StringRef FuncName) const {
     return true;
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 972ab636ec7a4..3fd2e3744ff25 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -119,7 +119,7 @@ bool OptBisect::shouldRunPass(StringRef PassName, StringRef IRDescription,
   ShouldRun = ShouldRun && !DisabledPasses.contains(PassName);
 
   // If passed a function name, check if the function is enabled for bisection
-  // via opt-bisect-funcs
+  // via opt-bisect-funcs.
   bool SkipGate = !FuncName.empty() && !OptBisectFuncNames.empty() &&
                   !OptBisectFuncNames.contains(FuncName);
   ShouldRun = ShouldRun && !SkipGate;
diff --git a/llvm/lib/IR/Pass.cpp b/llvm/lib/IR/Pass.cpp
index bf3472c0908b9..dbcd794a5d46b 100644
--- a/llvm/lib/IR/Pass.cpp
+++ b/llvm/lib/IR/Pass.cpp
@@ -68,7 +68,8 @@ bool ModulePass::skipModule(const Module &M) const {
   if (PassName.empty())
     PassName = this->getPassName();
 
-  return Gate.isEnabled() && !Gate.shouldRunPass(PassName, getDescription(M));
+  return Gate.isEnabled() &&
+         !Gate.shouldRunPass(PassName, getDescription(M), "");
 }
 
 bool Pass::mustPreserveAnalysisID(char &AID) const {



More information about the llvm-commits mailing list