[llvm] [OptBisect] Add support for selecting ranges of passes and refactor DebugCounter to use a shared Range API. (PR #152393)

Yonah Goldberg via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 5 13:55:46 PDT 2025


https://github.com/YonahGoldberg updated https://github.com/llvm/llvm-project/pull/152393

>From 329098d16054afc1975327e75ebf02fb0ebe37a5 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 6 Aug 2025 21:28:33 +0000
Subject: [PATCH 01/31] opt bisect skip

---
 llvm/include/llvm/IR/OptBisect.h   |  8 +++++++-
 llvm/lib/IR/OptBisect.cpp          | 10 ++++++++--
 llvm/test/Other/opt-bisect-skip.ll | 15 +++++++++++++++
 3 files changed, 30 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Other/opt-bisect-skip.ll

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index d813ae933d65e..7b244c833f767 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"
@@ -67,7 +68,11 @@ class LLVM_ABI OptBisect : public OptPassGate {
                      StringRef IRDescription) const override;
 
   /// isEnabled() should return true before calling shouldRunPass().
-  bool isEnabled() const override { return BisectLimit != Disabled; }
+  bool isEnabled() const override { return BisectLimit != Disabled || !BisectSkipNumbers.empty(); }
+
+  void addSkip(int SkipNumber) {
+    BisectSkipNumbers.insert(SkipNumber);
+  }
 
   /// Set the new optimization limit and reset the counter. Passing
   /// OptBisect::Disabled disables the limiting.
@@ -81,6 +86,7 @@ class LLVM_ABI OptBisect : public OptPassGate {
 private:
   int BisectLimit = Disabled;
   mutable int LastBisectNum = 0;
+  SmallSet<int, 4> BisectSkipNumbers;
 };
 
 /// This class implements a mechanism to disable passes and individual
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 29ca268408265..59639f3b1981d 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -37,6 +37,13 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
                                    }),
                                    cl::desc("Maximum optimization to perform"));
 
+static cl::opt<int> OptBisectSkip("opt-bisect-skip", cl::Hidden,
+  cl::init(OptBisect::Disabled), cl::Optional,
+  cl::cb<void, int>([](int PassNum) {
+    getOptBisector().addSkip(PassNum);
+  }),
+  cl::desc("Skip pass at the given index in the optimization pipeline"));
+
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
     cl::desc("Show verbose output when opt-bisect-limit is set"), cl::Hidden,
@@ -66,8 +73,7 @@ bool OptBisect::shouldRunPass(StringRef PassName,
   assert(isEnabled());
 
   int CurBisectNum = ++LastBisectNum;
-  bool ShouldRun = (BisectLimit == -1 || CurBisectNum <= BisectLimit);
-  if (OptBisectVerbose)
+  bool ShouldRun = (BisectLimit == -1 || BisectLimit == Disabled || CurBisectNum <= BisectLimit) && !BisectSkipNumbers.contains(CurBisectNum);  if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
   return ShouldRun;
 }
diff --git a/llvm/test/Other/opt-bisect-skip.ll b/llvm/test/Other/opt-bisect-skip.ll
new file mode 100644
index 0000000000000..da94ad6230527
--- /dev/null
+++ b/llvm/test/Other/opt-bisect-skip.ll
@@ -0,0 +1,15 @@
+; Test that verifies functionality for -opt-bisect-skip
+
+; RUN: opt -O1 -opt-bisect-skip=3 -opt-bisect-skip=7 %s 2>&1 | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
+; CHECK-DISABLE-PASS: BISECT: running pass (1) annotation2metadata on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (2) forceattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) inferattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (4) lower-expect on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (5) simplifycfg on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (6) sroa on foo
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (8) openmp-opt on [module]
+
+define void @foo() {
+  ret void
+}
\ No newline at end of file

>From 723514de5f46c562591fdf634442866498673969 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 6 Aug 2025 21:28:51 +0000
Subject: [PATCH 02/31] format

---
 llvm/include/llvm/IR/OptBisect.h |  8 ++++----
 llvm/lib/IR/OptBisect.cpp        | 15 ++++++++-------
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 7b244c833f767..ee787e0580032 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -68,12 +68,12 @@ class LLVM_ABI OptBisect : public OptPassGate {
                      StringRef IRDescription) const override;
 
   /// isEnabled() should return true before calling shouldRunPass().
-  bool isEnabled() const override { return BisectLimit != Disabled || !BisectSkipNumbers.empty(); }
-
-  void addSkip(int SkipNumber) {
-    BisectSkipNumbers.insert(SkipNumber);
+  bool isEnabled() const override {
+    return BisectLimit != Disabled || !BisectSkipNumbers.empty();
   }
 
+  void addSkip(int SkipNumber) { BisectSkipNumbers.insert(SkipNumber); }
+
   /// Set the new optimization limit and reset the counter. Passing
   /// OptBisect::Disabled disables the limiting.
   void setLimit(int Limit) {
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 59639f3b1981d..defb8a98f9716 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -37,12 +37,10 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
                                    }),
                                    cl::desc("Maximum optimization to perform"));
 
-static cl::opt<int> OptBisectSkip("opt-bisect-skip", cl::Hidden,
-  cl::init(OptBisect::Disabled), cl::Optional,
-  cl::cb<void, int>([](int PassNum) {
-    getOptBisector().addSkip(PassNum);
-  }),
-  cl::desc("Skip pass at the given index in the optimization pipeline"));
+static cl::opt<int> OptBisectSkip(
+    "opt-bisect-skip", cl::Hidden, cl::init(OptBisect::Disabled), cl::Optional,
+    cl::cb<void, int>([](int PassNum) { getOptBisector().addSkip(PassNum); }),
+    cl::desc("Skip pass at the given index in the optimization pipeline"));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
@@ -73,7 +71,10 @@ bool OptBisect::shouldRunPass(StringRef PassName,
   assert(isEnabled());
 
   int CurBisectNum = ++LastBisectNum;
-  bool ShouldRun = (BisectLimit == -1 || BisectLimit == Disabled || CurBisectNum <= BisectLimit) && !BisectSkipNumbers.contains(CurBisectNum);  if (OptBisectVerbose)
+  bool ShouldRun = (BisectLimit == -1 || BisectLimit == Disabled ||
+                    CurBisectNum <= BisectLimit) &&
+                   !BisectSkipNumbers.contains(CurBisectNum);
+  if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
   return ShouldRun;
 }

>From 431c13be5ad020febf1fc2e51dd3af5f8384127f Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 6 Aug 2025 21:36:51 +0000
Subject: [PATCH 03/31] comment

---
 llvm/include/llvm/IR/OptBisect.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index ee787e0580032..9dcc0077552d4 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -72,6 +72,8 @@ class LLVM_ABI OptBisect : public OptPassGate {
     return BisectLimit != Disabled || !BisectSkipNumbers.empty();
   }
 
+  /// Add pass at index SkipNumber to the list of passes to skip
+  /// during bisection.
   void addSkip(int SkipNumber) { BisectSkipNumbers.insert(SkipNumber); }
 
   /// Set the new optimization limit and reset the counter. Passing

>From ec6842e2c65af9260284850d17dde4b7c3cf3bf8 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 6 Aug 2025 21:46:32 +0000
Subject: [PATCH 04/31] newline

---
 llvm/test/Other/opt-bisect-skip.ll | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/Other/opt-bisect-skip.ll b/llvm/test/Other/opt-bisect-skip.ll
index da94ad6230527..0f26e5f46f742 100644
--- a/llvm/test/Other/opt-bisect-skip.ll
+++ b/llvm/test/Other/opt-bisect-skip.ll
@@ -12,4 +12,4 @@
 
 define void @foo() {
   ret void
-}
\ No newline at end of file
+}

>From 6b3ed997ddea73cdfdd3a2dc7ad8e225d27363fd Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 20 Aug 2025 21:58:07 +0000
Subject: [PATCH 05/31] renaming

---
 llvm/include/llvm/IR/OptBisect.h   |  4 ++--
 llvm/lib/IR/OptBisect.cpp          |  8 ++++----
 llvm/test/Other/opt-bisect-skip.ll | 15 ---------------
 3 files changed, 6 insertions(+), 21 deletions(-)
 delete mode 100644 llvm/test/Other/opt-bisect-skip.ll

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 9dcc0077552d4..bc2294a870389 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -72,9 +72,9 @@ class LLVM_ABI OptBisect : public OptPassGate {
     return BisectLimit != Disabled || !BisectSkipNumbers.empty();
   }
 
-  /// Add pass at index SkipNumber to the list of passes to skip
+  /// Add pass at index Index to the list of passes to skip
   /// during bisection.
-  void addSkip(int SkipNumber) { BisectSkipNumbers.insert(SkipNumber); }
+  void disablePassAtIndex(int Index) { BisectSkipNumbers.insert(Index); }
 
   /// Set the new optimization limit and reset the counter. Passing
   /// OptBisect::Disabled disables the limiting.
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index defb8a98f9716..8850e8f0c32a6 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -37,10 +37,10 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
                                    }),
                                    cl::desc("Maximum optimization to perform"));
 
-static cl::opt<int> OptBisectSkip(
-    "opt-bisect-skip", cl::Hidden, cl::init(OptBisect::Disabled), cl::Optional,
-    cl::cb<void, int>([](int PassNum) { getOptBisector().addSkip(PassNum); }),
-    cl::desc("Skip pass at the given index in the optimization pipeline"));
+static cl::list<int> OptDisableIndices(
+    "opt-disable-indices", cl::Hidden, cl::CommaSeparated, cl::Optional,
+    cl::cb<void, int>([](int Index) { getOptBisector().disablePassAtIndex(Index); }),
+    cl::desc("Disable passes at the given indices in the optimization pipeline (comma-separated list)"));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
diff --git a/llvm/test/Other/opt-bisect-skip.ll b/llvm/test/Other/opt-bisect-skip.ll
deleted file mode 100644
index 0f26e5f46f742..0000000000000
--- a/llvm/test/Other/opt-bisect-skip.ll
+++ /dev/null
@@ -1,15 +0,0 @@
-; Test that verifies functionality for -opt-bisect-skip
-
-; RUN: opt -O1 -opt-bisect-skip=3 -opt-bisect-skip=7 %s 2>&1 | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
-; CHECK-DISABLE-PASS: BISECT: running pass (1) annotation2metadata on [module]
-; CHECK-DISABLE-PASS: BISECT: running pass (2) forceattrs on [module]
-; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) inferattrs on [module]
-; CHECK-DISABLE-PASS: BISECT: running pass (4) lower-expect on foo
-; CHECK-DISABLE-PASS: BISECT: running pass (5) simplifycfg on foo
-; CHECK-DISABLE-PASS: BISECT: running pass (6) sroa on foo
-; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on foo
-; CHECK-DISABLE-PASS: BISECT: running pass (8) openmp-opt on [module]
-
-define void @foo() {
-  ret void
-}

>From 895a1bbba27fac2187bbdd7f251fff800d3883aa Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 20 Aug 2025 22:03:17 +0000
Subject: [PATCH 06/31] format

---
 llvm/lib/IR/OptBisect.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 8850e8f0c32a6..e5d64a02455ac 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -37,10 +37,13 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
                                    }),
                                    cl::desc("Maximum optimization to perform"));
 
-static cl::list<int> OptDisableIndices(
-    "opt-disable-indices", cl::Hidden, cl::CommaSeparated, cl::Optional,
-    cl::cb<void, int>([](int Index) { getOptBisector().disablePassAtIndex(Index); }),
-    cl::desc("Disable passes at the given indices in the optimization pipeline (comma-separated list)"));
+static cl::list<int>
+    OptDisableIndices("opt-disable-indices", cl::Hidden, cl::CommaSeparated,
+                      cl::Optional, cl::cb<void, int>([](int Index) {
+                        getOptBisector().disablePassAtIndex(Index);
+                      }),
+                      cl::desc("Disable passes at the given indices in the "
+                               "optimization pipeline (comma-separated list)"));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",

>From 4b382092d7b7dd2ccf9b02e476a69477bf80d869 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 04:33:27 +0000
Subject: [PATCH 07/31] range support

---
 llvm/include/llvm/IR/OptBisect.h              |  40 +--
 llvm/include/llvm/Support/DebugCounter.h      |  17 +-
 llvm/include/llvm/Support/Range.h             |  80 ++++++
 llvm/lib/IR/OptBisect.cpp                     |  52 ++--
 llvm/lib/Support/CMakeLists.txt               |   1 +
 llvm/lib/Support/DebugCounter.cpp             |  79 +-----
 llvm/lib/Support/Range.cpp                    | 140 +++++++++++
 llvm/test/Other/debugcounter-multi-ranges.ll  |  38 +++
 llvm/test/Other/opt-bisect-ranges.ll          |  35 +++
 llvm/test/Other/opt-disable-indices.ll        |  15 ++
 .../reduce-chunk-list/reduce-chunk-list.cpp   |  47 ++--
 llvm/unittests/Support/CMakeLists.txt         |   1 +
 llvm/unittests/Support/RangeTest.cpp          | 233 ++++++++++++++++++
 13 files changed, 629 insertions(+), 149 deletions(-)
 create mode 100644 llvm/include/llvm/Support/Range.h
 create mode 100644 llvm/lib/Support/Range.cpp
 create mode 100644 llvm/test/Other/debugcounter-multi-ranges.ll
 create mode 100644 llvm/test/Other/opt-bisect-ranges.ll
 create mode 100644 llvm/test/Other/opt-disable-indices.ll
 create mode 100644 llvm/unittests/Support/RangeTest.cpp

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index bc2294a870389..901fb5625aefd 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -14,11 +14,12 @@
 #ifndef LLVM_IR_OPTBISECT_H
 #define LLVM_IR_OPTBISECT_H
 
-#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Compiler.h"
-#include <limits>
+#include "llvm/Support/Range.h"
 
 namespace llvm {
 
@@ -41,7 +42,7 @@ class OptPassGate {
 
 /// This class implements a mechanism to disable passes and individual
 /// optimizations at compile time based on a command line option
-/// (-opt-bisect-limit) in order to perform a bisecting search for
+/// (-opt-bisect) in order to perform a bisecting search for
 /// optimization-related problems.
 class LLVM_ABI OptBisect : public OptPassGate {
 public:
@@ -54,12 +55,12 @@ class LLVM_ABI OptBisect : public OptPassGate {
 
   virtual ~OptBisect() = default;
 
-  /// Checks the bisect limit to determine if the specified pass should run.
+  /// Checks the bisect ranges to determine if the specified pass should run.
   ///
   /// The method prints the name of the pass, its assigned bisect number, and
   /// whether or not the pass will be executed. It returns true if the pass
-  /// should run, i.e. if the bisect limit is set to -1 or has not yet been
-  /// exceeded.
+  /// should run, i.e. if no ranges are specified or the current pass number
+  /// falls within one of the specified ranges.
   ///
   /// Most passes should not call this routine directly. Instead, it is called
   /// through helper routines provided by the base classes of the pass. For
@@ -68,27 +69,28 @@ class LLVM_ABI OptBisect : public OptPassGate {
                      StringRef IRDescription) const override;
 
   /// isEnabled() should return true before calling shouldRunPass().
-  bool isEnabled() const override {
-    return BisectLimit != Disabled || !BisectSkipNumbers.empty();
-  }
+  bool isEnabled() const override { return !BisectRanges.empty(); }
 
-  /// Add pass at index Index to the list of passes to skip
-  /// during bisection.
-  void disablePassAtIndex(int Index) { BisectSkipNumbers.insert(Index); }
+  /// Parse range specification and set the ranges for bisection.
+  /// Range format: "1-10,20-30,45" (runs passes 1-10, 20-30, and 45)
+  /// Returns true on parsing error.
+  bool parseRanges(StringRef RangeStr);
 
-  /// Set the new optimization limit and reset the counter. Passing
-  /// OptBisect::Disabled disables the limiting.
-  void setLimit(int Limit) {
-    BisectLimit = Limit;
+  /// Set ranges programmatically (for testing or other uses).
+  void setRanges(ArrayRef<Range> Ranges) { 
+    BisectRanges.assign(Ranges.begin(), Ranges.end());
     LastBisectNum = 0;
   }
 
-  static constexpr int Disabled = std::numeric_limits<int>::max();
+  /// Clear all ranges, effectively disabling bisection.
+  void clearRanges() { 
+    BisectRanges.clear();
+    LastBisectNum = 0;
+  }
 
 private:
-  int BisectLimit = Disabled;
   mutable int LastBisectNum = 0;
-  SmallSet<int, 4> BisectSkipNumbers;
+  RangeUtils::RangeList BisectRanges;
 };
 
 /// This class implements a mechanism to disable passes and individual
diff --git a/llvm/include/llvm/Support/DebugCounter.h b/llvm/include/llvm/Support/DebugCounter.h
index 89349d1ebffee..d6f46ce2346d8 100644
--- a/llvm/include/llvm/Support/DebugCounter.h
+++ b/llvm/include/llvm/Support/DebugCounter.h
@@ -48,6 +48,7 @@
 #include "llvm/ADT/UniqueVector.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Debug.h"
+#include "llvm/Support/Range.h"
 #include <string>
 
 namespace llvm {
@@ -56,18 +57,10 @@ class raw_ostream;
 
 class DebugCounter {
 public:
-  struct Chunk {
-    int64_t Begin;
-    int64_t End;
-    LLVM_ABI void print(llvm::raw_ostream &OS);
-    bool contains(int64_t Idx) const { return Idx >= Begin && Idx <= End; }
-  };
-
-  LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Chunk>);
+  // For backward compatibility, alias Range as Chunk
+  using Chunk = Range;
 
-  /// Return true on parsing error and print the error message on the
-  /// llvm::errs()
-  LLVM_ABI static bool parseChunks(StringRef Str, SmallVector<Chunk> &Res);
+  LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Range> Ranges);
 
   /// Returns a reference to the singleton instance.
   LLVM_ABI static DebugCounter &instance();
@@ -176,7 +169,7 @@ class DebugCounter {
     uint64_t CurrChunkIdx = 0;
     bool IsSet = false;
     std::string Desc;
-    SmallVector<Chunk> Chunks;
+    SmallVector<Range> Chunks;
   };
 
   DenseMap<unsigned, CounterInfo> Counters;
diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
new file mode 100644
index 0000000000000..c05cced7c7912
--- /dev/null
+++ b/llvm/include/llvm/Support/Range.h
@@ -0,0 +1,80 @@
+//===- llvm/Support/Range.h - Range parsing utility -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides utilities for parsing range specifications like "1-10,20-30,45"
+// which are commonly used in debugging and bisection tools.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_RANGE_H
+#define LLVM_SUPPORT_RANGE_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <cstdint>
+
+namespace llvm {
+class raw_ostream;
+} // end namespace llvm
+
+namespace llvm {
+
+/// Represents a range of integers [Begin, End] (inclusive on both ends)
+struct Range {
+  int64_t Begin;
+  int64_t End;
+
+  Range(int64_t Begin, int64_t End) : Begin(Begin), End(End) {}
+  Range(int64_t Single) : Begin(Single), End(Single) {}
+
+  /// Check if the given value is within this range (inclusive)
+  bool contains(int64_t Value) const { return Value >= Begin && Value <= End; }
+
+  /// Check if this range overlaps with another range
+  bool overlaps(const Range &Other) const {
+    return Begin <= Other.End && End >= Other.Begin;
+  }
+
+  /// Get the size of this range
+  int64_t size() const { return End - Begin + 1; }
+
+  bool operator==(const Range &Other) const {
+    return Begin == Other.Begin && End == Other.End;
+  }
+};
+
+/// Utility class for parsing and managing range specifications
+class RangeUtils {
+public:
+  using RangeList = SmallVector<Range, 8>;
+
+  /// Parse a range specification string like "1-10,20-30,45" or "1-10:20-30:45"
+  /// Returns true on error, false on success
+  /// \param RangeStr The string to parse
+  /// \param Ranges Output list of parsed ranges
+  /// \param Separator The separator character to use (',' or ':')
+  static bool parseRanges(StringRef RangeStr, RangeList &Ranges, char Separator = ',');
+
+  /// Check if a value is contained in any of the ranges
+  static bool contains(const RangeList &Ranges, int64_t Value);
+
+  /// Convert ranges back to string representation for debugging
+  static std::string rangesToString(const RangeList &Ranges, char Separator = ',');
+
+  /// Print ranges to output stream (DebugCounter-compatible)
+  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges);
+
+  /// Merge adjacent/consecutive ranges into single ranges
+  /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10]
+  static RangeList mergeAdjacentRanges(ArrayRef<Range> Ranges);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_SUPPORT_RANGE_H
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index e5d64a02455ac..ad0dd8930ecc1 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -13,10 +13,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/IR/OptBisect.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Pass.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Range.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
+#include <cstdlib>
 
 using namespace llvm;
 
@@ -31,19 +34,34 @@ static OptDisable &getOptDisabler() {
 }
 
 static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
-                                   cl::init(OptBisect::Disabled), cl::Optional,
+                                   cl::init(-1), cl::Optional,
                                    cl::cb<void, int>([](int Limit) {
-                                     getOptBisector().setLimit(Limit);
+                                     if (Limit == -1) {
+                                       // -1 means run all passes, which is equivalent to no ranges
+                                       getOptBisector().clearRanges();
+                                     } else if (Limit > 0) {
+                                       // Convert limit to range 1-Limit
+                                       std::string RangeStr = "1-" + llvm::utostr(Limit);
+                                       if (getOptBisector().parseRanges(RangeStr)) {
+                                         errs() << "Error: Invalid limit for -opt-bisect-limit: " 
+                                                << Limit << "\n";
+                                         exit(1);
+                                       }
+                                     }
                                    }),
-                                   cl::desc("Maximum optimization to perform"));
-
-static cl::list<int>
-    OptDisableIndices("opt-disable-indices", cl::Hidden, cl::CommaSeparated,
-                      cl::Optional, cl::cb<void, int>([](int Index) {
-                        getOptBisector().disablePassAtIndex(Index);
-                      }),
-                      cl::desc("Disable passes at the given indices in the "
-                               "optimization pipeline (comma-separated list)"));
+                                   cl::desc("Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));
+
+static cl::opt<std::string> OptBisectRanges(
+    "opt-bisect", cl::Hidden, cl::Optional,
+    cl::cb<void, const std::string &>([](const std::string &RangeStr) {
+      if (getOptBisector().parseRanges(RangeStr)) {
+        errs() << "Error: Invalid range specification for -opt-bisect: " 
+               << RangeStr << "\n";
+        exit(1);
+      }
+    }),
+    cl::desc("Run optimization passes only for the specified ranges. "
+             "Format: '1-10,20-30,45' (runs passes 1-10, 20-30, and 45)"));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
@@ -69,14 +87,20 @@ static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
          << " on " << TargetDesc << '\n';
 }
 
+bool OptBisect::parseRanges(StringRef RangeStr) {
+  LastBisectNum = 0;
+  return RangeUtils::parseRanges(RangeStr, BisectRanges);
+}
+
 bool OptBisect::shouldRunPass(StringRef PassName,
                               StringRef IRDescription) const {
   assert(isEnabled());
 
   int CurBisectNum = ++LastBisectNum;
-  bool ShouldRun = (BisectLimit == -1 || BisectLimit == Disabled ||
-                    CurBisectNum <= BisectLimit) &&
-                   !BisectSkipNumbers.contains(CurBisectNum);
+  
+  // Check if current pass number falls within any of the specified ranges
+  bool ShouldRun = RangeUtils::contains(BisectRanges, CurBisectNum);
+  
   if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
   return ShouldRun;
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 10b6101d73277..f6c64579cec5c 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -231,6 +231,7 @@ add_llvm_component_library(LLVMSupport
   PluginLoader.cpp
   PrettyStackTrace.cpp
   RandomNumberGenerator.cpp
+  Range.cpp
   Regex.cpp
   RewriteBuffer.cpp
   RewriteRope.cpp
diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index 6b65720440f30..ce01400893724 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -9,77 +9,8 @@ using namespace llvm;
 
 namespace llvm {
 
-void DebugCounter::Chunk::print(llvm::raw_ostream &OS) {
-  if (Begin == End)
-    OS << Begin;
-  else
-    OS << Begin << "-" << End;
-}
-
-void DebugCounter::printChunks(raw_ostream &OS, ArrayRef<Chunk> Chunks) {
-  if (Chunks.empty()) {
-    OS << "empty";
-  } else {
-    bool IsFirst = true;
-    for (auto E : Chunks) {
-      if (!IsFirst)
-        OS << ':';
-      else
-        IsFirst = false;
-      E.print(OS);
-    }
-  }
-}
-
-bool DebugCounter::parseChunks(StringRef Str, SmallVector<Chunk> &Chunks) {
-  StringRef Remaining = Str;
-
-  auto ConsumeInt = [&]() -> int64_t {
-    StringRef Number =
-        Remaining.take_until([](char c) { return c < '0' || c > '9'; });
-    int64_t Res;
-    if (Number.getAsInteger(10, Res)) {
-      errs() << "Failed to parse int at : " << Remaining << "\n";
-      return -1;
-    }
-    Remaining = Remaining.drop_front(Number.size());
-    return Res;
-  };
-
-  while (1) {
-    int64_t Num = ConsumeInt();
-    if (Num == -1)
-      return true;
-    if (!Chunks.empty() && Num <= Chunks[Chunks.size() - 1].End) {
-      errs() << "Expected Chunks to be in increasing order " << Num
-             << " <= " << Chunks[Chunks.size() - 1].End << "\n";
-      return true;
-    }
-    if (Remaining.starts_with("-")) {
-      Remaining = Remaining.drop_front();
-      int64_t Num2 = ConsumeInt();
-      if (Num2 == -1)
-        return true;
-      if (Num >= Num2) {
-        errs() << "Expected " << Num << " < " << Num2 << " in " << Num << "-"
-               << Num2 << "\n";
-        return true;
-      }
-
-      Chunks.push_back({Num, Num2});
-    } else {
-      Chunks.push_back({Num, Num});
-    }
-    if (Remaining.starts_with(":")) {
-      Remaining = Remaining.drop_front();
-      continue;
-    }
-    if (Remaining.empty())
-      break;
-    errs() << "Failed to parse at : " << Remaining;
-    return true;
-  }
-  return false;
+void DebugCounter::printChunks(raw_ostream &OS, ArrayRef<Range> Ranges) {
+  RangeUtils::printRanges(OS, Ranges);
 }
 
 } // namespace llvm
@@ -185,11 +116,13 @@ void DebugCounter::push_back(const std::string &Val) {
     return;
   }
   StringRef CounterName = CounterPair.first;
-  SmallVector<Chunk> Chunks;
+  RangeUtils::RangeList TempRanges;
+  SmallVector<Range> Chunks;
 
-  if (parseChunks(CounterPair.second, Chunks)) {
+  if (RangeUtils::parseRanges(CounterPair.second, TempRanges, ':')) {
     return;
   }
+  Chunks.assign(TempRanges.begin(), TempRanges.end());
 
   unsigned CounterID = getCounterId(std::string(CounterName));
   if (!CounterID) {
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
new file mode 100644
index 0000000000000..4b1952ab8e202
--- /dev/null
+++ b/llvm/lib/Support/Range.cpp
@@ -0,0 +1,140 @@
+//===- llvm/Support/Range.cpp - Range parsing utility ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Range.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include <sstream>
+
+using namespace llvm;
+
+bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
+  Ranges.clear();
+  
+  if (Str.empty())
+    return false;
+  
+  // Split by the specified separator
+  SmallVector<StringRef, 8> Parts;
+  Str.split(Parts, Separator, -1, false);
+  
+  // Regex to match either single number or range "num1-num2"
+  Regex RangeRegex("^([0-9]+)(-([0-9]+))?$");
+  
+  for (StringRef Part : Parts) {
+    Part = Part.trim();
+    if (Part.empty())
+      continue;
+      
+    SmallVector<StringRef, 4> Matches;
+    if (!RangeRegex.match(Part, &Matches)) {
+      errs() << "Invalid range format: '" << Part << "'\n";
+      return true;
+    }
+    
+    int64_t Begin, End;
+    if (Matches[1].getAsInteger(10, Begin)) {
+      errs() << "Failed to parse number: '" << Matches[1] << "'\n";
+      return true;
+    }
+    
+    if (!Matches[3].empty()) {
+      // Range format "begin-end"
+      if (Matches[3].getAsInteger(10, End)) {
+        errs() << "Failed to parse number: '" << Matches[3] << "'\n";
+        return true;
+      }
+      if (Begin >= End) {
+        errs() << "Invalid range: " << Begin << " >= " << End << "\n";
+        return true;
+      }
+    } else {
+      // Single number
+      End = Begin;
+    }
+    
+    // Check ordering constraint (ranges must be in increasing order)
+    if (!Ranges.empty() && Begin <= Ranges.back().End) {
+      errs() << "Expected ranges to be in increasing order: " << Begin
+             << " <= " << Ranges.back().End << "\n";
+      return true;
+    }
+    
+    Ranges.push_back(Range(Begin, End));
+  }
+  
+  return false;
+}
+
+bool RangeUtils::contains(const RangeList &Ranges, int64_t Value) {
+  for (const Range &R : Ranges) {
+    if (R.contains(Value))
+      return true;
+  }
+  return false;
+}
+
+
+
+std::string RangeUtils::rangesToString(const RangeList &Ranges, char Separator) {
+  std::ostringstream OS;
+  for (size_t I = 0; I < Ranges.size(); ++I) {
+    if (I > 0)
+      OS << Separator;
+    const Range &R = Ranges[I];
+    if (R.Begin == R.End) {
+      OS << R.Begin;
+    } else {
+      OS << R.Begin << "-" << R.End;
+    }
+  }
+  return OS.str();
+}
+
+void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges) {
+  if (Ranges.empty()) {
+    OS << "empty";
+  } else {
+    bool IsFirst = true;
+    for (const Range &R : Ranges) {
+      if (!IsFirst)
+        OS << ':';
+      else
+        IsFirst = false;
+      
+      if (R.Begin == R.End)
+        OS << R.Begin;
+      else
+        OS << R.Begin << "-" << R.End;
+    }
+  }
+}
+
+RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(ArrayRef<Range> Ranges) {
+  if (Ranges.empty())
+    return {};
+    
+  RangeList Result;
+  Result.push_back(Ranges[0]);
+  
+  for (size_t I = 1; I < Ranges.size(); ++I) {
+    const Range &Current = Ranges[I];
+    Range &Last = Result.back();
+    
+    // Check if current range is adjacent to the last merged range
+    if (Current.Begin == Last.End + 1) {
+      // Merge by extending the end of the last range
+      Last.End = Current.End;
+    } else {
+      // Not adjacent, add as separate range
+      Result.push_back(Current);
+    }
+  }
+  
+  return Result;
+}
diff --git a/llvm/test/Other/debugcounter-multi-ranges.ll b/llvm/test/Other/debugcounter-multi-ranges.ll
new file mode 100644
index 0000000000000..30cdbc6e40c0b
--- /dev/null
+++ b/llvm/test/Other/debugcounter-multi-ranges.ll
@@ -0,0 +1,38 @@
+; REQUIRES: asserts
+; Test debug counter with multiple ranges using colon separators
+; (DebugCounter uses colon separators to avoid conflicts with cl::CommaSeparated)
+
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=1:3:5 < %s | FileCheck %s --check-prefix=CHECK-COLON
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-2:4:6-7 < %s | FileCheck %s --check-prefix=CHECK-MIXED-COLON
+
+; Test that with debug counters on, we can selectively apply transformations
+; using different range syntaxes. All variants should produce the same result.
+
+; Original function has 8 dead instructions that DCE can eliminate
+define void @test() {
+  %dead1 = add i32 1, 2
+  %dead2 = add i32 3, 4  
+  %dead3 = add i32 5, 6
+  %dead4 = add i32 7, 8
+  %dead5 = add i32 9, 10
+  %dead6 = add i32 11, 12
+  %dead7 = add i32 13, 14
+  %dead8 = add i32 15, 16
+  ret void
+}
+
+; Test colon separator: apply transformations 1, 3, 5 (eliminate dead2, dead4, dead6)
+; CHECK-COLON-LABEL: @test
+; CHECK-COLON-NEXT: %dead1 = add i32 1, 2
+; CHECK-COLON-NEXT: %dead3 = add i32 5, 6
+; CHECK-COLON-NEXT: %dead5 = add i32 9, 10
+; CHECK-COLON-NEXT: %dead7 = add i32 13, 14
+; CHECK-COLON-NEXT: %dead8 = add i32 15, 16
+; CHECK-COLON-NEXT: ret void
+
+; Test mixed ranges with colon: apply transformations 1-2, 4, 6-7 (eliminate dead2, dead3, dead5, dead7, dead8)
+; CHECK-MIXED-COLON-LABEL: @test
+; CHECK-MIXED-COLON-NEXT: %dead1 = add i32 1, 2
+; CHECK-MIXED-COLON-NEXT: %dead4 = add i32 7, 8
+; CHECK-MIXED-COLON-NEXT: %dead6 = add i32 11, 12
+; CHECK-MIXED-COLON-NEXT: ret void
diff --git a/llvm/test/Other/opt-bisect-ranges.ll b/llvm/test/Other/opt-bisect-ranges.ll
new file mode 100644
index 0000000000000..145cf169ca370
--- /dev/null
+++ b/llvm/test/Other/opt-bisect-ranges.ll
@@ -0,0 +1,35 @@
+; Test that verifies functionality for -opt-bisect with range specifications
+
+; Test basic range functionality: run passes 1-3 and 7-8
+; RUN: opt -O1 -opt-bisect=1-3,7-8 %s 2>&1 | FileCheck %s --check-prefix=CHECK-RANGES
+; CHECK-RANGES: BISECT: running pass (1) annotation2metadata on [module]
+; CHECK-RANGES: BISECT: running pass (2) forceattrs on [module]
+; CHECK-RANGES: BISECT: running pass (3) inferattrs on [module]
+; CHECK-RANGES: BISECT: NOT running pass (4) lower-expect on foo
+; CHECK-RANGES: BISECT: NOT running pass (5) simplifycfg on foo
+; CHECK-RANGES: BISECT: NOT running pass (6) sroa on foo
+; CHECK-RANGES: BISECT: running pass (7) early-cse on foo
+; CHECK-RANGES: BISECT: running pass (8) openmp-opt on [module]
+
+; Test single pass selection: run only pass 5
+; RUN: opt -O1 -opt-bisect=5 %s 2>&1 | FileCheck %s --check-prefix=CHECK-SINGLE
+; CHECK-SINGLE: BISECT: NOT running pass (1) annotation2metadata on [module]
+; CHECK-SINGLE: BISECT: NOT running pass (2) forceattrs on [module]
+; CHECK-SINGLE: BISECT: NOT running pass (3) inferattrs on [module]
+; CHECK-SINGLE: BISECT: NOT running pass (4) lower-expect on foo
+; CHECK-SINGLE: BISECT: running pass (5) simplifycfg on foo
+; CHECK-SINGLE: BISECT: NOT running pass (6) sroa on foo
+
+; Test backward compatibility: -opt-bisect-limit=3 should be equivalent to -opt-bisect=1-3
+; RUN: opt -O1 -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
+; CHECK-LIMIT: BISECT: running pass (1) annotation2metadata on [module]
+; CHECK-LIMIT: BISECT: running pass (2) forceattrs on [module]
+; CHECK-LIMIT: BISECT: running pass (3) inferattrs on [module]
+; CHECK-LIMIT: BISECT: NOT running pass (4) lower-expect on foo
+; CHECK-LIMIT: BISECT: NOT running pass (5) simplifycfg on foo
+
+define void @foo() {
+  ret void
+}
+
+
diff --git a/llvm/test/Other/opt-disable-indices.ll b/llvm/test/Other/opt-disable-indices.ll
new file mode 100644
index 0000000000000..52c2b5b1c4ffa
--- /dev/null
+++ b/llvm/test/Other/opt-disable-indices.ll
@@ -0,0 +1,15 @@
+; Test that verifies functionality for -opt-disable-indices
+
+; RUN: opt -O1 -opt-disable-indices=3,7 %s 2>&1 | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
+; CHECK-DISABLE-PASS: BISECT: running pass (1) annotation2metadata on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (2) forceattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) inferattrs on [module]
+; CHECK-DISABLE-PASS: BISECT: running pass (4) lower-expect on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (5) simplifycfg on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (6) sroa on foo
+; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on foo
+; CHECK-DISABLE-PASS: BISECT: running pass (8) openmp-opt on [module]
+
+define void @foo() {
+  ret void
+}
diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
index dc99859b516db..687367b351a6c 100644
--- a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
+++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
@@ -11,10 +11,9 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/DenseSet.h"
 #include "llvm/Support/CommandLine.h"
-#include "llvm/Support/DebugCounter.h"
 #include "llvm/Support/Program.h"
+#include "llvm/Support/Range.h"
 
 using namespace llvm;
 
@@ -24,29 +23,15 @@ static cl::opt<std::string> StartChunks(cl::Positional, cl::Required);
 
 static cl::opt<bool> Pessimist("pessimist", cl::init(false));
 
-using Chunk = DebugCounter::Chunk;
-
 namespace {
 
-SmallVector<Chunk> simplifyChunksList(ArrayRef<Chunk> Chunks) {
-  SmallVector<Chunk> Res;
-  Res.push_back(Chunks.front());
-  for (unsigned Idx = 1; Idx < Chunks.size(); Idx++) {
-    if (Chunks[Idx].Begin == Res.back().End + 1)
-      Res.back().End = Chunks[Idx].End;
-    else
-      Res.push_back(Chunks[Idx]);
-  }
-  return Res;
-}
-
-bool isStillInteresting(ArrayRef<Chunk> Chunks) {
-  SmallVector<Chunk> SimpleChunks = simplifyChunksList(Chunks);
+bool isStillInteresting(ArrayRef<Range> Chunks) {
+  RangeUtils::RangeList SimpleChunks = RangeUtils::mergeAdjacentRanges(Chunks);
 
   std::string ChunkStr;
   {
     raw_string_ostream OS(ChunkStr);
-    DebugCounter::printChunks(OS, SimpleChunks);
+    RangeUtils::printRanges(OS, SimpleChunks);
   }
 
   errs() << "Checking with: " << ChunkStr << "\n";
@@ -73,18 +58,18 @@ bool isStillInteresting(ArrayRef<Chunk> Chunks) {
   return Res;
 }
 
-bool increaseGranularity(SmallVector<Chunk> &Chunks) {
+bool increaseGranularity(RangeUtils::RangeList &Chunks) {
   errs() << "Increasing granularity\n";
-  SmallVector<Chunk> NewChunks;
+  RangeUtils::RangeList NewChunks;
   bool SplitOne = false;
 
   for (auto &C : Chunks) {
     if (C.Begin == C.End) {
       NewChunks.push_back(C);
     } else {
-      int Half = (C.Begin + C.End) / 2;
-      NewChunks.push_back({C.Begin, Half});
-      NewChunks.push_back({Half + 1, C.End});
+      int64_t Half = (C.Begin + C.End) / 2;
+      NewChunks.push_back(Range(C.Begin, Half));
+      NewChunks.push_back(Range(Half + 1, C.End));
       SplitOne = true;
     }
   }
@@ -99,10 +84,9 @@ bool increaseGranularity(SmallVector<Chunk> &Chunks) {
 int main(int argc, char **argv) {
   cl::ParseCommandLineOptions(argc, argv);
 
-  SmallVector<Chunk> CurrChunks;
-  if (DebugCounter::parseChunks(StartChunks, CurrChunks)) {
+  RangeUtils::RangeList CurrChunks;
+  if (RangeUtils::parseRanges(StartChunks, CurrChunks, ','))
     return 1;
-  }
 
   auto Program = sys::findProgramByName(ReproductionCmd);
   if (!Program) {
@@ -126,9 +110,10 @@ int main(int argc, char **argv) {
       if (CurrChunks.size() == 1)
         break;
 
-      Chunk Testing = CurrChunks[Idx];
-      errs() << "Trying to remove : ";
-      Testing.print(errs());
+      Range Testing = CurrChunks[Idx];
+      errs() << "Trying to remove : " << Testing.Begin;
+      if (Testing.Begin != Testing.End)
+        errs() << "-" << Testing.End;
       errs() << "\n";
 
       CurrChunks.erase(CurrChunks.begin() + Idx);
@@ -142,6 +127,6 @@ int main(int argc, char **argv) {
   }
 
   errs() << "Minimal Chunks = ";
-  DebugCounter::printChunks(llvm::errs(), simplifyChunksList(CurrChunks));
+  RangeUtils::printRanges(llvm::errs(), RangeUtils::mergeAdjacentRanges(CurrChunks));
   errs() << "\n";
 }
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index 868c40b13b9b2..e1821b0172960 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -74,6 +74,7 @@ add_llvm_unittest(SupportTests
   ProcessTest.cpp
   ProgramTest.cpp
   ProgramStackTest.cpp
+  RangeTest.cpp
   RecyclerTest.cpp
   RegexTest.cpp
   ReverseIterationTest.cpp
diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
new file mode 100644
index 0000000000000..d4a91327bd992
--- /dev/null
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -0,0 +1,233 @@
+//===- llvm/unittests/Support/RangeTest.cpp - Range tests ----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/Range.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+TEST(RangeTest, BasicRange) {
+  Range R(5, 10);
+  EXPECT_EQ(R.Begin, 5);
+  EXPECT_EQ(R.End, 10);
+  EXPECT_TRUE(R.contains(5));
+  EXPECT_TRUE(R.contains(7));
+  EXPECT_TRUE(R.contains(10));
+  EXPECT_FALSE(R.contains(4));
+  EXPECT_FALSE(R.contains(11));
+}
+
+TEST(RangeTest, SingleValueRange) {
+  Range R(42);
+  EXPECT_EQ(R.Begin, 42);
+  EXPECT_EQ(R.End, 42);
+  EXPECT_TRUE(R.contains(42));
+  EXPECT_FALSE(R.contains(41));
+  EXPECT_FALSE(R.contains(43));
+}
+
+TEST(RangeTest, RangeOverlaps) {
+  Range R1(1, 5);
+  Range R2(3, 8);
+  Range R3(6, 10);
+  Range R4(11, 15);
+  
+  EXPECT_TRUE(R1.overlaps(R2));
+  EXPECT_TRUE(R2.overlaps(R1));
+  EXPECT_TRUE(R2.overlaps(R3));
+  EXPECT_FALSE(R1.overlaps(R3));
+  EXPECT_FALSE(R1.overlaps(R4));
+  EXPECT_FALSE(R3.overlaps(R4));
+}
+
+TEST(RangeUtilsTest, ParseSingleNumber) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("42", Ranges));
+  EXPECT_EQ(Ranges.size(), 1U);
+  EXPECT_EQ(Ranges[0].Begin, 42);
+  EXPECT_EQ(Ranges[0].End, 42);
+}
+
+TEST(RangeUtilsTest, ParseSingleRange) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("10-20", Ranges));
+  EXPECT_EQ(Ranges.size(), 1U);
+  EXPECT_EQ(Ranges[0].Begin, 10);
+  EXPECT_EQ(Ranges[0].End, 20);
+}
+
+TEST(RangeUtilsTest, ParseMultipleRanges) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  EXPECT_EQ(Ranges.size(), 3U);
+  
+  // Ranges are in input order (DebugCounter style)
+  EXPECT_EQ(Ranges[0].Begin, 1);
+  EXPECT_EQ(Ranges[0].End, 5);
+  EXPECT_EQ(Ranges[1].Begin, 10);
+  EXPECT_EQ(Ranges[1].End, 10);
+  EXPECT_EQ(Ranges[2].Begin, 15);
+  EXPECT_EQ(Ranges[2].End, 20);
+}
+
+TEST(RangeUtilsTest, ParseColonSeparated) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5:10:15-20", Ranges));
+  EXPECT_EQ(Ranges.size(), 3U);
+  EXPECT_EQ(Ranges[0].Begin, 1);
+  EXPECT_EQ(Ranges[0].End, 5);
+  EXPECT_EQ(Ranges[1].Begin, 10);
+  EXPECT_EQ(Ranges[1].End, 10);
+  EXPECT_EQ(Ranges[2].Begin, 15);
+  EXPECT_EQ(Ranges[2].End, 20);
+}
+
+TEST(RangeUtilsTest, ParseEmptyString) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("", Ranges));
+  EXPECT_TRUE(Ranges.empty());
+}
+
+TEST(RangeUtilsTest, ParseInvalidRanges) {
+  RangeUtils::RangeList Ranges;
+  
+  // Invalid number
+  EXPECT_TRUE(RangeUtils::parseRanges("abc", Ranges));
+  
+  // Invalid range (begin > end)
+  EXPECT_TRUE(RangeUtils::parseRanges("10-5", Ranges));
+  
+  // Out of order ranges (DebugCounter constraint)
+  EXPECT_TRUE(RangeUtils::parseRanges("10,5", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping
+}
+
+TEST(RangeUtilsTest, Contains) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 1));
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 3));
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 5));
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 10));
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 15));
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 18));
+  EXPECT_TRUE(RangeUtils::contains(Ranges, 20));
+  
+  EXPECT_FALSE(RangeUtils::contains(Ranges, 6));
+  EXPECT_FALSE(RangeUtils::contains(Ranges, 9));
+  EXPECT_FALSE(RangeUtils::contains(Ranges, 11));
+  EXPECT_FALSE(RangeUtils::contains(Ranges, 14));
+  EXPECT_FALSE(RangeUtils::contains(Ranges, 21));
+}
+
+TEST(RangeUtilsTest, RangesToString) {
+  RangeUtils::RangeList Ranges;
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  
+  std::string Result = RangeUtils::rangesToString(Ranges);
+  EXPECT_EQ(Result, "1-5,10,15-20");
+}
+
+TEST(RangeUtilsTest, SeparatorParameter) {
+  RangeUtils::RangeList ColonRanges, CommaRanges;
+  
+  // Test explicit separator parameters
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5:10:15-20", ColonRanges, ':'));
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", CommaRanges, ','));
+  
+  EXPECT_EQ(ColonRanges.size(), CommaRanges.size());
+  for (size_t I = 0; I < ColonRanges.size(); ++I) {
+    EXPECT_EQ(ColonRanges[I].Begin, CommaRanges[I].Begin);
+    EXPECT_EQ(ColonRanges[I].End, CommaRanges[I].End);
+  }
+  
+  // Test that both work with contains()
+  EXPECT_TRUE(RangeUtils::contains(ColonRanges, 3));
+  EXPECT_TRUE(RangeUtils::contains(CommaRanges, 3));
+  EXPECT_TRUE(RangeUtils::contains(ColonRanges, 10));
+  EXPECT_TRUE(RangeUtils::contains(CommaRanges, 10));
+  EXPECT_TRUE(RangeUtils::contains(ColonRanges, 18));
+  EXPECT_TRUE(RangeUtils::contains(CommaRanges, 18));
+  
+  EXPECT_FALSE(RangeUtils::contains(ColonRanges, 8));
+  EXPECT_FALSE(RangeUtils::contains(CommaRanges, 8));
+}
+
+TEST(RangeUtilsTest, DefaultCommaSeparator) {
+  RangeUtils::RangeList Ranges;
+  
+  // Test that comma is the default separator
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  EXPECT_EQ(Ranges.size(), 3U);
+  EXPECT_EQ(Ranges[0].Begin, 1);
+  EXPECT_EQ(Ranges[0].End, 5);
+  EXPECT_EQ(Ranges[1].Begin, 10);
+  EXPECT_EQ(Ranges[1].End, 10);
+  EXPECT_EQ(Ranges[2].Begin, 15);
+  EXPECT_EQ(Ranges[2].End, 20);
+}
+
+TEST(RangeTest, MergeAdjacentRanges) {
+  RangeUtils::RangeList Input, Expected, Result;
+  
+  // Empty input
+  Result = RangeUtils::mergeAdjacentRanges(Input);
+  EXPECT_TRUE(Result.empty());
+  
+  // Single range - no change
+  Input.push_back(Range(5, 10));
+  Expected.push_back(Range(5, 10));
+  Result = RangeUtils::mergeAdjacentRanges(Input);
+  EXPECT_EQ(Expected, Result);
+  
+  // Adjacent ranges should merge
+  Input.clear();
+  Expected.clear();
+  Input.push_back(Range(1, 3));
+  Input.push_back(Range(4, 6));
+  Input.push_back(Range(7, 9));
+  Expected.push_back(Range(1, 9));
+  Result = RangeUtils::mergeAdjacentRanges(Input);
+  EXPECT_EQ(Expected, Result);
+  
+  // Non-adjacent ranges should not merge
+  Input.clear();
+  Expected.clear();
+  Input.push_back(Range(1, 3));
+  Input.push_back(Range(5, 7)); // Gap between 3 and 5
+  Input.push_back(Range(10, 12)); // Gap between 7 and 10
+  Expected.push_back(Range(1, 3));
+  Expected.push_back(Range(5, 7));
+  Expected.push_back(Range(10, 12));
+  Result = RangeUtils::mergeAdjacentRanges(Input);
+  EXPECT_EQ(Expected, Result);
+  
+  // Mixed adjacent and non-adjacent
+  Input.clear();
+  Expected.clear();
+  Input.push_back(Range(1, 3));
+  Input.push_back(Range(4, 6)); // Adjacent to first
+  Input.push_back(Range(8, 10)); // Gap
+  Input.push_back(Range(11, 13)); // Adjacent to third
+  Input.push_back(Range(14, 16)); // Adjacent to fourth
+  Expected.push_back(Range(1, 6));   // Merged 1-3 and 4-6
+  Expected.push_back(Range(8, 16));  // Merged 8-10, 11-13, 14-16
+  Result = RangeUtils::mergeAdjacentRanges(Input);
+  EXPECT_EQ(Expected, Result);
+  
+  // Single numbers that are adjacent
+  Input.clear();
+  Expected.clear();
+  Input.push_back(Range(5));
+  Input.push_back(Range(6));
+  Input.push_back(Range(7));
+  Expected.push_back(Range(5, 7));
+  Result = RangeUtils::mergeAdjacentRanges(Input);
+  EXPECT_EQ(Expected, Result);
+}

>From d3f5add03c73de80b56a4bc8262d5b8a6273f000 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 04:53:04 +0000
Subject: [PATCH 08/31] small changes

---
 llvm/include/llvm/IR/OptBisect.h              |  2 +-
 llvm/include/llvm/Support/Range.h             |  2 +-
 llvm/lib/IR/OptBisect.cpp                     |  5 ++--
 llvm/lib/Support/DebugCounter.cpp             |  2 +-
 llvm/lib/Support/Range.cpp                    | 14 +++++------
 .../reduce-chunk-list/reduce-chunk-list.cpp   |  2 +-
 llvm/unittests/Support/RangeTest.cpp          | 24 +++++++++----------
 7 files changed, 25 insertions(+), 26 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 901fb5625aefd..680f4469402af 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -73,7 +73,7 @@ class LLVM_ABI OptBisect : public OptPassGate {
 
   /// Parse range specification and set the ranges for bisection.
   /// Range format: "1-10,20-30,45" (runs passes 1-10, 20-30, and 45)
-  /// Returns true on parsing error.
+  /// Returns false on parsing error.
   bool parseRanges(StringRef RangeStr);
 
   /// Set ranges programmatically (for testing or other uses).
diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index c05cced7c7912..56a92ffb1437a 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -55,7 +55,7 @@ class RangeUtils {
   using RangeList = SmallVector<Range, 8>;
 
   /// Parse a range specification string like "1-10,20-30,45" or "1-10:20-30:45"
-  /// Returns true on error, false on success
+  /// Returns false on error, true on success
   /// \param RangeStr The string to parse
   /// \param Ranges Output list of parsed ranges
   /// \param Separator The separator character to use (',' or ':')
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index ad0dd8930ecc1..3e29d8955900d 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -42,7 +42,7 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
                                      } else if (Limit > 0) {
                                        // Convert limit to range 1-Limit
                                        std::string RangeStr = "1-" + llvm::utostr(Limit);
-                                       if (getOptBisector().parseRanges(RangeStr)) {
+                                       if (!getOptBisector().parseRanges(RangeStr)) {
                                          errs() << "Error: Invalid limit for -opt-bisect-limit: " 
                                                 << Limit << "\n";
                                          exit(1);
@@ -54,7 +54,7 @@ static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
 static cl::opt<std::string> OptBisectRanges(
     "opt-bisect", cl::Hidden, cl::Optional,
     cl::cb<void, const std::string &>([](const std::string &RangeStr) {
-      if (getOptBisector().parseRanges(RangeStr)) {
+      if (!getOptBisector().parseRanges(RangeStr)) {
         errs() << "Error: Invalid range specification for -opt-bisect: " 
                << RangeStr << "\n";
         exit(1);
@@ -88,7 +88,6 @@ static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
 }
 
 bool OptBisect::parseRanges(StringRef RangeStr) {
-  LastBisectNum = 0;
   return RangeUtils::parseRanges(RangeStr, BisectRanges);
 }
 
diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index ce01400893724..fed7b0154c3de 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -119,7 +119,7 @@ void DebugCounter::push_back(const std::string &Val) {
   RangeUtils::RangeList TempRanges;
   SmallVector<Range> Chunks;
 
-  if (RangeUtils::parseRanges(CounterPair.second, TempRanges, ':')) {
+  if (!RangeUtils::parseRanges(CounterPair.second, TempRanges, ':')) {
     return;
   }
   Chunks.assign(TempRanges.begin(), TempRanges.end());
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index 4b1952ab8e202..0948d00f11ed4 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -17,7 +17,7 @@ bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
   Ranges.clear();
   
   if (Str.empty())
-    return false;
+    return true;
   
   // Split by the specified separator
   SmallVector<StringRef, 8> Parts;
@@ -34,24 +34,24 @@ bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
     SmallVector<StringRef, 4> Matches;
     if (!RangeRegex.match(Part, &Matches)) {
       errs() << "Invalid range format: '" << Part << "'\n";
-      return true;
+      return false;
     }
     
     int64_t Begin, End;
     if (Matches[1].getAsInteger(10, Begin)) {
       errs() << "Failed to parse number: '" << Matches[1] << "'\n";
-      return true;
+      return false;
     }
     
     if (!Matches[3].empty()) {
       // Range format "begin-end"
       if (Matches[3].getAsInteger(10, End)) {
         errs() << "Failed to parse number: '" << Matches[3] << "'\n";
-        return true;
+        return false;
       }
       if (Begin >= End) {
         errs() << "Invalid range: " << Begin << " >= " << End << "\n";
-        return true;
+        return false;
       }
     } else {
       // Single number
@@ -62,13 +62,13 @@ bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
     if (!Ranges.empty() && Begin <= Ranges.back().End) {
       errs() << "Expected ranges to be in increasing order: " << Begin
              << " <= " << Ranges.back().End << "\n";
-      return true;
+      return false;
     }
     
     Ranges.push_back(Range(Begin, End));
   }
   
-  return false;
+  return true;
 }
 
 bool RangeUtils::contains(const RangeList &Ranges, int64_t Value) {
diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
index 687367b351a6c..29cc32e05f7ee 100644
--- a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
+++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
@@ -85,7 +85,7 @@ int main(int argc, char **argv) {
   cl::ParseCommandLineOptions(argc, argv);
 
   RangeUtils::RangeList CurrChunks;
-  if (RangeUtils::parseRanges(StartChunks, CurrChunks, ','))
+  if (!RangeUtils::parseRanges(StartChunks, CurrChunks, ','))
     return 1;
 
   auto Program = sys::findProgramByName(ReproductionCmd);
diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index d4a91327bd992..5719e107b8ce8 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -47,7 +47,7 @@ TEST(RangeTest, RangeOverlaps) {
 
 TEST(RangeUtilsTest, ParseSingleNumber) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("42", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("42", Ranges));
   EXPECT_EQ(Ranges.size(), 1U);
   EXPECT_EQ(Ranges[0].Begin, 42);
   EXPECT_EQ(Ranges[0].End, 42);
@@ -55,7 +55,7 @@ TEST(RangeUtilsTest, ParseSingleNumber) {
 
 TEST(RangeUtilsTest, ParseSingleRange) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("10-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("10-20", Ranges));
   EXPECT_EQ(Ranges.size(), 1U);
   EXPECT_EQ(Ranges[0].Begin, 10);
   EXPECT_EQ(Ranges[0].End, 20);
@@ -63,7 +63,7 @@ TEST(RangeUtilsTest, ParseSingleRange) {
 
 TEST(RangeUtilsTest, ParseMultipleRanges) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
   EXPECT_EQ(Ranges.size(), 3U);
   
   // Ranges are in input order (DebugCounter style)
@@ -77,7 +77,7 @@ TEST(RangeUtilsTest, ParseMultipleRanges) {
 
 TEST(RangeUtilsTest, ParseColonSeparated) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5:10:15-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", Ranges));
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);
@@ -89,7 +89,7 @@ TEST(RangeUtilsTest, ParseColonSeparated) {
 
 TEST(RangeUtilsTest, ParseEmptyString) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("", Ranges));
   EXPECT_TRUE(Ranges.empty());
 }
 
@@ -97,19 +97,19 @@ TEST(RangeUtilsTest, ParseInvalidRanges) {
   RangeUtils::RangeList Ranges;
   
   // Invalid number
-  EXPECT_TRUE(RangeUtils::parseRanges("abc", Ranges));
+  EXPECT_FALSE(RangeUtils::parseRanges("abc", Ranges));
   
   // Invalid range (begin > end)
-  EXPECT_TRUE(RangeUtils::parseRanges("10-5", Ranges));
+  EXPECT_FALSE(RangeUtils::parseRanges("10-5", Ranges));
   
   // Out of order ranges (DebugCounter constraint)
-  EXPECT_TRUE(RangeUtils::parseRanges("10,5", Ranges));
+  EXPECT_FALSE(RangeUtils::parseRanges("10,5", Ranges));
   EXPECT_TRUE(RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping
 }
 
 TEST(RangeUtilsTest, Contains) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
   
   EXPECT_TRUE(RangeUtils::contains(Ranges, 1));
   EXPECT_TRUE(RangeUtils::contains(Ranges, 3));
@@ -128,7 +128,7 @@ TEST(RangeUtilsTest, Contains) {
 
 TEST(RangeUtilsTest, RangesToString) {
   RangeUtils::RangeList Ranges;
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
   
   std::string Result = RangeUtils::rangesToString(Ranges);
   EXPECT_EQ(Result, "1-5,10,15-20");
@@ -138,7 +138,7 @@ TEST(RangeUtilsTest, SeparatorParameter) {
   RangeUtils::RangeList ColonRanges, CommaRanges;
   
   // Test explicit separator parameters
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5:10:15-20", ColonRanges, ':'));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", ColonRanges, ':'));
   EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", CommaRanges, ','));
   
   EXPECT_EQ(ColonRanges.size(), CommaRanges.size());
@@ -163,7 +163,7 @@ TEST(RangeUtilsTest, DefaultCommaSeparator) {
   RangeUtils::RangeList Ranges;
   
   // Test that comma is the default separator
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);

>From 6f20867970219fea1b496ede854b0c6b3577f07b Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 18:19:58 +0000
Subject: [PATCH 09/31] remove opt-disable-indices

---
 llvm/test/Other/opt-disable-indices.ll | 15 ---------------
 1 file changed, 15 deletions(-)
 delete mode 100644 llvm/test/Other/opt-disable-indices.ll

diff --git a/llvm/test/Other/opt-disable-indices.ll b/llvm/test/Other/opt-disable-indices.ll
deleted file mode 100644
index 52c2b5b1c4ffa..0000000000000
--- a/llvm/test/Other/opt-disable-indices.ll
+++ /dev/null
@@ -1,15 +0,0 @@
-; Test that verifies functionality for -opt-disable-indices
-
-; RUN: opt -O1 -opt-disable-indices=3,7 %s 2>&1 | FileCheck %s --check-prefix=CHECK-DISABLE-PASS
-; CHECK-DISABLE-PASS: BISECT: running pass (1) annotation2metadata on [module]
-; CHECK-DISABLE-PASS: BISECT: running pass (2) forceattrs on [module]
-; CHECK-DISABLE-PASS: BISECT: NOT running pass (3) inferattrs on [module]
-; CHECK-DISABLE-PASS: BISECT: running pass (4) lower-expect on foo
-; CHECK-DISABLE-PASS: BISECT: running pass (5) simplifycfg on foo
-; CHECK-DISABLE-PASS: BISECT: running pass (6) sroa on foo
-; CHECK-DISABLE-PASS: BISECT: NOT running pass (7) early-cse on foo
-; CHECK-DISABLE-PASS: BISECT: running pass (8) openmp-opt on [module]
-
-define void @foo() {
-  ret void
-}

>From 5f8719360af8521b6d29782aa0e182e0eb2da0f3 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 21:07:57 +0000
Subject: [PATCH 10/31] remove

---
 llvm/include/llvm/IR/OptBisect.h | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 680f4469402af..1963601b95be9 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -76,12 +76,6 @@ class LLVM_ABI OptBisect : public OptPassGate {
   /// Returns false on parsing error.
   bool parseRanges(StringRef RangeStr);
 
-  /// Set ranges programmatically (for testing or other uses).
-  void setRanges(ArrayRef<Range> Ranges) { 
-    BisectRanges.assign(Ranges.begin(), Ranges.end());
-    LastBisectNum = 0;
-  }
-
   /// Clear all ranges, effectively disabling bisection.
   void clearRanges() { 
     BisectRanges.clear();

>From 8ab62b35ec7ab12cf3204f40b2e34ebb22d5a19a Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 21:46:06 +0000
Subject: [PATCH 11/31] cleanup

---
 llvm/include/llvm/Support/DebugCounter.h |  5 +----
 llvm/include/llvm/Support/Range.h        | 21 +++++++++++----------
 llvm/lib/Support/DebugCounter.cpp        | 10 ++--------
 llvm/lib/Support/Range.cpp               | 12 ++++++------
 llvm/unittests/Support/RangeTest.cpp     |  6 +++---
 5 files changed, 23 insertions(+), 31 deletions(-)

diff --git a/llvm/include/llvm/Support/DebugCounter.h b/llvm/include/llvm/Support/DebugCounter.h
index d6f46ce2346d8..f51a881ac4e24 100644
--- a/llvm/include/llvm/Support/DebugCounter.h
+++ b/llvm/include/llvm/Support/DebugCounter.h
@@ -57,9 +57,6 @@ class raw_ostream;
 
 class DebugCounter {
 public:
-  // For backward compatibility, alias Range as Chunk
-  using Chunk = Range;
-
   LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Range> Ranges);
 
   /// Returns a reference to the singleton instance.
@@ -169,7 +166,7 @@ class DebugCounter {
     uint64_t CurrChunkIdx = 0;
     bool IsSet = false;
     std::string Desc;
-    SmallVector<Range> Chunks;
+    RangeUtils::RangeList Chunks;
   };
 
   DenseMap<unsigned, CounterInfo> Counters;
diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 56a92ffb1437a..2041402c92925 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -30,11 +30,11 @@ struct Range {
   int64_t Begin;
   int64_t End;
 
-  Range(int64_t Begin, int64_t End) : Begin(Begin), End(End) {}
-  Range(int64_t Single) : Begin(Single), End(Single) {}
+  Range(const int64_t Begin, const int64_t End) : Begin(Begin), End(End) {}
+  Range(const int64_t Single) : Begin(Single), End(Single) {}
 
   /// Check if the given value is within this range (inclusive)
-  bool contains(int64_t Value) const { return Value >= Begin && Value <= End; }
+  bool contains(const int64_t Value) const { return Value >= Begin && Value <= End; }
 
   /// Check if this range overlaps with another range
   bool overlaps(const Range &Other) const {
@@ -54,25 +54,26 @@ class RangeUtils {
 public:
   using RangeList = SmallVector<Range, 8>;
 
-  /// Parse a range specification string like "1-10,20-30,45" or "1-10:20-30:45"
+  /// Parse a range specification string like "1-10,20-30,45" or "1-10:20-30:45".
+  /// Ranges must be in increasing order and non-overlapping.
   /// Returns false on error, true on success
   /// \param RangeStr The string to parse
   /// \param Ranges Output list of parsed ranges
   /// \param Separator The separator character to use (',' or ':')
-  static bool parseRanges(StringRef RangeStr, RangeList &Ranges, char Separator = ',');
+  static bool parseRanges(const StringRef RangeStr, RangeList &Ranges, const char Separator = ',');
 
   /// Check if a value is contained in any of the ranges
-  static bool contains(const RangeList &Ranges, int64_t Value);
+  static bool contains(const ArrayRef<Range> Ranges, const int64_t Value);
 
   /// Convert ranges back to string representation for debugging
-  static std::string rangesToString(const RangeList &Ranges, char Separator = ',');
+  static std::string rangesToString(const ArrayRef<Range> Ranges, const char Separator = ',');
 
-  /// Print ranges to output stream (DebugCounter-compatible)
-  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges);
+  /// Print ranges to output stream
+  static void printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges);
 
   /// Merge adjacent/consecutive ranges into single ranges
   /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10]
-  static RangeList mergeAdjacentRanges(ArrayRef<Range> Ranges);
+  static RangeList mergeAdjacentRanges(const ArrayRef<Range> Ranges);
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index fed7b0154c3de..a704119a38426 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -116,13 +116,6 @@ void DebugCounter::push_back(const std::string &Val) {
     return;
   }
   StringRef CounterName = CounterPair.first;
-  RangeUtils::RangeList TempRanges;
-  SmallVector<Range> Chunks;
-
-  if (!RangeUtils::parseRanges(CounterPair.second, TempRanges, ':')) {
-    return;
-  }
-  Chunks.assign(TempRanges.begin(), TempRanges.end());
 
   unsigned CounterID = getCounterId(std::string(CounterName));
   if (!CounterID) {
@@ -134,7 +127,8 @@ void DebugCounter::push_back(const std::string &Val) {
 
   CounterInfo &Counter = Counters[CounterID];
   Counter.IsSet = true;
-  Counter.Chunks = std::move(Chunks);
+  if (!RangeUtils::parseRanges(CounterPair.second, Counter.Chunks, ':'))
+    return;
 }
 
 void DebugCounter::print(raw_ostream &OS) const {
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index 0948d00f11ed4..d3b7b376adf7e 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -13,7 +13,7 @@
 
 using namespace llvm;
 
-bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
+bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges, const char Separator) {
   Ranges.clear();
   
   if (Str.empty())
@@ -24,7 +24,7 @@ bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
   Str.split(Parts, Separator, -1, false);
   
   // Regex to match either single number or range "num1-num2"
-  Regex RangeRegex("^([0-9]+)(-([0-9]+))?$");
+  const Regex RangeRegex("^([0-9]+)(-([0-9]+))?$");
   
   for (StringRef Part : Parts) {
     Part = Part.trim();
@@ -71,7 +71,7 @@ bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) {
   return true;
 }
 
-bool RangeUtils::contains(const RangeList &Ranges, int64_t Value) {
+bool RangeUtils::contains(const ArrayRef<Range> Ranges, const int64_t Value) {
   for (const Range &R : Ranges) {
     if (R.contains(Value))
       return true;
@@ -81,7 +81,7 @@ bool RangeUtils::contains(const RangeList &Ranges, int64_t Value) {
 
 
 
-std::string RangeUtils::rangesToString(const RangeList &Ranges, char Separator) {
+std::string RangeUtils::rangesToString(const ArrayRef<Range> Ranges, const char Separator) {
   std::ostringstream OS;
   for (size_t I = 0; I < Ranges.size(); ++I) {
     if (I > 0)
@@ -96,7 +96,7 @@ std::string RangeUtils::rangesToString(const RangeList &Ranges, char Separator)
   return OS.str();
 }
 
-void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges) {
+void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges) {
   if (Ranges.empty()) {
     OS << "empty";
   } else {
@@ -115,7 +115,7 @@ void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges) {
   }
 }
 
-RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(ArrayRef<Range> Ranges) {
+RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(const ArrayRef<Range> Ranges) {
   if (Ranges.empty())
     return {};
     
diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index 5719e107b8ce8..cddbf82c81c63 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -77,7 +77,7 @@ TEST(RangeUtilsTest, ParseMultipleRanges) {
 
 TEST(RangeUtilsTest, ParseColonSeparated) {
   RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", Ranges));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", Ranges, ':'));
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);
@@ -104,7 +104,7 @@ TEST(RangeUtilsTest, ParseInvalidRanges) {
   
   // Out of order ranges (DebugCounter constraint)
   EXPECT_FALSE(RangeUtils::parseRanges("10,5", Ranges));
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping
+  EXPECT_FALSE(RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping not allowed
 }
 
 TEST(RangeUtilsTest, Contains) {
@@ -139,7 +139,7 @@ TEST(RangeUtilsTest, SeparatorParameter) {
   
   // Test explicit separator parameters
   EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", ColonRanges, ':'));
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5,10,15-20", CommaRanges, ','));
+  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", CommaRanges, ','));
   
   EXPECT_EQ(ColonRanges.size(), CommaRanges.size());
   for (size_t I = 0; I < ColonRanges.size(); ++I) {

>From c3e5c7dc1bc66cb18e106654d12c16f70dbaa431 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 21:46:22 +0000
Subject: [PATCH 12/31] format

---
 llvm/include/llvm/IR/OptBisect.h              |  2 +-
 llvm/include/llvm/Support/Range.h             | 18 +++---
 llvm/lib/IR/OptBisect.cpp                     | 41 +++++++-------
 llvm/lib/Support/Range.cpp                    | 41 +++++++-------
 .../reduce-chunk-list/reduce-chunk-list.cpp   |  3 +-
 llvm/unittests/Support/RangeTest.cpp          | 55 ++++++++++---------
 6 files changed, 84 insertions(+), 76 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 1963601b95be9..fe2f808077ab4 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -77,7 +77,7 @@ class LLVM_ABI OptBisect : public OptPassGate {
   bool parseRanges(StringRef RangeStr);
 
   /// Clear all ranges, effectively disabling bisection.
-  void clearRanges() { 
+  void clearRanges() {
     BisectRanges.clear();
     LastBisectNum = 0;
   }
diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 2041402c92925..14f90c741edb0 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file provides utilities for parsing range specifications like "1-10,20-30,45"
-// which are commonly used in debugging and bisection tools.
+// This file provides utilities for parsing range specifications like
+// "1-10,20-30,45" which are commonly used in debugging and bisection tools.
 //
 //===----------------------------------------------------------------------===//
 
@@ -34,7 +34,9 @@ struct Range {
   Range(const int64_t Single) : Begin(Single), End(Single) {}
 
   /// Check if the given value is within this range (inclusive)
-  bool contains(const int64_t Value) const { return Value >= Begin && Value <= End; }
+  bool contains(const int64_t Value) const {
+    return Value >= Begin && Value <= End;
+  }
 
   /// Check if this range overlaps with another range
   bool overlaps(const Range &Other) const {
@@ -54,19 +56,21 @@ class RangeUtils {
 public:
   using RangeList = SmallVector<Range, 8>;
 
-  /// Parse a range specification string like "1-10,20-30,45" or "1-10:20-30:45".
-  /// Ranges must be in increasing order and non-overlapping.
+  /// Parse a range specification string like "1-10,20-30,45" or
+  /// "1-10:20-30:45". Ranges must be in increasing order and non-overlapping.
   /// Returns false on error, true on success
   /// \param RangeStr The string to parse
   /// \param Ranges Output list of parsed ranges
   /// \param Separator The separator character to use (',' or ':')
-  static bool parseRanges(const StringRef RangeStr, RangeList &Ranges, const char Separator = ',');
+  static bool parseRanges(const StringRef RangeStr, RangeList &Ranges,
+                          const char Separator = ',');
 
   /// Check if a value is contained in any of the ranges
   static bool contains(const ArrayRef<Range> Ranges, const int64_t Value);
 
   /// Convert ranges back to string representation for debugging
-  static std::string rangesToString(const ArrayRef<Range> Ranges, const char Separator = ',');
+  static std::string rangesToString(const ArrayRef<Range> Ranges,
+                                    const char Separator = ',');
 
   /// Print ranges to output stream
   static void printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges);
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 3e29d8955900d..7519e47b3f8b2 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -33,29 +33,30 @@ static OptDisable &getOptDisabler() {
   return OptDisabler;
 }
 
-static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
-                                   cl::init(-1), cl::Optional,
-                                   cl::cb<void, int>([](int Limit) {
-                                     if (Limit == -1) {
-                                       // -1 means run all passes, which is equivalent to no ranges
-                                       getOptBisector().clearRanges();
-                                     } else if (Limit > 0) {
-                                       // Convert limit to range 1-Limit
-                                       std::string RangeStr = "1-" + llvm::utostr(Limit);
-                                       if (!getOptBisector().parseRanges(RangeStr)) {
-                                         errs() << "Error: Invalid limit for -opt-bisect-limit: " 
-                                                << Limit << "\n";
-                                         exit(1);
-                                       }
-                                     }
-                                   }),
-                                   cl::desc("Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));
+static cl::opt<int> OptBisectLimit(
+    "opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional,
+    cl::cb<void, int>([](int Limit) {
+      if (Limit == -1) {
+        // -1 means run all passes, which is equivalent to no ranges
+        getOptBisector().clearRanges();
+      } else if (Limit > 0) {
+        // Convert limit to range 1-Limit
+        std::string RangeStr = "1-" + llvm::utostr(Limit);
+        if (!getOptBisector().parseRanges(RangeStr)) {
+          errs() << "Error: Invalid limit for -opt-bisect-limit: " << Limit
+                 << "\n";
+          exit(1);
+        }
+      }
+    }),
+    cl::desc(
+        "Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));
 
 static cl::opt<std::string> OptBisectRanges(
     "opt-bisect", cl::Hidden, cl::Optional,
     cl::cb<void, const std::string &>([](const std::string &RangeStr) {
       if (!getOptBisector().parseRanges(RangeStr)) {
-        errs() << "Error: Invalid range specification for -opt-bisect: " 
+        errs() << "Error: Invalid range specification for -opt-bisect: "
                << RangeStr << "\n";
         exit(1);
       }
@@ -96,10 +97,10 @@ bool OptBisect::shouldRunPass(StringRef PassName,
   assert(isEnabled());
 
   int CurBisectNum = ++LastBisectNum;
-  
+
   // Check if current pass number falls within any of the specified ranges
   bool ShouldRun = RangeUtils::contains(BisectRanges, CurBisectNum);
-  
+
   if (OptBisectVerbose)
     printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
   return ShouldRun;
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index d3b7b376adf7e..ddffbe5d9b6af 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -13,36 +13,37 @@
 
 using namespace llvm;
 
-bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges, const char Separator) {
+bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges,
+                             const char Separator) {
   Ranges.clear();
-  
+
   if (Str.empty())
     return true;
-  
+
   // Split by the specified separator
   SmallVector<StringRef, 8> Parts;
   Str.split(Parts, Separator, -1, false);
-  
+
   // Regex to match either single number or range "num1-num2"
   const Regex RangeRegex("^([0-9]+)(-([0-9]+))?$");
-  
+
   for (StringRef Part : Parts) {
     Part = Part.trim();
     if (Part.empty())
       continue;
-      
+
     SmallVector<StringRef, 4> Matches;
     if (!RangeRegex.match(Part, &Matches)) {
       errs() << "Invalid range format: '" << Part << "'\n";
       return false;
     }
-    
+
     int64_t Begin, End;
     if (Matches[1].getAsInteger(10, Begin)) {
       errs() << "Failed to parse number: '" << Matches[1] << "'\n";
       return false;
     }
-    
+
     if (!Matches[3].empty()) {
       // Range format "begin-end"
       if (Matches[3].getAsInteger(10, End)) {
@@ -57,17 +58,17 @@ bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges, const char
       // Single number
       End = Begin;
     }
-    
+
     // Check ordering constraint (ranges must be in increasing order)
     if (!Ranges.empty() && Begin <= Ranges.back().End) {
       errs() << "Expected ranges to be in increasing order: " << Begin
              << " <= " << Ranges.back().End << "\n";
       return false;
     }
-    
+
     Ranges.push_back(Range(Begin, End));
   }
-  
+
   return true;
 }
 
@@ -79,9 +80,8 @@ bool RangeUtils::contains(const ArrayRef<Range> Ranges, const int64_t Value) {
   return false;
 }
 
-
-
-std::string RangeUtils::rangesToString(const ArrayRef<Range> Ranges, const char Separator) {
+std::string RangeUtils::rangesToString(const ArrayRef<Range> Ranges,
+                                       const char Separator) {
   std::ostringstream OS;
   for (size_t I = 0; I < Ranges.size(); ++I) {
     if (I > 0)
@@ -106,7 +106,7 @@ void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges) {
         OS << ':';
       else
         IsFirst = false;
-      
+
       if (R.Begin == R.End)
         OS << R.Begin;
       else
@@ -115,17 +115,18 @@ void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges) {
   }
 }
 
-RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(const ArrayRef<Range> Ranges) {
+RangeUtils::RangeList
+RangeUtils::mergeAdjacentRanges(const ArrayRef<Range> Ranges) {
   if (Ranges.empty())
     return {};
-    
+
   RangeList Result;
   Result.push_back(Ranges[0]);
-  
+
   for (size_t I = 1; I < Ranges.size(); ++I) {
     const Range &Current = Ranges[I];
     Range &Last = Result.back();
-    
+
     // Check if current range is adjacent to the last merged range
     if (Current.Begin == Last.End + 1) {
       // Merge by extending the end of the last range
@@ -135,6 +136,6 @@ RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(const ArrayRef<Range> Rang
       Result.push_back(Current);
     }
   }
-  
+
   return Result;
 }
diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
index 29cc32e05f7ee..c09e92db60bed 100644
--- a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
+++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
@@ -127,6 +127,7 @@ int main(int argc, char **argv) {
   }
 
   errs() << "Minimal Chunks = ";
-  RangeUtils::printRanges(llvm::errs(), RangeUtils::mergeAdjacentRanges(CurrChunks));
+  RangeUtils::printRanges(llvm::errs(),
+                          RangeUtils::mergeAdjacentRanges(CurrChunks));
   errs() << "\n";
 }
diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index cddbf82c81c63..94fc281386cdd 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -36,7 +36,7 @@ TEST(RangeTest, RangeOverlaps) {
   Range R2(3, 8);
   Range R3(6, 10);
   Range R4(11, 15);
-  
+
   EXPECT_TRUE(R1.overlaps(R2));
   EXPECT_TRUE(R2.overlaps(R1));
   EXPECT_TRUE(R2.overlaps(R3));
@@ -65,7 +65,7 @@ TEST(RangeUtilsTest, ParseMultipleRanges) {
   RangeUtils::RangeList Ranges;
   EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
   EXPECT_EQ(Ranges.size(), 3U);
-  
+
   // Ranges are in input order (DebugCounter style)
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);
@@ -95,22 +95,23 @@ TEST(RangeUtilsTest, ParseEmptyString) {
 
 TEST(RangeUtilsTest, ParseInvalidRanges) {
   RangeUtils::RangeList Ranges;
-  
+
   // Invalid number
   EXPECT_FALSE(RangeUtils::parseRanges("abc", Ranges));
-  
+
   // Invalid range (begin > end)
   EXPECT_FALSE(RangeUtils::parseRanges("10-5", Ranges));
-  
+
   // Out of order ranges (DebugCounter constraint)
   EXPECT_FALSE(RangeUtils::parseRanges("10,5", Ranges));
-  EXPECT_FALSE(RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping not allowed
+  EXPECT_FALSE(
+      RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping not allowed
 }
 
 TEST(RangeUtilsTest, Contains) {
   RangeUtils::RangeList Ranges;
   EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
-  
+
   EXPECT_TRUE(RangeUtils::contains(Ranges, 1));
   EXPECT_TRUE(RangeUtils::contains(Ranges, 3));
   EXPECT_TRUE(RangeUtils::contains(Ranges, 5));
@@ -118,7 +119,7 @@ TEST(RangeUtilsTest, Contains) {
   EXPECT_TRUE(RangeUtils::contains(Ranges, 15));
   EXPECT_TRUE(RangeUtils::contains(Ranges, 18));
   EXPECT_TRUE(RangeUtils::contains(Ranges, 20));
-  
+
   EXPECT_FALSE(RangeUtils::contains(Ranges, 6));
   EXPECT_FALSE(RangeUtils::contains(Ranges, 9));
   EXPECT_FALSE(RangeUtils::contains(Ranges, 11));
@@ -129,24 +130,24 @@ TEST(RangeUtilsTest, Contains) {
 TEST(RangeUtilsTest, RangesToString) {
   RangeUtils::RangeList Ranges;
   EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
-  
+
   std::string Result = RangeUtils::rangesToString(Ranges);
   EXPECT_EQ(Result, "1-5,10,15-20");
 }
 
 TEST(RangeUtilsTest, SeparatorParameter) {
   RangeUtils::RangeList ColonRanges, CommaRanges;
-  
+
   // Test explicit separator parameters
   EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", ColonRanges, ':'));
   EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", CommaRanges, ','));
-  
+
   EXPECT_EQ(ColonRanges.size(), CommaRanges.size());
   for (size_t I = 0; I < ColonRanges.size(); ++I) {
     EXPECT_EQ(ColonRanges[I].Begin, CommaRanges[I].Begin);
     EXPECT_EQ(ColonRanges[I].End, CommaRanges[I].End);
   }
-  
+
   // Test that both work with contains()
   EXPECT_TRUE(RangeUtils::contains(ColonRanges, 3));
   EXPECT_TRUE(RangeUtils::contains(CommaRanges, 3));
@@ -154,14 +155,14 @@ TEST(RangeUtilsTest, SeparatorParameter) {
   EXPECT_TRUE(RangeUtils::contains(CommaRanges, 10));
   EXPECT_TRUE(RangeUtils::contains(ColonRanges, 18));
   EXPECT_TRUE(RangeUtils::contains(CommaRanges, 18));
-  
+
   EXPECT_FALSE(RangeUtils::contains(ColonRanges, 8));
   EXPECT_FALSE(RangeUtils::contains(CommaRanges, 8));
 }
 
 TEST(RangeUtilsTest, DefaultCommaSeparator) {
   RangeUtils::RangeList Ranges;
-  
+
   // Test that comma is the default separator
   EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
   EXPECT_EQ(Ranges.size(), 3U);
@@ -175,17 +176,17 @@ TEST(RangeUtilsTest, DefaultCommaSeparator) {
 
 TEST(RangeTest, MergeAdjacentRanges) {
   RangeUtils::RangeList Input, Expected, Result;
-  
+
   // Empty input
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_TRUE(Result.empty());
-  
+
   // Single range - no change
   Input.push_back(Range(5, 10));
   Expected.push_back(Range(5, 10));
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
-  
+
   // Adjacent ranges should merge
   Input.clear();
   Expected.clear();
@@ -195,32 +196,32 @@ TEST(RangeTest, MergeAdjacentRanges) {
   Expected.push_back(Range(1, 9));
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
-  
+
   // Non-adjacent ranges should not merge
   Input.clear();
   Expected.clear();
   Input.push_back(Range(1, 3));
-  Input.push_back(Range(5, 7)); // Gap between 3 and 5
+  Input.push_back(Range(5, 7));   // Gap between 3 and 5
   Input.push_back(Range(10, 12)); // Gap between 7 and 10
   Expected.push_back(Range(1, 3));
   Expected.push_back(Range(5, 7));
   Expected.push_back(Range(10, 12));
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
-  
+
   // Mixed adjacent and non-adjacent
   Input.clear();
   Expected.clear();
   Input.push_back(Range(1, 3));
-  Input.push_back(Range(4, 6)); // Adjacent to first
-  Input.push_back(Range(8, 10)); // Gap
-  Input.push_back(Range(11, 13)); // Adjacent to third
-  Input.push_back(Range(14, 16)); // Adjacent to fourth
-  Expected.push_back(Range(1, 6));   // Merged 1-3 and 4-6
-  Expected.push_back(Range(8, 16));  // Merged 8-10, 11-13, 14-16
+  Input.push_back(Range(4, 6));     // Adjacent to first
+  Input.push_back(Range(8, 10));    // Gap
+  Input.push_back(Range(11, 13));   // Adjacent to third
+  Input.push_back(Range(14, 16));   // Adjacent to fourth
+  Expected.push_back(Range(1, 6));  // Merged 1-3 and 4-6
+  Expected.push_back(Range(8, 16)); // Merged 8-10, 11-13, 14-16
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
-  
+
   // Single numbers that are adjacent
   Input.clear();
   Expected.clear();

>From 4cf5a990459ca913d347a5570fc540f341d1712a Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 22:32:30 +0000
Subject: [PATCH 13/31] use Expect for Range

---
 llvm/include/llvm/IR/OptBisect.h              |  8 +-
 llvm/include/llvm/Support/Range.h             | 10 +-
 llvm/lib/IR/OptBisect.cpp                     | 23 +++--
 llvm/lib/Support/DebugCounter.cpp             | 12 ++-
 llvm/lib/Support/Range.cpp                    | 45 ++++++---
 llvm/test/Other/debugcounter-multi-ranges.ll  | 92 +++++++++++++++----
 .../reduce-chunk-list/reduce-chunk-list.cpp   |  9 +-
 7 files changed, 145 insertions(+), 54 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index fe2f808077ab4..9e3400325defd 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -71,10 +71,10 @@ class LLVM_ABI OptBisect : public OptPassGate {
   /// isEnabled() should return true before calling shouldRunPass().
   bool isEnabled() const override { return !BisectRanges.empty(); }
 
-  /// Parse range specification and set the ranges for bisection.
-  /// Range format: "1-10,20-30,45" (runs passes 1-10, 20-30, and 45)
-  /// Returns false on parsing error.
-  bool parseRanges(StringRef RangeStr);
+  /// Set ranges directly from a RangeList
+  void setRanges(RangeUtils::RangeList Ranges) {
+    BisectRanges = std::move(Ranges);
+  }
 
   /// Clear all ranges, effectively disabling bisection.
   void clearRanges() {
diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 14f90c741edb0..9e3e68e7f4596 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -17,6 +17,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include <cstdint>
 
 namespace llvm {
@@ -58,10 +59,15 @@ class RangeUtils {
 
   /// Parse a range specification string like "1-10,20-30,45" or
   /// "1-10:20-30:45". Ranges must be in increasing order and non-overlapping.
-  /// Returns false on error, true on success
   /// \param RangeStr The string to parse
-  /// \param Ranges Output list of parsed ranges
   /// \param Separator The separator character to use (',' or ':')
+  /// \returns Expected<RangeList> containing the parsed ranges on success,
+  ///          or an Error on failure
+  static Expected<RangeList> parseRanges(const StringRef RangeStr,
+                                         const char Separator = ',');
+
+  /// Legacy interface for backward compatibility. 
+  /// \deprecated Use the Expected<RangeList> version instead
   static bool parseRanges(const StringRef RangeStr, RangeList &Ranges,
                           const char Separator = ',');
 
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 7519e47b3f8b2..b2bce5a9cf524 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -42,11 +42,15 @@ static cl::opt<int> OptBisectLimit(
       } else if (Limit > 0) {
         // Convert limit to range 1-Limit
         std::string RangeStr = "1-" + llvm::utostr(Limit);
-        if (!getOptBisector().parseRanges(RangeStr)) {
-          errs() << "Error: Invalid limit for -opt-bisect-limit: " << Limit
-                 << "\n";
+        auto Ranges = RangeUtils::parseRanges(RangeStr);
+        if (!Ranges) {
+          handleAllErrors(Ranges.takeError(), [&](const StringError &E) {
+            errs() << "Error: Invalid limit for -opt-bisect-limit: " << Limit
+                   << " (" << E.getMessage() << ")\n";
+          });
           exit(1);
         }
+        getOptBisector().setRanges(std::move(*Ranges));
       }
     }),
     cl::desc(
@@ -55,11 +59,15 @@ static cl::opt<int> OptBisectLimit(
 static cl::opt<std::string> OptBisectRanges(
     "opt-bisect", cl::Hidden, cl::Optional,
     cl::cb<void, const std::string &>([](const std::string &RangeStr) {
-      if (!getOptBisector().parseRanges(RangeStr)) {
-        errs() << "Error: Invalid range specification for -opt-bisect: "
-               << RangeStr << "\n";
+      auto Ranges = RangeUtils::parseRanges(RangeStr);
+      if (!Ranges) {
+        handleAllErrors(Ranges.takeError(), [&](const StringError &E) {
+          errs() << "Error: Invalid range specification for -opt-bisect: "
+                 << RangeStr << " (" << E.getMessage() << ")\n";
+        });
         exit(1);
       }
+      getOptBisector().setRanges(std::move(*Ranges));
     }),
     cl::desc("Run optimization passes only for the specified ranges. "
              "Format: '1-10,20-30,45' (runs passes 1-10, 20-30, and 45)"));
@@ -88,9 +96,6 @@ static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
          << " on " << TargetDesc << '\n';
 }
 
-bool OptBisect::parseRanges(StringRef RangeStr) {
-  return RangeUtils::parseRanges(RangeStr, BisectRanges);
-}
 
 bool OptBisect::shouldRunPass(StringRef PassName,
                               StringRef IRDescription) const {
diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index a704119a38426..4ac292a6c15fb 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -117,6 +117,15 @@ void DebugCounter::push_back(const std::string &Val) {
   }
   StringRef CounterName = CounterPair.first;
 
+  auto ExpectedChunks = RangeUtils::parseRanges(CounterPair.second, ':');
+  if (!ExpectedChunks) {
+    handleAllErrors(ExpectedChunks.takeError(), [&](const StringError &E) {
+      errs() << "DebugCounter Error: " << E.getMessage() << "\n";
+    });
+    return;
+  }
+  RangeUtils::RangeList Chunks = std::move(*ExpectedChunks);
+
   unsigned CounterID = getCounterId(std::string(CounterName));
   if (!CounterID) {
     errs() << "DebugCounter Error: " << CounterName
@@ -127,8 +136,7 @@ void DebugCounter::push_back(const std::string &Val) {
 
   CounterInfo &Counter = Counters[CounterID];
   Counter.IsSet = true;
-  if (!RangeUtils::parseRanges(CounterPair.second, Counter.Chunks, ':'))
-    return;
+  Counter.Chunks = std::move(Chunks);
 }
 
 void DebugCounter::print(raw_ostream &OS) const {
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index ddffbe5d9b6af..aa69fe35213bc 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -7,18 +7,19 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/Range.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/raw_ostream.h"
 #include <sstream>
 
 using namespace llvm;
 
-bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges,
-                             const char Separator) {
-  Ranges.clear();
+Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
+                                                       const char Separator) {
+  RangeList Ranges;
 
   if (Str.empty())
-    return true;
+    return std::move(Ranges);
 
   // Split by the specified separator
   SmallVector<StringRef, 8> Parts;
@@ -34,25 +35,25 @@ bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges,
 
     SmallVector<StringRef, 4> Matches;
     if (!RangeRegex.match(Part, &Matches)) {
-      errs() << "Invalid range format: '" << Part << "'\n";
-      return false;
+      return createStringError(std::errc::invalid_argument,
+                              "Invalid range format: '%s'", Part.str().c_str());
     }
 
     int64_t Begin, End;
     if (Matches[1].getAsInteger(10, Begin)) {
-      errs() << "Failed to parse number: '" << Matches[1] << "'\n";
-      return false;
+      return createStringError(std::errc::invalid_argument,
+                              "Failed to parse number: '%s'", Matches[1].str().c_str());
     }
 
     if (!Matches[3].empty()) {
       // Range format "begin-end"
       if (Matches[3].getAsInteger(10, End)) {
-        errs() << "Failed to parse number: '" << Matches[3] << "'\n";
-        return false;
+        return createStringError(std::errc::invalid_argument,
+                                "Failed to parse number: '%s'", Matches[3].str().c_str());
       }
       if (Begin >= End) {
-        errs() << "Invalid range: " << Begin << " >= " << End << "\n";
-        return false;
+        return createStringError(std::errc::invalid_argument,
+                                "Invalid range: %lld >= %lld", Begin, End);
       }
     } else {
       // Single number
@@ -61,14 +62,28 @@ bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges,
 
     // Check ordering constraint (ranges must be in increasing order)
     if (!Ranges.empty() && Begin <= Ranges.back().End) {
-      errs() << "Expected ranges to be in increasing order: " << Begin
-             << " <= " << Ranges.back().End << "\n";
-      return false;
+      return createStringError(std::errc::invalid_argument,
+                              "Expected ranges to be in increasing order: %lld <= %lld",
+                              Begin, Ranges.back().End);
     }
 
     Ranges.push_back(Range(Begin, End));
   }
 
+  return std::move(Ranges);
+}
+
+bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges,
+                             const char Separator) {
+  auto ExpectedRanges = parseRanges(Str, Separator);
+  if (!ExpectedRanges) {
+    // For backward compatibility, print error to stderr
+    handleAllErrors(ExpectedRanges.takeError(), [](const StringError &E) {
+      errs() << E.getMessage() << "\n";
+    });
+    return false;
+  }
+  Ranges = std::move(*ExpectedRanges);
   return true;
 }
 
diff --git a/llvm/test/Other/debugcounter-multi-ranges.ll b/llvm/test/Other/debugcounter-multi-ranges.ll
index 30cdbc6e40c0b..aa4f0aa247329 100644
--- a/llvm/test/Other/debugcounter-multi-ranges.ll
+++ b/llvm/test/Other/debugcounter-multi-ranges.ll
@@ -1,12 +1,20 @@
 ; REQUIRES: asserts
-; Test debug counter with multiple ranges using colon separators
-; (DebugCounter uses colon separators to avoid conflicts with cl::CommaSeparated)
+; Test debug counter with multiple ranges
 
-; RUN: opt -passes=dce -S -debug-counter=dce-transform=1:3:5 < %s | FileCheck %s --check-prefix=CHECK-COLON
-; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-2:4:6-7 < %s | FileCheck %s --check-prefix=CHECK-MIXED-COLON
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=1:3:5 < %s | FileCheck %s --check-prefix=CHECK-SINGLE
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-2:4:6-7 < %s | FileCheck %s --check-prefix=CHECK-MIXED
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-7 < %s | FileCheck %s --check-prefix=CHECK-ALL
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=100 < %s | FileCheck %s --check-prefix=CHECK-NONE
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=7 < %s | FileCheck %s --check-prefix=CHECK-LAST
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=1 < %s | FileCheck %s --check-prefix=CHECK-FIRST
+
+; Test error cases - these should produce error messages but not crash
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=invalid 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-INVALID
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=5-2 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-BACKWARDS
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=1:3:2 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-UNORDERED
 
 ; Test that with debug counters on, we can selectively apply transformations
-; using different range syntaxes. All variants should produce the same result.
+; using different range specifications and edge cases.
 
 ; Original function has 8 dead instructions that DCE can eliminate
 define void @test() {
@@ -21,18 +29,62 @@ define void @test() {
   ret void
 }
 
-; Test colon separator: apply transformations 1, 3, 5 (eliminate dead2, dead4, dead6)
-; CHECK-COLON-LABEL: @test
-; CHECK-COLON-NEXT: %dead1 = add i32 1, 2
-; CHECK-COLON-NEXT: %dead3 = add i32 5, 6
-; CHECK-COLON-NEXT: %dead5 = add i32 9, 10
-; CHECK-COLON-NEXT: %dead7 = add i32 13, 14
-; CHECK-COLON-NEXT: %dead8 = add i32 15, 16
-; CHECK-COLON-NEXT: ret void
-
-; Test mixed ranges with colon: apply transformations 1-2, 4, 6-7 (eliminate dead2, dead3, dead5, dead7, dead8)
-; CHECK-MIXED-COLON-LABEL: @test
-; CHECK-MIXED-COLON-NEXT: %dead1 = add i32 1, 2
-; CHECK-MIXED-COLON-NEXT: %dead4 = add i32 7, 8
-; CHECK-MIXED-COLON-NEXT: %dead6 = add i32 11, 12
-; CHECK-MIXED-COLON-NEXT: ret void
+; Test single values: apply transformations 1, 3, 5 (eliminate dead2, dead4, dead6)
+; CHECK-SINGLE-LABEL: @test
+; CHECK-SINGLE-NEXT: %dead1 = add i32 1, 2
+; CHECK-SINGLE-NEXT: %dead3 = add i32 5, 6
+; CHECK-SINGLE-NEXT: %dead5 = add i32 9, 10
+; CHECK-SINGLE-NEXT: %dead7 = add i32 13, 14
+; CHECK-SINGLE-NEXT: %dead8 = add i32 15, 16
+; CHECK-SINGLE-NEXT: ret void
+
+; Test mixed ranges: apply transformations 1-2, 4, 6-7 (eliminate dead2, dead3, dead5, dead7, dead8)
+; CHECK-MIXED-LABEL: @test
+; CHECK-MIXED-NEXT: %dead1 = add i32 1, 2
+; CHECK-MIXED-NEXT: %dead4 = add i32 7, 8
+; CHECK-MIXED-NEXT: %dead6 = add i32 11, 12
+; CHECK-MIXED-NEXT: ret void
+
+; Test all range: apply transformations 1-7 (eliminate all dead instructions except dead1)
+; CHECK-ALL-LABEL: @test
+; CHECK-ALL-NEXT: %dead1 = add i32 1, 2
+; CHECK-ALL-NEXT: ret void
+
+; Test out of range: apply transformation 100 (eliminate nothing, counter too high)
+; CHECK-NONE-LABEL: @test
+; CHECK-NONE-NEXT: %dead1 = add i32 1, 2
+; CHECK-NONE-NEXT: %dead2 = add i32 3, 4
+; CHECK-NONE-NEXT: %dead3 = add i32 5, 6
+; CHECK-NONE-NEXT: %dead4 = add i32 7, 8
+; CHECK-NONE-NEXT: %dead5 = add i32 9, 10
+; CHECK-NONE-NEXT: %dead6 = add i32 11, 12
+; CHECK-NONE-NEXT: %dead7 = add i32 13, 14
+; CHECK-NONE-NEXT: %dead8 = add i32 15, 16
+; CHECK-NONE-NEXT: ret void
+
+; Test last transformation: apply transformation 7 (eliminate dead8)
+; CHECK-LAST-LABEL: @test
+; CHECK-LAST-NEXT: %dead1 = add i32 1, 2
+; CHECK-LAST-NEXT: %dead2 = add i32 3, 4
+; CHECK-LAST-NEXT: %dead3 = add i32 5, 6
+; CHECK-LAST-NEXT: %dead4 = add i32 7, 8
+; CHECK-LAST-NEXT: %dead5 = add i32 9, 10
+; CHECK-LAST-NEXT: %dead6 = add i32 11, 12
+; CHECK-LAST-NEXT: %dead7 = add i32 13, 14
+; CHECK-LAST-NEXT: ret void
+
+; Test first transformation: apply transformation 1 (eliminate dead2)
+; CHECK-FIRST-LABEL: @test
+; CHECK-FIRST-NEXT: %dead1 = add i32 1, 2
+; CHECK-FIRST-NEXT: %dead3 = add i32 5, 6
+; CHECK-FIRST-NEXT: %dead4 = add i32 7, 8
+; CHECK-FIRST-NEXT: %dead5 = add i32 9, 10
+; CHECK-FIRST-NEXT: %dead6 = add i32 11, 12
+; CHECK-FIRST-NEXT: %dead7 = add i32 13, 14
+; CHECK-FIRST-NEXT: %dead8 = add i32 15, 16
+; CHECK-FIRST-NEXT: ret void
+
+; Error case checks
+; CHECK-ERROR-INVALID: Invalid range format: 'invalid'
+; CHECK-ERROR-BACKWARDS: Invalid range: 5 >= 2
+; CHECK-ERROR-UNORDERED: Expected ranges to be in increasing order: 2 <= 3
diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
index c09e92db60bed..cabaf0f865055 100644
--- a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
+++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
@@ -84,9 +84,14 @@ bool increaseGranularity(RangeUtils::RangeList &Chunks) {
 int main(int argc, char **argv) {
   cl::ParseCommandLineOptions(argc, argv);
 
-  RangeUtils::RangeList CurrChunks;
-  if (!RangeUtils::parseRanges(StartChunks, CurrChunks, ','))
+  auto ExpectedChunks = RangeUtils::parseRanges(StartChunks, ',');
+  if (!ExpectedChunks) {
+    handleAllErrors(ExpectedChunks.takeError(), [](const StringError &E) {
+      errs() << "Error parsing chunks: " << E.getMessage() << "\n";
+    });
     return 1;
+  }
+  RangeUtils::RangeList CurrChunks = std::move(*ExpectedChunks);
 
   auto Program = sys::findProgramByName(ReproductionCmd);
   if (!Program) {

>From 0eefd68066f8e071c58992f85d21a60c990c4b9f Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 22:32:37 +0000
Subject: [PATCH 14/31] format

---
 llvm/include/llvm/Support/Range.h |  2 +-
 llvm/lib/IR/OptBisect.cpp         |  1 -
 llvm/lib/Support/Range.cpp        | 20 ++++++++++++--------
 3 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 9e3e68e7f4596..b22ff312513e3 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -66,7 +66,7 @@ class RangeUtils {
   static Expected<RangeList> parseRanges(const StringRef RangeStr,
                                          const char Separator = ',');
 
-  /// Legacy interface for backward compatibility. 
+  /// Legacy interface for backward compatibility.
   /// \deprecated Use the Expected<RangeList> version instead
   static bool parseRanges(const StringRef RangeStr, RangeList &Ranges,
                           const char Separator = ',');
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index b2bce5a9cf524..6de86e7bae0b6 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -96,7 +96,6 @@ static void printPassMessage(StringRef Name, int PassNum, StringRef TargetDesc,
          << " on " << TargetDesc << '\n';
 }
 
-
 bool OptBisect::shouldRunPass(StringRef PassName,
                               StringRef IRDescription) const {
   assert(isEnabled());
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index aa69fe35213bc..2ea131fffb434 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -15,7 +15,7 @@
 using namespace llvm;
 
 Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
-                                                       const char Separator) {
+                                                        const char Separator) {
   RangeList Ranges;
 
   if (Str.empty())
@@ -36,24 +36,27 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
     SmallVector<StringRef, 4> Matches;
     if (!RangeRegex.match(Part, &Matches)) {
       return createStringError(std::errc::invalid_argument,
-                              "Invalid range format: '%s'", Part.str().c_str());
+                               "Invalid range format: '%s'",
+                               Part.str().c_str());
     }
 
     int64_t Begin, End;
     if (Matches[1].getAsInteger(10, Begin)) {
       return createStringError(std::errc::invalid_argument,
-                              "Failed to parse number: '%s'", Matches[1].str().c_str());
+                               "Failed to parse number: '%s'",
+                               Matches[1].str().c_str());
     }
 
     if (!Matches[3].empty()) {
       // Range format "begin-end"
       if (Matches[3].getAsInteger(10, End)) {
         return createStringError(std::errc::invalid_argument,
-                                "Failed to parse number: '%s'", Matches[3].str().c_str());
+                                 "Failed to parse number: '%s'",
+                                 Matches[3].str().c_str());
       }
       if (Begin >= End) {
         return createStringError(std::errc::invalid_argument,
-                                "Invalid range: %lld >= %lld", Begin, End);
+                                 "Invalid range: %lld >= %lld", Begin, End);
       }
     } else {
       // Single number
@@ -62,9 +65,10 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
 
     // Check ordering constraint (ranges must be in increasing order)
     if (!Ranges.empty() && Begin <= Ranges.back().End) {
-      return createStringError(std::errc::invalid_argument,
-                              "Expected ranges to be in increasing order: %lld <= %lld",
-                              Begin, Ranges.back().End);
+      return createStringError(
+          std::errc::invalid_argument,
+          "Expected ranges to be in increasing order: %lld <= %lld", Begin,
+          Ranges.back().End);
     }
 
     Ranges.push_back(Range(Begin, End));

>From a1595382b68df774197075c69fe52e25c9975fc0 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 22:35:47 +0000
Subject: [PATCH 15/31] fix hang

---
 llvm/lib/Support/DebugCounter.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index 4ac292a6c15fb..3b35a84a94253 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -122,7 +122,7 @@ void DebugCounter::push_back(const std::string &Val) {
     handleAllErrors(ExpectedChunks.takeError(), [&](const StringError &E) {
       errs() << "DebugCounter Error: " << E.getMessage() << "\n";
     });
-    return;
+    exit(1);
   }
   RangeUtils::RangeList Chunks = std::move(*ExpectedChunks);
 

>From 3215304e6ba90c30cc9ae99e47aafd34688424de Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Wed, 3 Sep 2025 22:57:01 +0000
Subject: [PATCH 16/31] fix error handling tests

---
 llvm/lib/Support/DebugCounter.cpp            |  4 +--
 llvm/test/Other/debugcounter-multi-ranges.ll | 38 ++++++++++++++++----
 2 files changed, 34 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index 3b35a84a94253..7e65a3efa18ce 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -113,7 +113,7 @@ void DebugCounter::push_back(const std::string &Val) {
   auto CounterPair = StringRef(Val).split('=');
   if (CounterPair.second.empty()) {
     errs() << "DebugCounter Error: " << Val << " does not have an = in it\n";
-    return;
+    exit(1);
   }
   StringRef CounterName = CounterPair.first;
 
@@ -130,7 +130,7 @@ void DebugCounter::push_back(const std::string &Val) {
   if (!CounterID) {
     errs() << "DebugCounter Error: " << CounterName
            << " is not a registered counter\n";
-    return;
+    exit(1);
   }
   enableAllCounters();
 
diff --git a/llvm/test/Other/debugcounter-multi-ranges.ll b/llvm/test/Other/debugcounter-multi-ranges.ll
index aa4f0aa247329..7d9b1949a5499 100644
--- a/llvm/test/Other/debugcounter-multi-ranges.ll
+++ b/llvm/test/Other/debugcounter-multi-ranges.ll
@@ -1,6 +1,7 @@
 ; REQUIRES: asserts
-; Test debug counter with multiple ranges
 
+; Test debug counter with multiple ranges
+; RUN: opt -passes=dce -S -debug-counter=dce-transform=0 < %s | FileCheck %s --check-prefix=CHECK-ZERO
 ; RUN: opt -passes=dce -S -debug-counter=dce-transform=1:3:5 < %s | FileCheck %s --check-prefix=CHECK-SINGLE
 ; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-2:4:6-7 < %s | FileCheck %s --check-prefix=CHECK-MIXED
 ; RUN: opt -passes=dce -S -debug-counter=dce-transform=1-7 < %s | FileCheck %s --check-prefix=CHECK-ALL
@@ -12,9 +13,16 @@
 ; RUN: not opt -passes=dce -S -debug-counter=dce-transform=invalid 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-INVALID
 ; RUN: not opt -passes=dce -S -debug-counter=dce-transform=5-2 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-BACKWARDS
 ; RUN: not opt -passes=dce -S -debug-counter=dce-transform=1:3:2 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-UNORDERED
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=abc-def 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-NON-NUMERIC
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=1-abc 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-MIXED
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=1:2:3:2:4 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-COMPLEX-UNORDERED
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=1--5 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-DOUBLE-DASH
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=-5 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-NEGATIVE
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform= 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-EMPTY
+; RUN: not opt -passes=dce -S -debug-counter=dce-transform=1:1:1 2>&1 | FileCheck %s --check-prefix=CHECK-ERROR-DUPLICATE
 
 ; Test that with debug counters on, we can selectively apply transformations
-; using different range specifications and edge cases.
+; using different range specifications. Also check that we catch errors during parsing.
 
 ; Original function has 8 dead instructions that DCE can eliminate
 define void @test() {
@@ -29,6 +37,17 @@ define void @test() {
   ret void
 }
 
+; Test zero: eliminate transformation 0
+; CHECK-ZERO-LABEL: @test  
+; CHECK-ZERO-NEXT: %dead2 = add i32 3, 4
+; CHECK-ZERO-NEXT: %dead3 = add i32 5, 6
+; CHECK-ZERO-NEXT: %dead4 = add i32 7, 8
+; CHECK-ZERO-NEXT: %dead5 = add i32 9, 10
+; CHECK-ZERO-NEXT: %dead6 = add i32 11, 12
+; CHECK-ZERO-NEXT: %dead7 = add i32 13, 14
+; CHECK-ZERO-NEXT: %dead8 = add i32 15, 16
+; CHECK-ZERO-NEXT: ret void
+
 ; Test single values: apply transformations 1, 3, 5 (eliminate dead2, dead4, dead6)
 ; CHECK-SINGLE-LABEL: @test
 ; CHECK-SINGLE-NEXT: %dead1 = add i32 1, 2
@@ -84,7 +103,14 @@ define void @test() {
 ; CHECK-FIRST-NEXT: %dead8 = add i32 15, 16
 ; CHECK-FIRST-NEXT: ret void
 
-; Error case checks
-; CHECK-ERROR-INVALID: Invalid range format: 'invalid'
-; CHECK-ERROR-BACKWARDS: Invalid range: 5 >= 2
-; CHECK-ERROR-UNORDERED: Expected ranges to be in increasing order: 2 <= 3
+; Error case checks - test comprehensive error handling
+; CHECK-ERROR-INVALID: DebugCounter Error: Invalid range format: 'invalid'
+; CHECK-ERROR-BACKWARDS: DebugCounter Error: Invalid range: 5 >= 2
+; CHECK-ERROR-UNORDERED: DebugCounter Error: Expected ranges to be in increasing order: 2 <= 3
+; CHECK-ERROR-NON-NUMERIC: DebugCounter Error: Invalid range format: 'abc-def'
+; CHECK-ERROR-MIXED: DebugCounter Error: Invalid range format: '1-abc'
+; CHECK-ERROR-COMPLEX-UNORDERED: DebugCounter Error: Expected ranges to be in increasing order: 2 <= 3
+; CHECK-ERROR-DOUBLE-DASH: DebugCounter Error: Invalid range format: '1--5'
+; CHECK-ERROR-NEGATIVE: DebugCounter Error: Invalid range format: '-5'
+; CHECK-ERROR-EMPTY: DebugCounter Error: dce-transform= does not have an = in it
+; CHECK-ERROR-DUPLICATE: DebugCounter Error: Expected ranges to be in increasing order: 1 <= 1

>From af7873f55de04dc8b8761b55e81d27fad8e04520 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 17:23:43 +0000
Subject: [PATCH 17/31] format

---
 llvm/lib/Support/Range.cpp | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index 2ea131fffb434..f2567970c6670 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -34,42 +34,36 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
       continue;
 
     SmallVector<StringRef, 4> Matches;
-    if (!RangeRegex.match(Part, &Matches)) {
+    if (!RangeRegex.match(Part, &Matches))
       return createStringError(std::errc::invalid_argument,
                                "Invalid range format: '%s'",
                                Part.str().c_str());
-    }
 
     int64_t Begin, End;
-    if (Matches[1].getAsInteger(10, Begin)) {
+    if (Matches[1].getAsInteger(10, Begin))
       return createStringError(std::errc::invalid_argument,
                                "Failed to parse number: '%s'",
                                Matches[1].str().c_str());
-    }
 
     if (!Matches[3].empty()) {
       // Range format "begin-end"
-      if (Matches[3].getAsInteger(10, End)) {
+      if (Matches[3].getAsInteger(10, End))
         return createStringError(std::errc::invalid_argument,
                                  "Failed to parse number: '%s'",
                                  Matches[3].str().c_str());
-      }
-      if (Begin >= End) {
+      if (Begin >= End)
         return createStringError(std::errc::invalid_argument,
                                  "Invalid range: %lld >= %lld", Begin, End);
-      }
-    } else {
+    } else
       // Single number
       End = Begin;
-    }
 
     // Check ordering constraint (ranges must be in increasing order)
-    if (!Ranges.empty() && Begin <= Ranges.back().End) {
+    if (!Ranges.empty() && Begin <= Ranges.back().End)
       return createStringError(
           std::errc::invalid_argument,
           "Expected ranges to be in increasing order: %lld <= %lld", Begin,
           Ranges.back().End);
-    }
 
     Ranges.push_back(Range(Begin, End));
   }
@@ -106,19 +100,18 @@ std::string RangeUtils::rangesToString(const ArrayRef<Range> Ranges,
     if (I > 0)
       OS << Separator;
     const Range &R = Ranges[I];
-    if (R.Begin == R.End) {
+    if (R.Begin == R.End)
       OS << R.Begin;
-    } else {
+    else
       OS << R.Begin << "-" << R.End;
-    }
   }
   return OS.str();
 }
 
 void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges) {
-  if (Ranges.empty()) {
+  if (Ranges.empty())
     OS << "empty";
-  } else {
+  else {
     bool IsFirst = true;
     for (const Range &R : Ranges) {
       if (!IsFirst)

>From c27ce560333596aa5e0ba262e7e6386027034fbf Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 17:31:34 +0000
Subject: [PATCH 18/31] cleanup

---
 llvm/include/llvm/Support/Range.h                  | 8 ++++++++
 llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp | 3 +--
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index b22ff312513e3..5fb73c5cd99a4 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -47,6 +47,14 @@ struct Range {
   /// Get the size of this range
   int64_t size() const { return End - Begin + 1; }
 
+  std::string toString() const {
+    return std::to_string(Begin) + "-" + std::to_string(End);
+  }
+
+  void print(raw_ostream &OS) const {
+    OS << Begin << "-" << End;
+  }
+
   bool operator==(const Range &Other) const {
     return Begin == Other.Begin && End == Other.End;
   }
diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
index cabaf0f865055..eb588dbe85aad 100644
--- a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
+++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
@@ -117,8 +117,7 @@ int main(int argc, char **argv) {
 
       Range Testing = CurrChunks[Idx];
       errs() << "Trying to remove : " << Testing.Begin;
-      if (Testing.Begin != Testing.End)
-        errs() << "-" << Testing.End;
+      Testing.print(errs());
       errs() << "\n";
 
       CurrChunks.erase(CurrChunks.begin() + Idx);

>From 6c4fea1e3584131058b388f62f1e85f535028cb3 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 17:31:48 +0000
Subject: [PATCH 19/31] clang format

---
 llvm/include/llvm/Support/Range.h | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 5fb73c5cd99a4..7f093d7efb631 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -51,9 +51,7 @@ struct Range {
     return std::to_string(Begin) + "-" + std::to_string(End);
   }
 
-  void print(raw_ostream &OS) const {
-    OS << Begin << "-" << End;
-  }
+  void print(raw_ostream &OS) const { OS << Begin << "-" << End; }
 
   bool operator==(const Range &Other) const {
     return Begin == Other.Begin && End == Other.End;

>From 5e1d4431a6bf46ab08e662a45d8a6b15c60eaf91 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 17:49:40 +0000
Subject: [PATCH 20/31] fix minor formatting bug

---
 llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
index eb588dbe85aad..50a8cb246d290 100644
--- a/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
+++ b/llvm/tools/reduce-chunk-list/reduce-chunk-list.cpp
@@ -116,7 +116,7 @@ int main(int argc, char **argv) {
         break;
 
       Range Testing = CurrChunks[Idx];
-      errs() << "Trying to remove : " << Testing.Begin;
+      errs() << "Trying to remove : ";
       Testing.print(errs());
       errs() << "\n";
 

>From 466991dcd876e4ebfda3eaf031ac714387f74642 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 18:56:02 +0000
Subject: [PATCH 21/31] small bug

---
 llvm/lib/IR/OptBisect.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 6de86e7bae0b6..1c349c2178b2d 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -41,7 +41,7 @@ static cl::opt<int> OptBisectLimit(
         getOptBisector().clearRanges();
       } else if (Limit > 0) {
         // Convert limit to range 1-Limit
-        std::string RangeStr = "1-" + llvm::utostr(Limit);
+        std::string RangeStr = Limit == 1? "1" : "1-" + llvm::utostr(Limit);
         auto Ranges = RangeUtils::parseRanges(RangeStr);
         if (!Ranges) {
           handleAllErrors(Ranges.takeError(), [&](const StringError &E) {

>From 0eaab13d5a4666afb507320fff509ad5226560da Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 18:56:12 +0000
Subject: [PATCH 22/31] format

---
 llvm/lib/IR/OptBisect.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 1c349c2178b2d..33f345cd2c3b8 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -41,7 +41,7 @@ static cl::opt<int> OptBisectLimit(
         getOptBisector().clearRanges();
       } else if (Limit > 0) {
         // Convert limit to range 1-Limit
-        std::string RangeStr = Limit == 1? "1" : "1-" + llvm::utostr(Limit);
+        std::string RangeStr = Limit == 1 ? "1" : "1-" + llvm::utostr(Limit);
         auto Ranges = RangeUtils::parseRanges(RangeStr);
         if (!Ranges) {
           handleAllErrors(Ranges.takeError(), [&](const StringError &E) {

>From 5ce4228cc68d8c2b08c449e9607dafc8501d01cb Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 19:56:43 +0000
Subject: [PATCH 23/31] allow -opt-bisect=0 and -opt-bisect=-1

---
 llvm/lib/IR/OptBisect.cpp            | 15 ++++++++++++---
 llvm/test/Other/opt-bisect-ranges.ll | 18 ++++++++++++++++++
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 33f345cd2c3b8..4bf920a40879c 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -37,8 +37,11 @@ static cl::opt<int> OptBisectLimit(
     "opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional,
     cl::cb<void, int>([](int Limit) {
       if (Limit == -1) {
-        // -1 means run all passes, which is equivalent to no ranges
-        getOptBisector().clearRanges();
+        // -1 means run all passes
+        getOptBisector().setRanges({{1, std::numeric_limits<int>::max()}});
+      } else if (Limit == 0) {
+        // 0 means run no passes
+        getOptBisector().setRanges({{0, 0}});
       } else if (Limit > 0) {
         // Convert limit to range 1-Limit
         std::string RangeStr = Limit == 1 ? "1" : "1-" + llvm::utostr(Limit);
@@ -59,6 +62,12 @@ static cl::opt<int> OptBisectLimit(
 static cl::opt<std::string> OptBisectRanges(
     "opt-bisect", cl::Hidden, cl::Optional,
     cl::cb<void, const std::string &>([](const std::string &RangeStr) {
+      if (RangeStr == "-1") {
+        // -1 means run all passes
+        getOptBisector().setRanges({{1, std::numeric_limits<int>::max()}});
+        return;
+      }
+
       auto Ranges = RangeUtils::parseRanges(RangeStr);
       if (!Ranges) {
         handleAllErrors(Ranges.takeError(), [&](const StringError &E) {
@@ -70,7 +79,7 @@ static cl::opt<std::string> OptBisectRanges(
       getOptBisector().setRanges(std::move(*Ranges));
     }),
     cl::desc("Run optimization passes only for the specified ranges. "
-             "Format: '1-10,20-30,45' (runs passes 1-10, 20-30, and 45)"));
+             "Format: '1-10,20-30,45' (runs passes 1-10, 20-30, and 45). Pass '0' to run no passes and -1 to run all passes."));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",
diff --git a/llvm/test/Other/opt-bisect-ranges.ll b/llvm/test/Other/opt-bisect-ranges.ll
index 145cf169ca370..5f48aa963290c 100644
--- a/llvm/test/Other/opt-bisect-ranges.ll
+++ b/llvm/test/Other/opt-bisect-ranges.ll
@@ -20,6 +20,24 @@
 ; CHECK-SINGLE: BISECT: running pass (5) simplifycfg on foo
 ; CHECK-SINGLE: BISECT: NOT running pass (6) sroa on foo
 
+; Test running no passes
+; RUN: opt -O1 -opt-bisect=0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
+; CHECK-NONE: BISECT: NOT running pass (1) annotation2metadata on [module]
+; CHECK-NONE: BISECT: NOT running pass (2) forceattrs on [module]
+; CHECK-NONE: BISECT: NOT running pass (3) inferattrs on [module]
+; CHECK-NONE: BISECT: NOT running pass (4) lower-expect on foo
+; CHECK-NONE: BISECT: NOT running pass (5) simplifycfg on foo
+; CHECK-NONE: BISECT: NOT running pass (6) sroa on foo
+
+; Test running all passes
+; RUN: opt -O1 -opt-bisect=-1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-ALL
+; CHECK-ALL: BISECT: running pass (1) annotation2metadata on [module]
+; CHECK-ALL: BISECT: running pass (2) forceattrs on [module]
+; CHECK-ALL: BISECT: running pass (3) inferattrs on [module]
+; CHECK-ALL: BISECT: running pass (4) lower-expect on foo
+; CHECK-ALL: BISECT: running pass (5) simplifycfg on foo
+; CHECK-ALL: BISECT: running pass (6) sroa on foo
+
 ; Test backward compatibility: -opt-bisect-limit=3 should be equivalent to -opt-bisect=1-3
 ; RUN: opt -O1 -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
 ; CHECK-LIMIT: BISECT: running pass (1) annotation2metadata on [module]

>From 095c35884e50af4ffc06e36869e8b6b1dc72271c Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 19:58:35 +0000
Subject: [PATCH 24/31] comment change

---
 llvm/lib/IR/OptBisect.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 4bf920a40879c..46092670fa07b 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -79,7 +79,7 @@ static cl::opt<std::string> OptBisectRanges(
       getOptBisector().setRanges(std::move(*Ranges));
     }),
     cl::desc("Run optimization passes only for the specified ranges. "
-             "Format: '1-10,20-30,45' (runs passes 1-10, 20-30, and 45). Pass '0' to run no passes and -1 to run all passes."));
+             "Format: '1-10,20-30,45' runs passes 1-10, 20-30, and 45, where index 1 is the first pass. Supply '0' to run no passes and -1 to run all passes."));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",

>From dce302aabcb1cfcfe34dc4e2540b98b8c6fb98c7 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Thu, 4 Sep 2025 19:58:49 +0000
Subject: [PATCH 25/31] format

---
 llvm/lib/IR/OptBisect.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 46092670fa07b..8add03d957f24 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -79,7 +79,9 @@ static cl::opt<std::string> OptBisectRanges(
       getOptBisector().setRanges(std::move(*Ranges));
     }),
     cl::desc("Run optimization passes only for the specified ranges. "
-             "Format: '1-10,20-30,45' runs passes 1-10, 20-30, and 45, where index 1 is the first pass. Supply '0' to run no passes and -1 to run all passes."));
+             "Format: '1-10,20-30,45' runs passes 1-10, 20-30, and 45, where "
+             "index 1 is the first pass. Supply '0' to run no passes and -1 to "
+             "run all passes."));
 
 static cl::opt<bool> OptBisectVerbose(
     "opt-bisect-verbose",

>From 2b779d3cb4a77ef85372497f8d44199a5b840279 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Fri, 5 Sep 2025 20:18:50 +0000
Subject: [PATCH 26/31] cleanup

---
 llvm/include/llvm/Support/Range.h    | 30 +++++++----------
 llvm/lib/IR/OptBisect.cpp            | 21 ++++--------
 llvm/lib/Support/Range.cpp           | 48 +++++-----------------------
 llvm/test/Other/opt-bisect-ranges.ll | 10 +++---
 llvm/unittests/Support/RangeTest.cpp |  8 -----
 5 files changed, 32 insertions(+), 85 deletions(-)

diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 7f093d7efb631..0714d26b963bc 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -18,6 +18,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
+#include <cassert>
 #include <cstdint>
 
 namespace llvm {
@@ -26,16 +27,18 @@ class raw_ostream;
 
 namespace llvm {
 
-/// Represents a range of integers [Begin, End] (inclusive on both ends)
+/// Represents a range of integers [Begin, End], inclusive on both ends, where Begin <= End.
 struct Range {
   int64_t Begin;
   int64_t End;
 
-  Range(const int64_t Begin, const int64_t End) : Begin(Begin), End(End) {}
-  Range(const int64_t Single) : Begin(Single), End(Single) {}
+  Range(int64_t Begin, int64_t End) : Begin(Begin), End(End) {
+    assert(Begin <= End && "Range Begin must be <= End");
+  }
+  Range(int64_t Single) : Begin(Single), End(Single) {}
 
   /// Check if the given value is within this range (inclusive)
-  bool contains(const int64_t Value) const {
+  bool contains(int64_t Value) const {
     return Value >= Begin && Value <= End;
   }
 
@@ -69,27 +72,18 @@ class RangeUtils {
   /// \param Separator The separator character to use (',' or ':')
   /// \returns Expected<RangeList> containing the parsed ranges on success,
   ///          or an Error on failure
-  static Expected<RangeList> parseRanges(const StringRef RangeStr,
-                                         const char Separator = ',');
-
-  /// Legacy interface for backward compatibility.
-  /// \deprecated Use the Expected<RangeList> version instead
-  static bool parseRanges(const StringRef RangeStr, RangeList &Ranges,
-                          const char Separator = ',');
+  static Expected<RangeList> parseRanges(StringRef RangeStr,
+                                         char Separator = ',');
 
   /// Check if a value is contained in any of the ranges
-  static bool contains(const ArrayRef<Range> Ranges, const int64_t Value);
-
-  /// Convert ranges back to string representation for debugging
-  static std::string rangesToString(const ArrayRef<Range> Ranges,
-                                    const char Separator = ',');
+  static bool contains(ArrayRef<Range> Ranges, int64_t Value);
 
   /// Print ranges to output stream
-  static void printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges);
+  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges);
 
   /// Merge adjacent/consecutive ranges into single ranges
   /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10]
-  static RangeList mergeAdjacentRanges(const ArrayRef<Range> Ranges);
+  static RangeList mergeAdjacentRanges(ArrayRef<Range> Ranges);
 };
 
 } // end namespace llvm
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 8add03d957f24..964964999abd4 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -36,25 +36,18 @@ static OptDisable &getOptDisabler() {
 static cl::opt<int> OptBisectLimit(
     "opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional,
     cl::cb<void, int>([](int Limit) {
-      if (Limit == -1) {
+      if (Limit == -1)
         // -1 means run all passes
         getOptBisector().setRanges({{1, std::numeric_limits<int>::max()}});
-      } else if (Limit == 0) {
+      else if (Limit == 0)
         // 0 means run no passes
         getOptBisector().setRanges({{0, 0}});
-      } else if (Limit > 0) {
+      else if (Limit > 0)
         // Convert limit to range 1-Limit
-        std::string RangeStr = Limit == 1 ? "1" : "1-" + llvm::utostr(Limit);
-        auto Ranges = RangeUtils::parseRanges(RangeStr);
-        if (!Ranges) {
-          handleAllErrors(Ranges.takeError(), [&](const StringError &E) {
-            errs() << "Error: Invalid limit for -opt-bisect-limit: " << Limit
-                   << " (" << E.getMessage() << ")\n";
-          });
-          exit(1);
-        }
-        getOptBisector().setRanges(std::move(*Ranges));
-      }
+        getOptBisector().setRanges({{1, Limit}});
+      else
+        llvm_unreachable(
+            ("Invalid limit for -opt-bisect-limit: " + llvm::utostr(Limit)).c_str());
     }),
     cl::desc(
         "Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index f2567970c6670..bdc7e50171898 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/Range.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/Regex.h"
 #include "llvm/Support/raw_ostream.h"
@@ -14,21 +15,17 @@
 
 using namespace llvm;
 
-Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
-                                                        const char Separator) {
+Expected<RangeUtils::RangeList> RangeUtils::parseRanges(StringRef Str,
+                                                        char Separator) {
   RangeList Ranges;
 
   if (Str.empty())
     return std::move(Ranges);
 
-  // Split by the specified separator
-  SmallVector<StringRef, 8> Parts;
-  Str.split(Parts, Separator, -1, false);
-
   // Regex to match either single number or range "num1-num2"
   const Regex RangeRegex("^([0-9]+)(-([0-9]+))?$");
 
-  for (StringRef Part : Parts) {
+  for (StringRef Part : llvm::split(Str, Separator)) {
     Part = Part.trim();
     if (Part.empty())
       continue;
@@ -68,24 +65,10 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(const StringRef Str,
     Ranges.push_back(Range(Begin, End));
   }
 
-  return std::move(Ranges);
-}
-
-bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges,
-                             const char Separator) {
-  auto ExpectedRanges = parseRanges(Str, Separator);
-  if (!ExpectedRanges) {
-    // For backward compatibility, print error to stderr
-    handleAllErrors(ExpectedRanges.takeError(), [](const StringError &E) {
-      errs() << E.getMessage() << "\n";
-    });
-    return false;
-  }
-  Ranges = std::move(*ExpectedRanges);
-  return true;
+  return Ranges;
 }
 
-bool RangeUtils::contains(const ArrayRef<Range> Ranges, const int64_t Value) {
+bool RangeUtils::contains(ArrayRef<Range> Ranges, int64_t Value) {
   for (const Range &R : Ranges) {
     if (R.contains(Value))
       return true;
@@ -93,22 +76,7 @@ bool RangeUtils::contains(const ArrayRef<Range> Ranges, const int64_t Value) {
   return false;
 }
 
-std::string RangeUtils::rangesToString(const ArrayRef<Range> Ranges,
-                                       const char Separator) {
-  std::ostringstream OS;
-  for (size_t I = 0; I < Ranges.size(); ++I) {
-    if (I > 0)
-      OS << Separator;
-    const Range &R = Ranges[I];
-    if (R.Begin == R.End)
-      OS << R.Begin;
-    else
-      OS << R.Begin << "-" << R.End;
-  }
-  return OS.str();
-}
-
-void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges) {
+void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges) {
   if (Ranges.empty())
     OS << "empty";
   else {
@@ -128,7 +96,7 @@ void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges) {
 }
 
 RangeUtils::RangeList
-RangeUtils::mergeAdjacentRanges(const ArrayRef<Range> Ranges) {
+RangeUtils::mergeAdjacentRanges(ArrayRef<Range> Ranges) {
   if (Ranges.empty())
     return {};
 
diff --git a/llvm/test/Other/opt-bisect-ranges.ll b/llvm/test/Other/opt-bisect-ranges.ll
index 5f48aa963290c..5cdfba699e28e 100644
--- a/llvm/test/Other/opt-bisect-ranges.ll
+++ b/llvm/test/Other/opt-bisect-ranges.ll
@@ -1,7 +1,7 @@
 ; Test that verifies functionality for -opt-bisect with range specifications
 
 ; Test basic range functionality: run passes 1-3 and 7-8
-; RUN: opt -O1 -opt-bisect=1-3,7-8 %s 2>&1 | FileCheck %s --check-prefix=CHECK-RANGES
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=1-3,7-8 %s 2>&1 | FileCheck %s --check-prefix=CHECK-RANGES
 ; CHECK-RANGES: BISECT: running pass (1) annotation2metadata on [module]
 ; CHECK-RANGES: BISECT: running pass (2) forceattrs on [module]
 ; CHECK-RANGES: BISECT: running pass (3) inferattrs on [module]
@@ -12,7 +12,7 @@
 ; CHECK-RANGES: BISECT: running pass (8) openmp-opt on [module]
 
 ; Test single pass selection: run only pass 5
-; RUN: opt -O1 -opt-bisect=5 %s 2>&1 | FileCheck %s --check-prefix=CHECK-SINGLE
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=5 %s 2>&1 | FileCheck %s --check-prefix=CHECK-SINGLE
 ; CHECK-SINGLE: BISECT: NOT running pass (1) annotation2metadata on [module]
 ; CHECK-SINGLE: BISECT: NOT running pass (2) forceattrs on [module]
 ; CHECK-SINGLE: BISECT: NOT running pass (3) inferattrs on [module]
@@ -21,7 +21,7 @@
 ; CHECK-SINGLE: BISECT: NOT running pass (6) sroa on foo
 
 ; Test running no passes
-; RUN: opt -O1 -opt-bisect=0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
 ; CHECK-NONE: BISECT: NOT running pass (1) annotation2metadata on [module]
 ; CHECK-NONE: BISECT: NOT running pass (2) forceattrs on [module]
 ; CHECK-NONE: BISECT: NOT running pass (3) inferattrs on [module]
@@ -30,7 +30,7 @@
 ; CHECK-NONE: BISECT: NOT running pass (6) sroa on foo
 
 ; Test running all passes
-; RUN: opt -O1 -opt-bisect=-1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-ALL
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=-1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-ALL
 ; CHECK-ALL: BISECT: running pass (1) annotation2metadata on [module]
 ; CHECK-ALL: BISECT: running pass (2) forceattrs on [module]
 ; CHECK-ALL: BISECT: running pass (3) inferattrs on [module]
@@ -39,7 +39,7 @@
 ; CHECK-ALL: BISECT: running pass (6) sroa on foo
 
 ; Test backward compatibility: -opt-bisect-limit=3 should be equivalent to -opt-bisect=1-3
-; RUN: opt -O1 -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
 ; CHECK-LIMIT: BISECT: running pass (1) annotation2metadata on [module]
 ; CHECK-LIMIT: BISECT: running pass (2) forceattrs on [module]
 ; CHECK-LIMIT: BISECT: running pass (3) inferattrs on [module]
diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index 94fc281386cdd..17f215049e387 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -127,14 +127,6 @@ TEST(RangeUtilsTest, Contains) {
   EXPECT_FALSE(RangeUtils::contains(Ranges, 21));
 }
 
-TEST(RangeUtilsTest, RangesToString) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
-
-  std::string Result = RangeUtils::rangesToString(Ranges);
-  EXPECT_EQ(Result, "1-5,10,15-20");
-}
-
 TEST(RangeUtilsTest, SeparatorParameter) {
   RangeUtils::RangeList ColonRanges, CommaRanges;
 

>From 5a80bfb990f6595040efed5aa4c74089360a0380 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Fri, 5 Sep 2025 20:21:16 +0000
Subject: [PATCH 27/31] format

---
 llvm/include/llvm/Support/Range.h    |  7 +++----
 llvm/lib/IR/OptBisect.cpp            |  3 ++-
 llvm/lib/Support/Range.cpp           |  3 +--
 llvm/test/Other/opt-bisect-ranges.ll | 15 ++++++++++-----
 4 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 0714d26b963bc..c785546640f36 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -27,7 +27,8 @@ class raw_ostream;
 
 namespace llvm {
 
-/// Represents a range of integers [Begin, End], inclusive on both ends, where Begin <= End.
+/// Represents a range of integers [Begin, End], inclusive on both ends, where
+/// Begin <= End.
 struct Range {
   int64_t Begin;
   int64_t End;
@@ -38,9 +39,7 @@ struct Range {
   Range(int64_t Single) : Begin(Single), End(Single) {}
 
   /// Check if the given value is within this range (inclusive)
-  bool contains(int64_t Value) const {
-    return Value >= Begin && Value <= End;
-  }
+  bool contains(int64_t Value) const { return Value >= Begin && Value <= End; }
 
   /// Check if this range overlaps with another range
   bool overlaps(const Range &Other) const {
diff --git a/llvm/lib/IR/OptBisect.cpp b/llvm/lib/IR/OptBisect.cpp
index 964964999abd4..0523d1450751e 100644
--- a/llvm/lib/IR/OptBisect.cpp
+++ b/llvm/lib/IR/OptBisect.cpp
@@ -47,7 +47,8 @@ static cl::opt<int> OptBisectLimit(
         getOptBisector().setRanges({{1, Limit}});
       else
         llvm_unreachable(
-            ("Invalid limit for -opt-bisect-limit: " + llvm::utostr(Limit)).c_str());
+            ("Invalid limit for -opt-bisect-limit: " + llvm::utostr(Limit))
+                .c_str());
     }),
     cl::desc(
         "Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index bdc7e50171898..76cacb26b10b4 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -95,8 +95,7 @@ void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges) {
   }
 }
 
-RangeUtils::RangeList
-RangeUtils::mergeAdjacentRanges(ArrayRef<Range> Ranges) {
+RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(ArrayRef<Range> Ranges) {
   if (Ranges.empty())
     return {};
 
diff --git a/llvm/test/Other/opt-bisect-ranges.ll b/llvm/test/Other/opt-bisect-ranges.ll
index 5cdfba699e28e..84b2d336f6f6e 100644
--- a/llvm/test/Other/opt-bisect-ranges.ll
+++ b/llvm/test/Other/opt-bisect-ranges.ll
@@ -1,7 +1,8 @@
 ; Test that verifies functionality for -opt-bisect with range specifications
 
 ; Test basic range functionality: run passes 1-3 and 7-8
-; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=1-3,7-8 %s 2>&1 | FileCheck %s --check-prefix=CHECK-RANGES
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
+; RUN:   -opt-bisect=1-3,7-8 %s 2>&1 | FileCheck %s --check-prefix=CHECK-RANGES
 ; CHECK-RANGES: BISECT: running pass (1) annotation2metadata on [module]
 ; CHECK-RANGES: BISECT: running pass (2) forceattrs on [module]
 ; CHECK-RANGES: BISECT: running pass (3) inferattrs on [module]
@@ -12,7 +13,8 @@
 ; CHECK-RANGES: BISECT: running pass (8) openmp-opt on [module]
 
 ; Test single pass selection: run only pass 5
-; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=5 %s 2>&1 | FileCheck %s --check-prefix=CHECK-SINGLE
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
+; RUN:   -opt-bisect=5 %s 2>&1 | FileCheck %s --check-prefix=CHECK-SINGLE
 ; CHECK-SINGLE: BISECT: NOT running pass (1) annotation2metadata on [module]
 ; CHECK-SINGLE: BISECT: NOT running pass (2) forceattrs on [module]
 ; CHECK-SINGLE: BISECT: NOT running pass (3) inferattrs on [module]
@@ -21,7 +23,8 @@
 ; CHECK-SINGLE: BISECT: NOT running pass (6) sroa on foo
 
 ; Test running no passes
-; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
+; RUN:   -opt-bisect=0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
 ; CHECK-NONE: BISECT: NOT running pass (1) annotation2metadata on [module]
 ; CHECK-NONE: BISECT: NOT running pass (2) forceattrs on [module]
 ; CHECK-NONE: BISECT: NOT running pass (3) inferattrs on [module]
@@ -30,7 +33,8 @@
 ; CHECK-NONE: BISECT: NOT running pass (6) sroa on foo
 
 ; Test running all passes
-; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect=-1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-ALL
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
+; RUN:   -opt-bisect=-1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-ALL
 ; CHECK-ALL: BISECT: running pass (1) annotation2metadata on [module]
 ; CHECK-ALL: BISECT: running pass (2) forceattrs on [module]
 ; CHECK-ALL: BISECT: running pass (3) inferattrs on [module]
@@ -39,7 +43,8 @@
 ; CHECK-ALL: BISECT: running pass (6) sroa on foo
 
 ; Test backward compatibility: -opt-bisect-limit=3 should be equivalent to -opt-bisect=1-3
-; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
+; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
+; RUN:   -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
 ; CHECK-LIMIT: BISECT: running pass (1) annotation2metadata on [module]
 ; CHECK-LIMIT: BISECT: running pass (2) forceattrs on [module]
 ; CHECK-LIMIT: BISECT: running pass (3) inferattrs on [module]

>From 3616aa638bf60f5e5051929865cee1e546af7c39 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Fri, 5 Sep 2025 20:47:49 +0000
Subject: [PATCH 28/31] more cleanup

---
 llvm/include/llvm/IR/OptBisect.h     |   2 +-
 llvm/include/llvm/Support/Range.h    |  39 ++++++----
 llvm/lib/Support/DebugCounter.cpp    |   2 +-
 llvm/lib/Support/Range.cpp           |  24 +++---
 llvm/unittests/Support/RangeTest.cpp | 109 ++++++++++++++++-----------
 5 files changed, 98 insertions(+), 78 deletions(-)

diff --git a/llvm/include/llvm/IR/OptBisect.h b/llvm/include/llvm/IR/OptBisect.h
index 9e3400325defd..de008338209a5 100644
--- a/llvm/include/llvm/IR/OptBisect.h
+++ b/llvm/include/llvm/IR/OptBisect.h
@@ -71,7 +71,7 @@ class LLVM_ABI OptBisect : public OptPassGate {
   /// isEnabled() should return true before calling shouldRunPass().
   bool isEnabled() const override { return !BisectRanges.empty(); }
 
-  /// Set ranges directly from a RangeList
+  /// Set ranges directly from a RangeList.
   void setRanges(RangeUtils::RangeList Ranges) {
     BisectRanges = std::move(Ranges);
   }
diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index c785546640f36..67bdf56da1f64 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -33,55 +33,62 @@ struct Range {
   int64_t Begin;
   int64_t End;
 
+  /// Create a range [Begin, End].
   Range(int64_t Begin, int64_t End) : Begin(Begin), End(End) {
     assert(Begin <= End && "Range Begin must be <= End");
   }
+  /// Create a range [Single, Single].
   Range(int64_t Single) : Begin(Single), End(Single) {}
 
-  /// Check if the given value is within this range (inclusive)
+  /// Check if the given value is within this range (inclusive).
   bool contains(int64_t Value) const { return Value >= Begin && Value <= End; }
 
-  /// Check if this range overlaps with another range
+  /// Check if this range overlaps with another range.
   bool overlaps(const Range &Other) const {
     return Begin <= Other.End && End >= Other.Begin;
   }
 
-  /// Get the size of this range
+  /// Get the size of this range.
   int64_t size() const { return End - Begin + 1; }
 
-  std::string toString() const {
-    return std::to_string(Begin) + "-" + std::to_string(End);
+  /// Print the range to the output stream.
+  void print(raw_ostream &OS) const {
+    if (Begin == End)
+      OS << Begin;
+    else
+      OS << Begin << "-" << End;
   }
 
-  void print(raw_ostream &OS) const { OS << Begin << "-" << End; }
-
   bool operator==(const Range &Other) const {
     return Begin == Other.Begin && End == Other.End;
   }
 };
 
-/// Utility class for parsing and managing range specifications
+/// Utility class for parsing and managing range specifications.
 class RangeUtils {
 public:
   using RangeList = SmallVector<Range, 8>;
 
   /// Parse a range specification string like "1-10,20-30,45" or
   /// "1-10:20-30:45". Ranges must be in increasing order and non-overlapping.
-  /// \param RangeStr The string to parse
-  /// \param Separator The separator character to use (',' or ':')
+  /// \param RangeStr The string to parse.
+  /// \param Separator The separator character to use (',' or ':').
   /// \returns Expected<RangeList> containing the parsed ranges on success,
-  ///          or an Error on failure
+  ///          or an Error on failure.
   static Expected<RangeList> parseRanges(StringRef RangeStr,
                                          char Separator = ',');
 
-  /// Check if a value is contained in any of the ranges
+  /// Check if a value is contained in any of the ranges.
   static bool contains(ArrayRef<Range> Ranges, int64_t Value);
 
-  /// Print ranges to output stream
-  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges);
+  /// Print ranges to output stream.
+  /// \param OS The output stream to print to.
+  /// \param Ranges The ranges to print.
+  /// \param Separator The separator character to use between ranges (i.e. ',' or ':').
+  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges, char Separator = ',');
 
-  /// Merge adjacent/consecutive ranges into single ranges
-  /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10]
+  /// Merge adjacent/consecutive ranges into single ranges.
+  /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10].
   static RangeList mergeAdjacentRanges(ArrayRef<Range> Ranges);
 };
 
diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp
index 7e65a3efa18ce..1abf4f872f03d 100644
--- a/llvm/lib/Support/DebugCounter.cpp
+++ b/llvm/lib/Support/DebugCounter.cpp
@@ -10,7 +10,7 @@ using namespace llvm;
 namespace llvm {
 
 void DebugCounter::printChunks(raw_ostream &OS, ArrayRef<Range> Ranges) {
-  RangeUtils::printRanges(OS, Ranges);
+  RangeUtils::printRanges(OS, Ranges, ':');
 }
 
 } // namespace llvm
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index 76cacb26b10b4..09bdc567597aa 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -22,7 +22,7 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(StringRef Str,
   if (Str.empty())
     return std::move(Ranges);
 
-  // Regex to match either single number or range "num1-num2"
+  // Regex to match either single number or range "num1-num2".
   const Regex RangeRegex("^([0-9]+)(-([0-9]+))?$");
 
   for (StringRef Part : llvm::split(Str, Separator)) {
@@ -43,7 +43,7 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(StringRef Str,
                                Matches[1].str().c_str());
 
     if (!Matches[3].empty()) {
-      // Range format "begin-end"
+      // Range format "begin-end".
       if (Matches[3].getAsInteger(10, End))
         return createStringError(std::errc::invalid_argument,
                                  "Failed to parse number: '%s'",
@@ -52,10 +52,10 @@ Expected<RangeUtils::RangeList> RangeUtils::parseRanges(StringRef Str,
         return createStringError(std::errc::invalid_argument,
                                  "Invalid range: %lld >= %lld", Begin, End);
     } else
-      // Single number
+      // Single number.
       End = Begin;
 
-    // Check ordering constraint (ranges must be in increasing order)
+    // Check ordering constraint (ranges must be in increasing order).
     if (!Ranges.empty() && Begin <= Ranges.back().End)
       return createStringError(
           std::errc::invalid_argument,
@@ -76,21 +76,17 @@ bool RangeUtils::contains(ArrayRef<Range> Ranges, int64_t Value) {
   return false;
 }
 
-void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges) {
+void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges, char Separator) {
   if (Ranges.empty())
     OS << "empty";
   else {
     bool IsFirst = true;
     for (const Range &R : Ranges) {
       if (!IsFirst)
-        OS << ':';
+        OS << Separator;
       else
         IsFirst = false;
-
-      if (R.Begin == R.End)
-        OS << R.Begin;
-      else
-        OS << R.Begin << "-" << R.End;
+      R.print(OS);
     }
   }
 }
@@ -106,12 +102,12 @@ RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(ArrayRef<Range> Ranges) {
     const Range &Current = Ranges[I];
     Range &Last = Result.back();
 
-    // Check if current range is adjacent to the last merged range
+    // Check if current range is adjacent to the last merged range.
     if (Current.Begin == Last.End + 1) {
-      // Merge by extending the end of the last range
+      // Merge by extending the end of the last range.
       Last.End = Current.End;
     } else {
-      // Not adjacent, add as separate range
+      // Not adjacent, add as separate range.
       Result.push_back(Current);
     }
   }
diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index 17f215049e387..6140dbbd125e4 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -46,27 +46,30 @@ TEST(RangeTest, RangeOverlaps) {
 }
 
 TEST(RangeUtilsTest, ParseSingleNumber) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("42", Ranges));
+  auto ER = RangeUtils::parseRanges("42");
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 1U);
   EXPECT_EQ(Ranges[0].Begin, 42);
   EXPECT_EQ(Ranges[0].End, 42);
 }
 
 TEST(RangeUtilsTest, ParseSingleRange) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("10-20", Ranges));
+  auto ER = RangeUtils::parseRanges("10-20");
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 1U);
   EXPECT_EQ(Ranges[0].Begin, 10);
   EXPECT_EQ(Ranges[0].End, 20);
 }
 
 TEST(RangeUtilsTest, ParseMultipleRanges) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  auto ER = RangeUtils::parseRanges("1-5,10,15-20");
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 3U);
 
-  // Ranges are in input order (DebugCounter style)
+  // Ranges are in input order (DebugCounter style).
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);
   EXPECT_EQ(Ranges[1].Begin, 10);
@@ -76,8 +79,9 @@ TEST(RangeUtilsTest, ParseMultipleRanges) {
 }
 
 TEST(RangeUtilsTest, ParseColonSeparated) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", Ranges, ':'));
+  auto ER = RangeUtils::parseRanges("1-5:10:15-20", ':');
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);
@@ -88,29 +92,37 @@ TEST(RangeUtilsTest, ParseColonSeparated) {
 }
 
 TEST(RangeUtilsTest, ParseEmptyString) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("", Ranges));
+  auto ER = RangeUtils::parseRanges("");
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
   EXPECT_TRUE(Ranges.empty());
 }
 
 TEST(RangeUtilsTest, ParseInvalidRanges) {
-  RangeUtils::RangeList Ranges;
-
-  // Invalid number
-  EXPECT_FALSE(RangeUtils::parseRanges("abc", Ranges));
-
-  // Invalid range (begin > end)
-  EXPECT_FALSE(RangeUtils::parseRanges("10-5", Ranges));
-
-  // Out of order ranges (DebugCounter constraint)
-  EXPECT_FALSE(RangeUtils::parseRanges("10,5", Ranges));
-  EXPECT_FALSE(
-      RangeUtils::parseRanges("1-5,3-7", Ranges)); // Overlapping not allowed
+  // Invalid number.
+  auto ER1 = RangeUtils::parseRanges("abc");
+  EXPECT_FALSE(ER1);
+  consumeError(ER1.takeError());
+
+  // Invalid range (begin > end).
+  auto ER2 = RangeUtils::parseRanges("10-5");
+  EXPECT_FALSE(ER2);
+  consumeError(ER2.takeError());
+
+  // Out of order ranges (DebugCounter constraint and overlap).
+  auto ER3 = RangeUtils::parseRanges("10,5");
+  EXPECT_FALSE(ER3);
+  consumeError(ER3.takeError());
+
+  auto ER4 = RangeUtils::parseRanges("1-5,3-7");
+  EXPECT_FALSE(ER4);
+  consumeError(ER4.takeError());
 }
 
 TEST(RangeUtilsTest, Contains) {
-  RangeUtils::RangeList Ranges;
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  auto ER = RangeUtils::parseRanges("1-5,10,15-20");
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
 
   EXPECT_TRUE(RangeUtils::contains(Ranges, 1));
   EXPECT_TRUE(RangeUtils::contains(Ranges, 3));
@@ -130,9 +142,14 @@ TEST(RangeUtilsTest, Contains) {
 TEST(RangeUtilsTest, SeparatorParameter) {
   RangeUtils::RangeList ColonRanges, CommaRanges;
 
-  // Test explicit separator parameters
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5:10:15-20", ColonRanges, ':'));
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", CommaRanges, ','));
+  // Test explicit separator parameters.
+  auto ERC = RangeUtils::parseRanges("1-5:10:15-20", ':');
+  ASSERT_TRUE(ERC);
+  ColonRanges = std::move(*ERC);
+
+  auto ERM = RangeUtils::parseRanges("1-5,10,15-20", ',');
+  ASSERT_TRUE(ERM);
+  CommaRanges = std::move(*ERM);
 
   EXPECT_EQ(ColonRanges.size(), CommaRanges.size());
   for (size_t I = 0; I < ColonRanges.size(); ++I) {
@@ -140,7 +157,7 @@ TEST(RangeUtilsTest, SeparatorParameter) {
     EXPECT_EQ(ColonRanges[I].End, CommaRanges[I].End);
   }
 
-  // Test that both work with contains()
+  // Test that both work with contains().
   EXPECT_TRUE(RangeUtils::contains(ColonRanges, 3));
   EXPECT_TRUE(RangeUtils::contains(CommaRanges, 3));
   EXPECT_TRUE(RangeUtils::contains(ColonRanges, 10));
@@ -153,10 +170,10 @@ TEST(RangeUtilsTest, SeparatorParameter) {
 }
 
 TEST(RangeUtilsTest, DefaultCommaSeparator) {
-  RangeUtils::RangeList Ranges;
-
-  // Test that comma is the default separator
-  EXPECT_TRUE(RangeUtils::parseRanges("1-5,10,15-20", Ranges));
+  // Test that comma is the default separator.
+  auto ER = RangeUtils::parseRanges("1-5,10,15-20");
+  ASSERT_TRUE(ER);
+  auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);
   EXPECT_EQ(Ranges[0].End, 5);
@@ -173,13 +190,13 @@ TEST(RangeTest, MergeAdjacentRanges) {
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_TRUE(Result.empty());
 
-  // Single range - no change
+  // Single range - no change.
   Input.push_back(Range(5, 10));
   Expected.push_back(Range(5, 10));
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
 
-  // Adjacent ranges should merge
+  // Adjacent ranges should merge.
   Input.clear();
   Expected.clear();
   Input.push_back(Range(1, 3));
@@ -189,32 +206,32 @@ TEST(RangeTest, MergeAdjacentRanges) {
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
 
-  // Non-adjacent ranges should not merge
+  // Non-adjacent ranges should not merge.
   Input.clear();
   Expected.clear();
   Input.push_back(Range(1, 3));
-  Input.push_back(Range(5, 7));   // Gap between 3 and 5
-  Input.push_back(Range(10, 12)); // Gap between 7 and 10
+  Input.push_back(Range(5, 7));   // Gap between 3 and 5.
+  Input.push_back(Range(10, 12)); // Gap between 7 and 10.
   Expected.push_back(Range(1, 3));
   Expected.push_back(Range(5, 7));
   Expected.push_back(Range(10, 12));
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
 
-  // Mixed adjacent and non-adjacent
+  // Mixed adjacent and non-adjacent.
   Input.clear();
   Expected.clear();
   Input.push_back(Range(1, 3));
-  Input.push_back(Range(4, 6));     // Adjacent to first
-  Input.push_back(Range(8, 10));    // Gap
-  Input.push_back(Range(11, 13));   // Adjacent to third
-  Input.push_back(Range(14, 16));   // Adjacent to fourth
-  Expected.push_back(Range(1, 6));  // Merged 1-3 and 4-6
-  Expected.push_back(Range(8, 16)); // Merged 8-10, 11-13, 14-16
+  Input.push_back(Range(4, 6));     // Adjacent to first.
+  Input.push_back(Range(8, 10));    // Gap.
+  Input.push_back(Range(11, 13));   // Adjacent to third.
+  Input.push_back(Range(14, 16));   // Adjacent to fourth.
+  Expected.push_back(Range(1, 6));  // Merged 1-3 and 4-6.
+  Expected.push_back(Range(8, 16)); // Merged 8-10, 11-13, 14-16.
   Result = RangeUtils::mergeAdjacentRanges(Input);
   EXPECT_EQ(Expected, Result);
 
-  // Single numbers that are adjacent
+  // Single numbers that are adjacent.
   Input.clear();
   Expected.clear();
   Input.push_back(Range(5));

>From 58825847840c71b9821e2e3cbe45a261345c9b25 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Fri, 5 Sep 2025 20:47:59 +0000
Subject: [PATCH 29/31] format

---
 llvm/include/llvm/Support/Range.h | 6 ++++--
 llvm/lib/Support/Range.cpp        | 3 ++-
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/Support/Range.h b/llvm/include/llvm/Support/Range.h
index 67bdf56da1f64..3d6d08bec27e6 100644
--- a/llvm/include/llvm/Support/Range.h
+++ b/llvm/include/llvm/Support/Range.h
@@ -84,8 +84,10 @@ class RangeUtils {
   /// Print ranges to output stream.
   /// \param OS The output stream to print to.
   /// \param Ranges The ranges to print.
-  /// \param Separator The separator character to use between ranges (i.e. ',' or ':').
-  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges, char Separator = ',');
+  /// \param Separator The separator character to use between ranges (i.e. ','
+  /// or ':').
+  static void printRanges(raw_ostream &OS, ArrayRef<Range> Ranges,
+                          char Separator = ',');
 
   /// Merge adjacent/consecutive ranges into single ranges.
   /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10].
diff --git a/llvm/lib/Support/Range.cpp b/llvm/lib/Support/Range.cpp
index 09bdc567597aa..a3f52fc73b89d 100644
--- a/llvm/lib/Support/Range.cpp
+++ b/llvm/lib/Support/Range.cpp
@@ -76,7 +76,8 @@ bool RangeUtils::contains(ArrayRef<Range> Ranges, int64_t Value) {
   return false;
 }
 
-void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges, char Separator) {
+void RangeUtils::printRanges(raw_ostream &OS, ArrayRef<Range> Ranges,
+                             char Separator) {
   if (Ranges.empty())
     OS << "empty";
   else {

>From 0f0499c4a5ad2c9febba20cc258518b0b42a47e1 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Fri, 5 Sep 2025 20:54:55 +0000
Subject: [PATCH 30/31] range test cleanup

---
 llvm/unittests/Support/RangeTest.cpp | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index 6140dbbd125e4..f10038c759f78 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -8,6 +8,7 @@
 
 #include "llvm/Support/Range.h"
 #include "gtest/gtest.h"
+#include "llvm/Testing/Support/Error.h"
 
 using namespace llvm;
 
@@ -47,7 +48,7 @@ TEST(RangeTest, RangeOverlaps) {
 
 TEST(RangeUtilsTest, ParseSingleNumber) {
   auto ER = RangeUtils::parseRanges("42");
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 1U);
   EXPECT_EQ(Ranges[0].Begin, 42);
@@ -56,7 +57,7 @@ TEST(RangeUtilsTest, ParseSingleNumber) {
 
 TEST(RangeUtilsTest, ParseSingleRange) {
   auto ER = RangeUtils::parseRanges("10-20");
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 1U);
   EXPECT_EQ(Ranges[0].Begin, 10);
@@ -65,7 +66,7 @@ TEST(RangeUtilsTest, ParseSingleRange) {
 
 TEST(RangeUtilsTest, ParseMultipleRanges) {
   auto ER = RangeUtils::parseRanges("1-5,10,15-20");
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 3U);
 
@@ -80,7 +81,7 @@ TEST(RangeUtilsTest, ParseMultipleRanges) {
 
 TEST(RangeUtilsTest, ParseColonSeparated) {
   auto ER = RangeUtils::parseRanges("1-5:10:15-20", ':');
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);
@@ -93,7 +94,7 @@ TEST(RangeUtilsTest, ParseColonSeparated) {
 
 TEST(RangeUtilsTest, ParseEmptyString) {
   auto ER = RangeUtils::parseRanges("");
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
   EXPECT_TRUE(Ranges.empty());
 }
@@ -101,27 +102,27 @@ TEST(RangeUtilsTest, ParseEmptyString) {
 TEST(RangeUtilsTest, ParseInvalidRanges) {
   // Invalid number.
   auto ER1 = RangeUtils::parseRanges("abc");
-  EXPECT_FALSE(ER1);
+  EXPECT_THAT_EXPECTED(ER1, Failed());
   consumeError(ER1.takeError());
 
   // Invalid range (begin > end).
   auto ER2 = RangeUtils::parseRanges("10-5");
-  EXPECT_FALSE(ER2);
+  EXPECT_THAT_EXPECTED(ER2, Failed());
   consumeError(ER2.takeError());
 
   // Out of order ranges (DebugCounter constraint and overlap).
   auto ER3 = RangeUtils::parseRanges("10,5");
-  EXPECT_FALSE(ER3);
+  EXPECT_THAT_EXPECTED(ER3, Failed());
   consumeError(ER3.takeError());
 
   auto ER4 = RangeUtils::parseRanges("1-5,3-7");
-  EXPECT_FALSE(ER4);
+  EXPECT_THAT_EXPECTED(ER4, Failed());
   consumeError(ER4.takeError());
 }
 
 TEST(RangeUtilsTest, Contains) {
   auto ER = RangeUtils::parseRanges("1-5,10,15-20");
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
 
   EXPECT_TRUE(RangeUtils::contains(Ranges, 1));
@@ -144,11 +145,11 @@ TEST(RangeUtilsTest, SeparatorParameter) {
 
   // Test explicit separator parameters.
   auto ERC = RangeUtils::parseRanges("1-5:10:15-20", ':');
-  ASSERT_TRUE(ERC);
+  ASSERT_THAT_EXPECTED(ERC, Succeeded());
   ColonRanges = std::move(*ERC);
 
   auto ERM = RangeUtils::parseRanges("1-5,10,15-20", ',');
-  ASSERT_TRUE(ERM);
+  ASSERT_THAT_EXPECTED(ERM, Succeeded());
   CommaRanges = std::move(*ERM);
 
   EXPECT_EQ(ColonRanges.size(), CommaRanges.size());
@@ -172,7 +173,7 @@ TEST(RangeUtilsTest, SeparatorParameter) {
 TEST(RangeUtilsTest, DefaultCommaSeparator) {
   // Test that comma is the default separator.
   auto ER = RangeUtils::parseRanges("1-5,10,15-20");
-  ASSERT_TRUE(ER);
+  ASSERT_THAT_EXPECTED(ER, Succeeded());
   auto Ranges = std::move(*ER);
   EXPECT_EQ(Ranges.size(), 3U);
   EXPECT_EQ(Ranges[0].Begin, 1);

>From 1615205b571fc53f500cdafdc3290a7714a7b157 Mon Sep 17 00:00:00 2001
From: Yonah Goldberg <ygoldberg at nvidia.com>
Date: Fri, 5 Sep 2025 20:55:06 +0000
Subject: [PATCH 31/31] format

---
 llvm/unittests/Support/RangeTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/unittests/Support/RangeTest.cpp b/llvm/unittests/Support/RangeTest.cpp
index f10038c759f78..75c381342ef26 100644
--- a/llvm/unittests/Support/RangeTest.cpp
+++ b/llvm/unittests/Support/RangeTest.cpp
@@ -7,8 +7,8 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/Range.h"
-#include "gtest/gtest.h"
 #include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
 
 using namespace llvm;
 



More information about the llvm-commits mailing list