From 329098d16054afc1975327e75ebf02fb0ebe37a5 Mon Sep 17 00:00:00 2001 From: Yonah Goldberg Date: Wed, 6 Aug 2025 21:28:33 +0000 Subject: [PATCH 01/25] 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 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 OptBisectLimit("opt-bisect-limit", cl::Hidden, }), cl::desc("Maximum optimization to perform")); +static cl::opt OptBisectSkip("opt-bisect-skip", cl::Hidden, + cl::init(OptBisect::Disabled), cl::Optional, + cl::cb([](int PassNum) { + getOptBisector().addSkip(PassNum); + }), + cl::desc("Skip pass at the given index in the optimization pipeline")); + static cl::opt 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 Date: Wed, 6 Aug 2025 21:28:51 +0000 Subject: [PATCH 02/25] 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 OptBisectLimit("opt-bisect-limit", cl::Hidden, }), cl::desc("Maximum optimization to perform")); -static cl::opt OptBisectSkip("opt-bisect-skip", cl::Hidden, - cl::init(OptBisect::Disabled), cl::Optional, - cl::cb([](int PassNum) { - getOptBisector().addSkip(PassNum); - }), - cl::desc("Skip pass at the given index in the optimization pipeline")); +static cl::opt OptBisectSkip( + "opt-bisect-skip", cl::Hidden, cl::init(OptBisect::Disabled), cl::Optional, + cl::cb([](int PassNum) { getOptBisector().addSkip(PassNum); }), + cl::desc("Skip pass at the given index in the optimization pipeline")); static cl::opt 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 Date: Wed, 6 Aug 2025 21:36:51 +0000 Subject: [PATCH 03/25] 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 Date: Wed, 6 Aug 2025 21:46:32 +0000 Subject: [PATCH 04/25] 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 Date: Wed, 20 Aug 2025 21:58:07 +0000 Subject: [PATCH 05/25] 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 OptBisectLimit("opt-bisect-limit", cl::Hidden, }), cl::desc("Maximum optimization to perform")); -static cl::opt OptBisectSkip( - "opt-bisect-skip", cl::Hidden, cl::init(OptBisect::Disabled), cl::Optional, - cl::cb([](int PassNum) { getOptBisector().addSkip(PassNum); }), - cl::desc("Skip pass at the given index in the optimization pipeline")); +static cl::list OptDisableIndices( + "opt-disable-indices", cl::Hidden, cl::CommaSeparated, cl::Optional, + cl::cb([](int Index) { getOptBisector().disablePassAtIndex(Index); }), + cl::desc("Disable passes at the given indices in the optimization pipeline (comma-separated list)")); static cl::opt 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 Date: Wed, 20 Aug 2025 22:03:17 +0000 Subject: [PATCH 06/25] 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 OptBisectLimit("opt-bisect-limit", cl::Hidden, }), cl::desc("Maximum optimization to perform")); -static cl::list OptDisableIndices( - "opt-disable-indices", cl::Hidden, cl::CommaSeparated, cl::Optional, - cl::cb([](int Index) { getOptBisector().disablePassAtIndex(Index); }), - cl::desc("Disable passes at the given indices in the optimization pipeline (comma-separated list)")); +static cl::list + OptDisableIndices("opt-disable-indices", cl::Hidden, cl::CommaSeparated, + cl::Optional, cl::cb([](int Index) { + getOptBisector().disablePassAtIndex(Index); + }), + cl::desc("Disable passes at the given indices in the " + "optimization pipeline (comma-separated list)")); static cl::opt OptBisectVerbose( "opt-bisect-verbose", From 4b382092d7b7dd2ccf9b02e476a69477bf80d869 Mon Sep 17 00:00:00 2001 From: Yonah Goldberg Date: Wed, 3 Sep 2025 04:33:27 +0000 Subject: [PATCH 07/25] 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 +#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 Ranges) { + BisectRanges.assign(Ranges.begin(), Ranges.end()); LastBisectNum = 0; } - static constexpr int Disabled = std::numeric_limits::max(); + /// Clear all ranges, effectively disabling bisection. + void clearRanges() { + BisectRanges.clear(); + LastBisectNum = 0; + } private: - int BisectLimit = Disabled; mutable int LastBisectNum = 0; - SmallSet 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 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); + // 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 &Res); + LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef 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 Chunks; + SmallVector Chunks; }; DenseMap 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 + +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; + + /// 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 Ranges); + + /// Merge adjacent/consecutive ranges into single ranges + /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10] + static RangeList mergeAdjacentRanges(ArrayRef 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 +#include using namespace llvm; @@ -31,19 +34,34 @@ static OptDisable &getOptDisabler() { } static cl::opt OptBisectLimit("opt-bisect-limit", cl::Hidden, - cl::init(OptBisect::Disabled), cl::Optional, + cl::init(-1), cl::Optional, cl::cb([](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 - OptDisableIndices("opt-disable-indices", cl::Hidden, cl::CommaSeparated, - cl::Optional, cl::cb([](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 OptBisectRanges( + "opt-bisect", cl::Hidden, cl::Optional, + cl::cb([](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 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 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 &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 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 Chunks; + RangeUtils::RangeList TempRanges; + SmallVector 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 + +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 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 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 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 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 StartChunks(cl::Positional, cl::Required); static cl::opt Pessimist("pessimist", cl::init(false)); -using Chunk = DebugCounter::Chunk; - namespace { -SmallVector simplifyChunksList(ArrayRef Chunks) { - SmallVector 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 Chunks) { - SmallVector SimpleChunks = simplifyChunksList(Chunks); +bool isStillInteresting(ArrayRef 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 Chunks) { return Res; } -bool increaseGranularity(SmallVector &Chunks) { +bool increaseGranularity(RangeUtils::RangeList &Chunks) { errs() << "Increasing granularity\n"; - SmallVector 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 &Chunks) { int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv); - SmallVector 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 Date: Wed, 3 Sep 2025 04:53:04 +0000 Subject: [PATCH 08/25] 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; /// 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 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 OptBisectLimit("opt-bisect-limit", cl::Hidden, static cl::opt OptBisectRanges( "opt-bisect", cl::Hidden, cl::Optional, cl::cb([](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 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 Parts; @@ -34,24 +34,24 @@ bool RangeUtils::parseRanges(StringRef Str, RangeList &Ranges, char Separator) { SmallVector 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 Date: Wed, 3 Sep 2025 18:19:58 +0000 Subject: [PATCH 09/25] 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 Date: Wed, 3 Sep 2025 21:07:57 +0000 Subject: [PATCH 10/25] 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 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 Date: Wed, 3 Sep 2025 21:46:06 +0000 Subject: [PATCH 11/25] 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 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 Chunks; + RangeUtils::RangeList Chunks; }; DenseMap 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; - /// 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 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 Ranges, const char Separator = ','); - /// Print ranges to output stream (DebugCounter-compatible) - static void printRanges(raw_ostream &OS, ArrayRef Ranges); + /// Print ranges to output stream + static void printRanges(raw_ostream &OS, const ArrayRef Ranges); /// Merge adjacent/consecutive ranges into single ranges /// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10] - static RangeList mergeAdjacentRanges(ArrayRef Ranges); + static RangeList mergeAdjacentRanges(const ArrayRef 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 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 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 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 Ranges) { +void RangeUtils::printRanges(raw_ostream &OS, const ArrayRef Ranges) { if (Ranges.empty()) { OS << "empty"; } else { @@ -115,7 +115,7 @@ void RangeUtils::printRanges(raw_ostream &OS, ArrayRef Ranges) { } } -RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(ArrayRef Ranges) { +RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(const ArrayRef 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 Date: Wed, 3 Sep 2025 21:46:22 +0000 Subject: [PATCH 12/25] 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; - /// 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 Ranges, const int64_t Value); /// Convert ranges back to string representation for debugging - static std::string rangesToString(const ArrayRef Ranges, const char Separator = ','); + static std::string rangesToString(const ArrayRef Ranges, + const char Separator = ','); /// Print ranges to output stream static void printRanges(raw_ostream &OS, const ArrayRef 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 OptBisectLimit("opt-bisect-limit", cl::Hidden, - cl::init(-1), cl::Optional, - cl::cb([](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 OptBisectLimit( + "opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional, + cl::cb([](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 OptBisectRanges( "opt-bisect", cl::Hidden, cl::Optional, cl::cb([](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 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 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 Ranges, const int64_t Value) { return false; } - - -std::string RangeUtils::rangesToString(const ArrayRef Ranges, const char Separator) { +std::string RangeUtils::rangesToString(const ArrayRef 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 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 Ranges) { } } -RangeUtils::RangeList RangeUtils::mergeAdjacentRanges(const ArrayRef Ranges) { +RangeUtils::RangeList +RangeUtils::mergeAdjacentRanges(const ArrayRef 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 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 Date: Wed, 3 Sep 2025 22:32:30 +0000 Subject: [PATCH 13/25] 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 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 containing the parsed ranges on success, + /// or an Error on failure + static Expected parseRanges(const StringRef RangeStr, + const char Separator = ','); + + /// Legacy interface for backward compatibility. + /// \deprecated Use the Expected 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 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 OptBisectLimit( static cl::opt OptBisectRanges( "opt-bisect", cl::Hidden, cl::Optional, cl::cb([](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 using namespace llvm; -bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges, - const char Separator) { - Ranges.clear(); +Expected 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 Parts; @@ -34,25 +35,25 @@ bool RangeUtils::parseRanges(const StringRef Str, RangeList &Ranges, SmallVector 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 Date: Wed, 3 Sep 2025 22:32:37 +0000 Subject: [PATCH 14/25] 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 parseRanges(const StringRef RangeStr, const char Separator = ','); - /// Legacy interface for backward compatibility. + /// Legacy interface for backward compatibility. /// \deprecated Use the Expected 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::parseRanges(const StringRef Str, - const char Separator) { + const char Separator) { RangeList Ranges; if (Str.empty()) @@ -36,24 +36,27 @@ Expected RangeUtils::parseRanges(const StringRef Str, SmallVector 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::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 Date: Wed, 3 Sep 2025 22:35:47 +0000 Subject: [PATCH 15/25] 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 Date: Wed, 3 Sep 2025 22:57:01 +0000 Subject: [PATCH 16/25] 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 Date: Thu, 4 Sep 2025 17:23:43 +0000 Subject: [PATCH 17/25] 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::parseRanges(const StringRef Str, continue; SmallVector 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 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 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 Date: Thu, 4 Sep 2025 17:31:34 +0000 Subject: [PATCH 18/25] 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 Date: Thu, 4 Sep 2025 17:31:48 +0000 Subject: [PATCH 19/25] 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 Date: Thu, 4 Sep 2025 17:49:40 +0000 Subject: [PATCH 20/25] 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 Date: Thu, 4 Sep 2025 18:56:02 +0000 Subject: [PATCH 21/25] 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 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 Date: Thu, 4 Sep 2025 18:56:12 +0000 Subject: [PATCH 22/25] 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 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 Date: Thu, 4 Sep 2025 19:56:43 +0000 Subject: [PATCH 23/25] 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 OptBisectLimit( "opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional, cl::cb([](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::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 OptBisectLimit( static cl::opt OptBisectRanges( "opt-bisect", cl::Hidden, cl::Optional, cl::cb([](const std::string &RangeStr) { + if (RangeStr == "-1") { + // -1 means run all passes + getOptBisector().setRanges({{1, std::numeric_limits::max()}}); + return; + } + auto Ranges = RangeUtils::parseRanges(RangeStr); if (!Ranges) { handleAllErrors(Ranges.takeError(), [&](const StringError &E) { @@ -70,7 +79,7 @@ static cl::opt 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 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 Date: Thu, 4 Sep 2025 19:58:35 +0000 Subject: [PATCH 24/25] 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 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 OptBisectVerbose( "opt-bisect-verbose", From dce302aabcb1cfcfe34dc4e2540b98b8c6fb98c7 Mon Sep 17 00:00:00 2001 From: Yonah Goldberg Date: Thu, 4 Sep 2025 19:58:49 +0000 Subject: [PATCH 25/25] 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 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 OptBisectVerbose( "opt-bisect-verbose",