Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
329098d
opt bisect skip
YonahGoldberg Aug 6, 2025
723514d
format
YonahGoldberg Aug 6, 2025
431c13b
comment
YonahGoldberg Aug 6, 2025
ec6842e
newline
YonahGoldberg Aug 6, 2025
6b3ed99
renaming
YonahGoldberg Aug 20, 2025
895a1bb
format
YonahGoldberg Aug 20, 2025
4b38209
range support
YonahGoldberg Sep 3, 2025
d3f5add
small changes
YonahGoldberg Sep 3, 2025
6f20867
remove opt-disable-indices
YonahGoldberg Sep 3, 2025
5f87193
remove
YonahGoldberg Sep 3, 2025
8ab62b3
cleanup
YonahGoldberg Sep 3, 2025
c3e5c7d
format
YonahGoldberg Sep 3, 2025
4cf5a99
use Expect for Range
YonahGoldberg Sep 3, 2025
0eefd68
format
YonahGoldberg Sep 3, 2025
a159538
fix hang
YonahGoldberg Sep 3, 2025
3215304
fix error handling tests
YonahGoldberg Sep 3, 2025
af7873f
format
YonahGoldberg Sep 4, 2025
c27ce56
cleanup
YonahGoldberg Sep 4, 2025
6c4fea1
clang format
YonahGoldberg Sep 4, 2025
5e1d443
fix minor formatting bug
YonahGoldberg Sep 4, 2025
466991d
small bug
YonahGoldberg Sep 4, 2025
0eaab13
format
YonahGoldberg Sep 4, 2025
5ce4228
allow -opt-bisect=0 and -opt-bisect=-1
YonahGoldberg Sep 4, 2025
095c358
comment change
YonahGoldberg Sep 4, 2025
dce302a
format
YonahGoldberg Sep 4, 2025
2b779d3
cleanup
YonahGoldberg Sep 5, 2025
5a80bfb
format
YonahGoldberg Sep 5, 2025
3616aa6
more cleanup
YonahGoldberg Sep 5, 2025
5882584
format
YonahGoldberg Sep 5, 2025
0f0499c
range test cleanup
YonahGoldberg Sep 5, 2025
1615205
format
YonahGoldberg Sep 5, 2025
98b2256
move range from struct -> class
YonahGoldberg Sep 5, 2025
debce85
better range size function
YonahGoldberg Sep 5, 2025
7db719f
range size checks
YonahGoldberg Sep 5, 2025
7f44558
format
YonahGoldberg Sep 5, 2025
6924b7b
format
YonahGoldberg Sep 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions llvm/include/llvm/IR/OptBisect.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
#ifndef LLVM_IR_OPTBISECT_H
#define LLVM_IR_OPTBISECT_H

#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Compiler.h"
#include <limits>
#include "llvm/Support/Range.h"

namespace llvm {

Expand All @@ -40,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:
Expand All @@ -53,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
Expand All @@ -67,20 +69,22 @@ 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 !BisectRanges.empty(); }

/// Set the new optimization limit and reset the counter. Passing
/// OptBisect::Disabled disables the limiting.
void setLimit(int Limit) {
BisectLimit = Limit;
LastBisectNum = 0;
/// Set ranges directly from a RangeList
void setRanges(RangeUtils::RangeList Ranges) {
BisectRanges = std::move(Ranges);
}

static constexpr int Disabled = std::numeric_limits<int>::max();
/// Clear all ranges, effectively disabling bisection.
void clearRanges() {
BisectRanges.clear();
LastBisectNum = 0;
}

private:
int BisectLimit = Disabled;
mutable int LastBisectNum = 0;
RangeUtils::RangeList BisectRanges;
};

/// This class implements a mechanism to disable passes and individual
Expand Down
16 changes: 3 additions & 13 deletions llvm/include/llvm/Support/DebugCounter.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "llvm/ADT/UniqueVector.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Range.h"
#include <string>

namespace llvm {
Expand All @@ -56,18 +57,7 @@ class raw_ostream;

class DebugCounter {
public:
struct Chunk {
int64_t Begin;
int64_t End;
LLVM_ABI void print(llvm::raw_ostream &OS);
bool contains(int64_t Idx) const { return Idx >= Begin && Idx <= End; }
};

LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Chunk>);

/// Return true on parsing error and print the error message on the
/// llvm::errs()
LLVM_ABI static bool parseChunks(StringRef Str, SmallVector<Chunk> &Res);
LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Range> Ranges);

/// Returns a reference to the singleton instance.
LLVM_ABI static DebugCounter &instance();
Expand Down Expand Up @@ -176,7 +166,7 @@ class DebugCounter {
uint64_t CurrChunkIdx = 0;
bool IsSet = false;
std::string Desc;
SmallVector<Chunk> Chunks;
RangeUtils::RangeList Chunks;
};

DenseMap<unsigned, CounterInfo> Counters;
Expand Down
97 changes: 97 additions & 0 deletions llvm/include/llvm/Support/Range.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===- 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 "llvm/Support/Error.h"
#include <cstdint>

namespace llvm {
class raw_ostream;
} // end namespace llvm

namespace llvm {

/// Represents a range of integers [Begin, End] (inclusive on both ends)
struct Range {
int64_t Begin;
int64_t End;

Range(const int64_t Begin, const int64_t End) : Begin(Begin), End(End) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Range(const int64_t Begin, const int64_t End) : Begin(Begin), End(End) {}
Range(int64_t Begin, int64_t End) : Begin(Begin), End(End) {}

Here and elsewhere, drop the const on non-reference/pointer params.

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;
}

/// 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; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return an unsigned type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I also realized that End - Begin can overflow. I don't know if what I implemented is overkill, but I made it safer so that it won't overflow.


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;
}
};

/// Utility class for parsing and managing range specifications
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Utility class for parsing and managing range specifications
/// Utility class for parsing and managing range specifications.

Here an elsewhere, end sentences with a period, including in doc comments.

class RangeUtils {
public:
using RangeList = SmallVector<Range, 8>;

/// Parse a range specification string like "1-10,20-30,45" or
/// "1-10:20-30:45". Ranges must be in increasing order and non-overlapping.
/// \param RangeStr The string to parse
/// \param Separator The separator character to use (',' or ':')
/// \returns Expected<RangeList> containing the parsed ranges on success,
/// or an Error on failure
static Expected<RangeList> parseRanges(const StringRef RangeStr,
const char Separator = ',');

/// Legacy interface for backward compatibility.
/// \deprecated Use the Expected<RangeList> version instead
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a brand-new API, why have a deprecated interface?

static bool parseRanges(const StringRef RangeStr, RangeList &Ranges,
const char Separator = ',');

/// Check if a value is contained in any of the ranges
static bool contains(const ArrayRef<Range> Ranges, const int64_t Value);

/// Convert ranges back to string representation for debugging
static std::string rangesToString(const ArrayRef<Range> Ranges,
const char Separator = ',');

/// Print ranges to output stream
static void printRanges(raw_ostream &OS, const ArrayRef<Range> Ranges);

/// Merge adjacent/consecutive ranges into single ranges
/// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10]
static RangeList mergeAdjacentRanges(const ArrayRef<Range> Ranges);
};

} // end namespace llvm

#endif // LLVM_SUPPORT_RANGE_H
63 changes: 56 additions & 7 deletions llvm/lib/IR/OptBisect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
//===----------------------------------------------------------------------===//

#include "llvm/IR/OptBisect.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Range.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstdlib>

using namespace llvm;

Expand All @@ -30,12 +33,55 @@ static OptDisable &getOptDisabler() {
return OptDisabler;
}

static cl::opt<int> OptBisectLimit("opt-bisect-limit", cl::Hidden,
cl::init(OptBisect::Disabled), cl::Optional,
cl::cb<void, int>([](int Limit) {
getOptBisector().setLimit(Limit);
}),
cl::desc("Maximum optimization to perform"));
static cl::opt<int> OptBisectLimit(
"opt-bisect-limit", cl::Hidden, cl::init(-1), cl::Optional,
cl::cb<void, int>([](int Limit) {
if (Limit == -1) {
// -1 means run all passes
getOptBisector().setRanges({{1, std::numeric_limits<int>::max()}});
} else if (Limit == 0) {
// 0 means run no passes
getOptBisector().setRanges({{0, 0}});
} else if (Limit > 0) {
// Convert limit to range 1-Limit
std::string RangeStr = Limit == 1 ? "1" : "1-" + llvm::utostr(Limit);
auto Ranges = RangeUtils::parseRanges(RangeStr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going through a string and then parsing it here is redundant? You can directly create the range.

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(
"Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));

static cl::opt<std::string> OptBisectRanges(
"opt-bisect", cl::Hidden, cl::Optional,
cl::cb<void, const std::string &>([](const std::string &RangeStr) {
if (RangeStr == "-1") {
// -1 means run all passes
getOptBisector().setRanges({{1, std::numeric_limits<int>::max()}});
return;
}

auto Ranges = RangeUtils::parseRanges(RangeStr);
if (!Ranges) {
handleAllErrors(Ranges.takeError(), [&](const StringError &E) {
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, where "
"index 1 is the first pass. Supply '0' to run no passes and -1 to "
"run all passes."));

static cl::opt<bool> OptBisectVerbose(
"opt-bisect-verbose",
Expand Down Expand Up @@ -66,7 +112,10 @@ bool OptBisect::shouldRunPass(StringRef PassName,
assert(isEnabled());

int CurBisectNum = ++LastBisectNum;
bool ShouldRun = (BisectLimit == -1 || CurBisectNum <= BisectLimit);

// 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;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ add_llvm_component_library(LLVMSupport
PluginLoader.cpp
PrettyStackTrace.cpp
RandomNumberGenerator.cpp
Range.cpp
Regex.cpp
RewriteBuffer.cpp
RewriteRope.cpp
Expand Down
Loading