Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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?


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