[OptBisect][ADT] Add support for running ranges of passes and introduce IntegerInclusiveInterval ADT (#152393)
This PR does two things: 1. Introduce `-opt-bisect=<inclusive_integer_interval>` for running ranges of passes, where `inclusive_integer_interval` could be: - `-1` to run all pases - `0` to run no passes - `10-12` to run passes 10 through 12 - `10-12,15-18 `to run passes 10 through 10 and 15 through 18 `-opt-bisect-limit=N` now maps to `-opt-bisect=1-N` 2. Introduces `IntegerInclusiveInterval` in ADT to represent these intervals and the ability to parse string ranges (from the command line). The motivation to move this into ADT is that both `opt-bisect` and `debug-counter` need to parse intervals from the command line. `reduce-chunk-list` also relied on a interval object, so I refactored that to use `IntegerInclusiveInterval` as well.
This commit is contained in:
parent
c371721586
commit
7f9a00e59c
@ -17,7 +17,7 @@
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/StringSet.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include <limits>
|
||||
#include "llvm/Support/IntegerInclusiveInterval.h"
|
||||
|
||||
namespace llvm {
|
||||
|
||||
@ -40,7 +40,7 @@ public:
|
||||
|
||||
/// 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:
|
||||
@ -53,12 +53,12 @@ public:
|
||||
|
||||
~OptBisect() override = default;
|
||||
|
||||
/// Checks the bisect limit to determine if the specified pass should run.
|
||||
/// Checks the bisect intervals 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 intervals are specified or the current pass number
|
||||
/// falls within one of the specified intervals.
|
||||
///
|
||||
/// Most passes should not call this routine directly. Instead, it is called
|
||||
/// through helper routines provided by the base classes of the pass. For
|
||||
@ -67,20 +67,22 @@ public:
|
||||
StringRef IRDescription) const override;
|
||||
|
||||
/// isEnabled() should return true before calling shouldRunPass().
|
||||
bool isEnabled() const override { return BisectLimit != Disabled; }
|
||||
bool isEnabled() const override { return !BisectIntervals.empty(); }
|
||||
|
||||
/// Set the new optimization limit and reset the counter. Passing
|
||||
/// OptBisect::Disabled disables the limiting.
|
||||
void setLimit(int Limit) {
|
||||
BisectLimit = Limit;
|
||||
/// Set intervals directly from an IntervalList.
|
||||
void setIntervals(IntegerInclusiveIntervalUtils::IntervalList Intervals) {
|
||||
BisectIntervals = std::move(Intervals);
|
||||
}
|
||||
|
||||
/// Clear all intervals, effectively disabling bisection.
|
||||
void clearIntervals() {
|
||||
BisectIntervals.clear();
|
||||
LastBisectNum = 0;
|
||||
}
|
||||
|
||||
static constexpr int Disabled = std::numeric_limits<int>::max();
|
||||
|
||||
private:
|
||||
int BisectLimit = Disabled;
|
||||
mutable int LastBisectNum = 0;
|
||||
IntegerInclusiveIntervalUtils::IntervalList BisectIntervals;
|
||||
};
|
||||
|
||||
/// This class implements a mechanism to disable passes and individual
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
/// debug. That is where debug counting steps in. You can instrument the pass
|
||||
/// with a debug counter before it does a certain thing, and depending on the
|
||||
/// counts, it will either execute that thing or not. The debug counter itself
|
||||
/// consists of a list of chunks (inclusive numeric ranges). `shouldExecute`
|
||||
/// consists of a list of chunks (inclusive numeric intervals). `shouldExecute`
|
||||
/// returns true iff the list is empty or the current count is in one of the
|
||||
/// chunks.
|
||||
///
|
||||
@ -48,6 +48,7 @@
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/IntegerInclusiveInterval.h"
|
||||
#include <string>
|
||||
|
||||
namespace llvm {
|
||||
@ -56,13 +57,6 @@ 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; }
|
||||
};
|
||||
|
||||
/// Struct to store counter info.
|
||||
class CounterInfo {
|
||||
friend class DebugCounter;
|
||||
@ -78,7 +72,7 @@ public:
|
||||
uint64_t CurrChunkIdx = 0;
|
||||
StringRef Name;
|
||||
StringRef Desc;
|
||||
SmallVector<Chunk> Chunks;
|
||||
IntegerInclusiveIntervalUtils::IntervalList Chunks;
|
||||
|
||||
public:
|
||||
CounterInfo(StringRef Name, StringRef Desc) : Name(Name), Desc(Desc) {
|
||||
@ -86,11 +80,13 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
LLVM_ABI static void printChunks(raw_ostream &OS, ArrayRef<Chunk>);
|
||||
LLVM_ABI static void
|
||||
printChunks(raw_ostream &OS, ArrayRef<IntegerInclusiveInterval> Intervals);
|
||||
|
||||
/// 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 bool
|
||||
parseChunks(StringRef Str, IntegerInclusiveIntervalUtils::IntervalList &Res);
|
||||
|
||||
/// Returns a reference to the singleton instance.
|
||||
LLVM_ABI static DebugCounter &instance();
|
||||
|
||||
115
llvm/include/llvm/Support/IntegerInclusiveInterval.h
Normal file
115
llvm/include/llvm/Support/IntegerInclusiveInterval.h
Normal file
@ -0,0 +1,115 @@
|
||||
//===- IntegerInclusiveInterval.h -------------------------------*- 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 defines the IntegerInclusiveInterval class and utilities for
|
||||
// handling lists of inclusive integer intervals, such as parsing interval
|
||||
// strings like "1-10,20-30,45", which are used in debugging and bisection
|
||||
// tools.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_SUPPORT_INTEGER_INCLUSIVE_INTERVAL_H
|
||||
#define LLVM_SUPPORT_INTEGER_INCLUSIVE_INTERVAL_H
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
namespace llvm {
|
||||
class raw_ostream;
|
||||
} // end namespace llvm
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Represents an inclusive integer interval [Begin, End] where Begin <= End.
|
||||
class IntegerInclusiveInterval {
|
||||
int64_t Begin;
|
||||
int64_t End;
|
||||
|
||||
public:
|
||||
/// Create an interval [Begin, End].
|
||||
IntegerInclusiveInterval(int64_t Begin, int64_t End)
|
||||
: Begin(Begin), End(End) {
|
||||
assert(Begin <= End && "Interval Begin must be <= End");
|
||||
}
|
||||
/// Create a singleton interval [Single, Single].
|
||||
IntegerInclusiveInterval(int64_t Single) : Begin(Single), End(Single) {}
|
||||
|
||||
int64_t getBegin() const { return Begin; }
|
||||
int64_t getEnd() const { return End; }
|
||||
|
||||
void setBegin(int64_t NewBegin) {
|
||||
assert(NewBegin <= End && "Interval Begin must be <= End");
|
||||
Begin = NewBegin;
|
||||
}
|
||||
void setEnd(int64_t NewEnd) {
|
||||
assert(Begin <= NewEnd && "Interval Begin must be <= End");
|
||||
End = NewEnd;
|
||||
}
|
||||
|
||||
/// Check if the given value is within this interval (inclusive).
|
||||
bool contains(int64_t Value) const { return Value >= Begin && Value <= End; }
|
||||
|
||||
/// Check if this interval overlaps with another interval.
|
||||
bool overlaps(const IntegerInclusiveInterval &Other) const {
|
||||
return Begin <= Other.End && End >= Other.Begin;
|
||||
}
|
||||
|
||||
/// Print the interval to the output stream.
|
||||
void print(raw_ostream &OS) const {
|
||||
if (Begin == End)
|
||||
OS << Begin;
|
||||
else
|
||||
OS << Begin << "-" << End;
|
||||
}
|
||||
|
||||
bool operator==(const IntegerInclusiveInterval &Other) const {
|
||||
return Begin == Other.Begin && End == Other.End;
|
||||
}
|
||||
};
|
||||
|
||||
namespace IntegerInclusiveIntervalUtils {
|
||||
|
||||
/// A list of integer intervals.
|
||||
using IntervalList = SmallVector<IntegerInclusiveInterval, 8>;
|
||||
|
||||
/// Parse a interval specification string like "1-10,20-30,45" or
|
||||
/// "1-10:20-30:45". Intervals must be in increasing order and non-overlapping.
|
||||
/// \param IntervalStr The string to parse.
|
||||
/// \param Separator The separator character to use (',' or ':').
|
||||
/// \returns Expected<IntervalList> containing the parsed intervals on success,
|
||||
/// or an Error on failure.
|
||||
Expected<IntervalList> parseIntervals(StringRef IntervalStr,
|
||||
char Separator = ',');
|
||||
|
||||
/// Check if a value is contained in any of the intervals.
|
||||
bool contains(ArrayRef<IntegerInclusiveInterval> Intervals, int64_t Value);
|
||||
|
||||
/// Print intervals to output stream.
|
||||
/// \param OS The output stream to print to.
|
||||
/// \param Intervals The intervals to print.
|
||||
/// \param Separator The separator character to use between intervals (i.e. ','
|
||||
/// or
|
||||
/// ':').
|
||||
void printIntervals(raw_ostream &OS,
|
||||
ArrayRef<IntegerInclusiveInterval> Intervals,
|
||||
char Separator = ',');
|
||||
|
||||
/// Merge adjacent/consecutive intervals into single intervals.
|
||||
/// Example: [1-3, 4-6, 8-10] -> [1-6, 8-10].
|
||||
IntervalList
|
||||
mergeAdjacentIntervals(ArrayRef<IntegerInclusiveInterval> Intervals);
|
||||
|
||||
} // end namespace IntegerInclusiveIntervalUtils
|
||||
|
||||
} // end namespace llvm
|
||||
|
||||
#endif // LLVM_SUPPORT_INTEGER_INCLUSIVE_INTERVAL_H
|
||||
@ -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/IntegerInclusiveInterval.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
@ -30,12 +33,50 @@ 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().setIntervals({{1, std::numeric_limits<int>::max()}});
|
||||
else if (Limit == 0)
|
||||
// 0 means run no passes.
|
||||
getOptBisector().setIntervals({{0, 0}});
|
||||
else if (Limit > 0)
|
||||
// Convert limit to interval 1-Limit.
|
||||
getOptBisector().setIntervals({{1, Limit}});
|
||||
else
|
||||
llvm_unreachable(
|
||||
("Invalid limit for -opt-bisect-limit: " + llvm::utostr(Limit))
|
||||
.c_str());
|
||||
}),
|
||||
cl::desc(
|
||||
"Maximum optimization to perform (equivalent to -opt-bisect=1-N)"));
|
||||
|
||||
static cl::opt<std::string> OptBisectIntervals(
|
||||
"opt-bisect", cl::Hidden, cl::Optional,
|
||||
cl::cb<void, const std::string &>([](const std::string &IntervalStr) {
|
||||
if (IntervalStr == "-1") {
|
||||
// -1 means run all passes.
|
||||
getOptBisector().setIntervals({{1, std::numeric_limits<int>::max()}});
|
||||
return;
|
||||
}
|
||||
|
||||
auto Intervals =
|
||||
IntegerInclusiveIntervalUtils::parseIntervals(IntervalStr);
|
||||
if (!Intervals) {
|
||||
handleAllErrors(Intervals.takeError(), [&](const StringError &E) {
|
||||
errs() << "Error: Invalid interval specification for -opt-bisect: "
|
||||
<< IntervalStr << " (" << E.getMessage() << ")\n";
|
||||
});
|
||||
exit(1);
|
||||
}
|
||||
getOptBisector().setIntervals(std::move(*Intervals));
|
||||
}),
|
||||
cl::desc("Run optimization passes only for the specified intervals. "
|
||||
"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",
|
||||
@ -66,7 +107,11 @@ 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 intervals.
|
||||
bool ShouldRun =
|
||||
IntegerInclusiveIntervalUtils::contains(BisectIntervals, CurBisectNum);
|
||||
|
||||
if (OptBisectVerbose)
|
||||
printPassMessage(PassName, CurBisectNum, IRDescription, ShouldRun);
|
||||
return ShouldRun;
|
||||
|
||||
@ -236,6 +236,7 @@ add_llvm_component_library(LLVMSupport
|
||||
PluginLoader.cpp
|
||||
PrettyStackTrace.cpp
|
||||
RandomNumberGenerator.cpp
|
||||
IntegerInclusiveInterval.cpp
|
||||
Regex.cpp
|
||||
RewriteBuffer.cpp
|
||||
RewriteRope.cpp
|
||||
|
||||
@ -10,77 +10,9 @@ using namespace llvm;
|
||||
|
||||
namespace llvm {
|
||||
|
||||
void DebugCounter::Chunk::print(llvm::raw_ostream &OS) {
|
||||
if (Begin == End)
|
||||
OS << Begin;
|
||||
else
|
||||
OS << Begin << "-" << End;
|
||||
}
|
||||
|
||||
void DebugCounter::printChunks(raw_ostream &OS, ArrayRef<Chunk> Chunks) {
|
||||
if (Chunks.empty()) {
|
||||
OS << "empty";
|
||||
} else {
|
||||
bool IsFirst = true;
|
||||
for (auto E : Chunks) {
|
||||
if (!IsFirst)
|
||||
OS << ':';
|
||||
else
|
||||
IsFirst = false;
|
||||
E.print(OS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DebugCounter::parseChunks(StringRef Str, SmallVector<Chunk> &Chunks) {
|
||||
StringRef Remaining = Str;
|
||||
|
||||
auto ConsumeInt = [&]() -> int64_t {
|
||||
StringRef Number =
|
||||
Remaining.take_until([](char c) { return c < '0' || c > '9'; });
|
||||
int64_t Res;
|
||||
if (Number.getAsInteger(10, Res)) {
|
||||
errs() << "Failed to parse int at : " << Remaining << "\n";
|
||||
return -1;
|
||||
}
|
||||
Remaining = Remaining.drop_front(Number.size());
|
||||
return Res;
|
||||
};
|
||||
|
||||
while (1) {
|
||||
int64_t Num = ConsumeInt();
|
||||
if (Num == -1)
|
||||
return true;
|
||||
if (!Chunks.empty() && Num <= Chunks[Chunks.size() - 1].End) {
|
||||
errs() << "Expected Chunks to be in increasing order " << Num
|
||||
<< " <= " << Chunks[Chunks.size() - 1].End << "\n";
|
||||
return true;
|
||||
}
|
||||
if (Remaining.starts_with("-")) {
|
||||
Remaining = Remaining.drop_front();
|
||||
int64_t Num2 = ConsumeInt();
|
||||
if (Num2 == -1)
|
||||
return true;
|
||||
if (Num >= Num2) {
|
||||
errs() << "Expected " << Num << " < " << Num2 << " in " << Num << "-"
|
||||
<< Num2 << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
Chunks.push_back({Num, Num2});
|
||||
} else {
|
||||
Chunks.push_back({Num, Num});
|
||||
}
|
||||
if (Remaining.starts_with(":")) {
|
||||
Remaining = Remaining.drop_front();
|
||||
continue;
|
||||
}
|
||||
if (Remaining.empty())
|
||||
break;
|
||||
errs() << "Failed to parse at : " << Remaining;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
void DebugCounter::printChunks(raw_ostream &OS,
|
||||
ArrayRef<IntegerInclusiveInterval> Chunks) {
|
||||
IntegerInclusiveIntervalUtils::printIntervals(OS, Chunks, ':');
|
||||
}
|
||||
|
||||
} // namespace llvm
|
||||
@ -190,14 +122,9 @@ 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;
|
||||
SmallVector<Chunk> Chunks;
|
||||
|
||||
if (parseChunks(CounterPair.second, Chunks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CounterInfo *Counter = getCounterInfo(CounterName);
|
||||
if (!Counter) {
|
||||
@ -206,8 +133,16 @@ void DebugCounter::push_back(const std::string &Val) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto ExpectedChunks =
|
||||
IntegerInclusiveIntervalUtils::parseIntervals(CounterPair.second, ':');
|
||||
if (!ExpectedChunks) {
|
||||
handleAllErrors(ExpectedChunks.takeError(), [&](const StringError &E) {
|
||||
errs() << "DebugCounter Error: " << E.getMessage() << "\n";
|
||||
});
|
||||
exit(1);
|
||||
}
|
||||
Counter->Chunks = std::move(*ExpectedChunks);
|
||||
Counter->Active = Counter->IsSet = true;
|
||||
Counter->Chunks = std::move(Chunks);
|
||||
}
|
||||
|
||||
void DebugCounter::print(raw_ostream &OS) const {
|
||||
@ -234,15 +169,15 @@ bool DebugCounter::handleCounterIncrement(CounterInfo &Info) {
|
||||
|
||||
bool Res = Info.Chunks[CurrIdx].contains(CurrCount);
|
||||
if (BreakOnLast && CurrIdx == (Info.Chunks.size() - 1) &&
|
||||
CurrCount == Info.Chunks[CurrIdx].End) {
|
||||
CurrCount == Info.Chunks[CurrIdx].getEnd()) {
|
||||
LLVM_BUILTIN_DEBUGTRAP;
|
||||
}
|
||||
if (CurrCount > Info.Chunks[CurrIdx].End) {
|
||||
if (CurrCount > Info.Chunks[CurrIdx].getEnd()) {
|
||||
Info.CurrChunkIdx++;
|
||||
|
||||
/// Handle consecutive blocks.
|
||||
if (Info.CurrChunkIdx < Info.Chunks.size() &&
|
||||
CurrCount == Info.Chunks[Info.CurrChunkIdx].Begin)
|
||||
CurrCount == Info.Chunks[Info.CurrChunkIdx].getBegin())
|
||||
return true;
|
||||
}
|
||||
return Res;
|
||||
|
||||
125
llvm/lib/Support/IntegerInclusiveInterval.cpp
Normal file
125
llvm/lib/Support/IntegerInclusiveInterval.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
//===- IntegerInclusiveInterval.cpp -----------------------------*- 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 implements utilities for handling lists of inclusive integer
|
||||
// intervals, such as parsing interval strings like "1-10,20-30,45", which are
|
||||
// used in debugging and bisection tools.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Support/IntegerInclusiveInterval.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/Regex.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
namespace llvm::IntegerInclusiveIntervalUtils {
|
||||
|
||||
Expected<IntervalList> parseIntervals(StringRef Str, char Separator) {
|
||||
IntervalList Intervals;
|
||||
|
||||
if (Str.empty())
|
||||
return std::move(Intervals);
|
||||
|
||||
// Regex to match either single number or interval "num1-num2".
|
||||
const Regex IntervalRegex("^([0-9]+)(-([0-9]+))?$");
|
||||
|
||||
for (StringRef Part : llvm::split(Str, Separator)) {
|
||||
Part = Part.trim();
|
||||
if (Part.empty())
|
||||
continue;
|
||||
|
||||
SmallVector<StringRef, 4> Matches;
|
||||
if (!IntervalRegex.match(Part, &Matches))
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"Invalid interval 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());
|
||||
|
||||
if (!Matches[3].empty()) {
|
||||
// Interval 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());
|
||||
if (Begin >= End)
|
||||
return createStringError(std::errc::invalid_argument,
|
||||
"Invalid interval: %lld >= %lld", Begin, End);
|
||||
} else
|
||||
// Single number.
|
||||
End = Begin;
|
||||
|
||||
// Check ordering constraint (intervals must be in increasing order).
|
||||
if (!Intervals.empty() && Begin <= Intervals.back().getEnd())
|
||||
return createStringError(
|
||||
std::errc::invalid_argument,
|
||||
"Expected intervals to be in increasing order: %lld <= %lld", Begin,
|
||||
Intervals.back().getEnd());
|
||||
|
||||
Intervals.push_back(IntegerInclusiveInterval(Begin, End));
|
||||
}
|
||||
|
||||
return Intervals;
|
||||
}
|
||||
|
||||
bool contains(ArrayRef<IntegerInclusiveInterval> Intervals, int64_t Value) {
|
||||
for (const IntegerInclusiveInterval &It : Intervals) {
|
||||
if (It.contains(Value))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void printIntervals(raw_ostream &OS,
|
||||
ArrayRef<IntegerInclusiveInterval> Intervals,
|
||||
char Separator) {
|
||||
if (Intervals.empty()) {
|
||||
OS << "empty";
|
||||
return;
|
||||
}
|
||||
|
||||
std::string Sep(1, Separator);
|
||||
ListSeparator LS(Sep);
|
||||
for (const IntegerInclusiveInterval &It : Intervals) {
|
||||
OS << LS;
|
||||
It.print(OS);
|
||||
}
|
||||
}
|
||||
|
||||
IntervalList
|
||||
mergeAdjacentIntervals(ArrayRef<IntegerInclusiveInterval> Intervals) {
|
||||
if (Intervals.empty())
|
||||
return {};
|
||||
|
||||
IntervalList Result;
|
||||
Result.push_back(Intervals[0]);
|
||||
|
||||
for (const IntegerInclusiveInterval &Current : Intervals.drop_front()) {
|
||||
IntegerInclusiveInterval &Last = Result.back();
|
||||
// Check if current interval is adjacent to the last merged interval.
|
||||
if (Current.getBegin() == Last.getEnd() + 1) {
|
||||
// Merge by extending the end of the last interval.
|
||||
Last.setEnd(Current.getEnd());
|
||||
} else {
|
||||
// Not adjacent, add as separate interval.
|
||||
Result.push_back(Current);
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
} // end namespace llvm::IntegerInclusiveIntervalUtils
|
||||
116
llvm/test/Other/debugcounter-multi-intervals.ll
Normal file
116
llvm/test/Other/debugcounter-multi-intervals.ll
Normal file
@ -0,0 +1,116 @@
|
||||
; REQUIRES: asserts
|
||||
|
||||
; Test debug counter with multiple intervals
|
||||
; 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
|
||||
; 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
|
||||
; 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 interval specifications. Also check that we catch errors during parsing.
|
||||
|
||||
; 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 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
|
||||
; 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 intervals: 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 interval: 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 interval: 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 - test comprehensive error handling
|
||||
; CHECK-ERROR-INVALID: DebugCounter Error: Invalid interval format: 'invalid'
|
||||
; CHECK-ERROR-BACKWARDS: DebugCounter Error: Invalid interval: 5 >= 2
|
||||
; CHECK-ERROR-UNORDERED: DebugCounter Error: Expected intervals to be in increasing order: 2 <= 3
|
||||
; CHECK-ERROR-NON-NUMERIC: DebugCounter Error: Invalid interval format: 'abc-def'
|
||||
; CHECK-ERROR-MIXED: DebugCounter Error: Invalid interval format: '1-abc'
|
||||
; CHECK-ERROR-COMPLEX-UNORDERED: DebugCounter Error: Expected intervals to be in increasing order: 2 <= 3
|
||||
; CHECK-ERROR-DOUBLE-DASH: DebugCounter Error: Invalid interval format: '1--5'
|
||||
; CHECK-ERROR-NEGATIVE: DebugCounter Error: Invalid interval format: '-5'
|
||||
; CHECK-ERROR-EMPTY: DebugCounter Error: dce-transform= does not have an = in it
|
||||
; CHECK-ERROR-DUPLICATE: DebugCounter Error: Expected intervals to be in increasing order: 1 <= 1
|
||||
58
llvm/test/Other/opt-bisect-ranges.ll
Normal file
58
llvm/test/Other/opt-bisect-ranges.ll
Normal file
@ -0,0 +1,58 @@
|
||||
; Test that verifies functionality for -opt-bisect with interval specifications
|
||||
|
||||
; Test basic interval functionality: run passes 1-3 and 7-8
|
||||
; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
|
||||
; RUN: -opt-bisect=1-3,7-8 %s 2>&1 | FileCheck %s --check-prefix=CHECK-INTERVALS
|
||||
; CHECK-INTERVALS: BISECT: running pass (1) annotation2metadata on [module]
|
||||
; CHECK-INTERVALS: BISECT: running pass (2) forceattrs on [module]
|
||||
; CHECK-INTERVALS: BISECT: running pass (3) inferattrs on [module]
|
||||
; CHECK-INTERVALS: BISECT: NOT running pass (4) lower-expect on foo
|
||||
; CHECK-INTERVALS: BISECT: NOT running pass (5) simplifycfg on foo
|
||||
; CHECK-INTERVALS: BISECT: NOT running pass (6) sroa on foo
|
||||
; CHECK-INTERVALS: BISECT: running pass (7) early-cse on foo
|
||||
; CHECK-INTERVALS: BISECT: running pass (8) openmp-opt on [module]
|
||||
|
||||
; Test single pass selection: run only pass 5
|
||||
; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
|
||||
; RUN: -opt-bisect=5 %s 2>&1 | FileCheck %s --check-prefix=CHECK-SINGLE-INTERVAL
|
||||
; CHECK-SINGLE-INTERVAL: BISECT: NOT running pass (1) annotation2metadata on [module]
|
||||
; CHECK-SINGLE-INTERVAL: BISECT: NOT running pass (2) forceattrs on [module]
|
||||
; CHECK-SINGLE-INTERVAL: BISECT: NOT running pass (3) inferattrs on [module]
|
||||
; CHECK-SINGLE-INTERVAL: BISECT: NOT running pass (4) lower-expect on foo
|
||||
; CHECK-SINGLE-INTERVAL: BISECT: running pass (5) simplifycfg on foo
|
||||
; CHECK-SINGLE-INTERVAL: BISECT: NOT running pass (6) sroa on foo
|
||||
|
||||
; Test running no passes
|
||||
; RUN: opt -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
|
||||
; RUN: -opt-bisect=0 %s 2>&1 | FileCheck %s --check-prefix=CHECK-NONE
|
||||
; CHECK-NONE: BISECT: NOT running pass (1) annotation2metadata on [module]
|
||||
; CHECK-NONE: BISECT: NOT running pass (2) forceattrs on [module]
|
||||
; CHECK-NONE: BISECT: NOT running pass (3) inferattrs on [module]
|
||||
; 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 -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
|
||||
; RUN: -opt-bisect=-1 %s 2>&1 | FileCheck %s --check-prefix=CHECK-ALL
|
||||
; CHECK-ALL: BISECT: running pass (1) annotation2metadata on [module]
|
||||
; CHECK-ALL: BISECT: running pass (2) forceattrs on [module]
|
||||
; CHECK-ALL: BISECT: running pass (3) inferattrs on [module]
|
||||
; 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 -passes='annotation2metadata,forceattrs,inferattrs,function(lower-expect),function(simplifycfg),function(sroa),function(early-cse),openmp-opt' \
|
||||
; RUN: -opt-bisect-limit=3 %s 2>&1 | FileCheck %s --check-prefix=CHECK-LIMIT
|
||||
; CHECK-LIMIT: BISECT: running pass (1) annotation2metadata on [module]
|
||||
; CHECK-LIMIT: BISECT: running pass (2) forceattrs on [module]
|
||||
; CHECK-LIMIT: BISECT: running pass (3) inferattrs on [module]
|
||||
; 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
|
||||
}
|
||||
|
||||
|
||||
@ -11,9 +11,8 @@
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/ADT/DenseSet.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "llvm/Support/DebugCounter.h"
|
||||
#include "llvm/Support/IntegerInclusiveInterval.h"
|
||||
#include "llvm/Support/Program.h"
|
||||
|
||||
using namespace llvm;
|
||||
@ -24,29 +23,16 @@ static cl::opt<std::string> StartChunks(cl::Positional, cl::Required);
|
||||
|
||||
static cl::opt<bool> Pessimist("pessimist", cl::init(false));
|
||||
|
||||
using Chunk = DebugCounter::Chunk;
|
||||
|
||||
namespace {
|
||||
|
||||
SmallVector<Chunk> simplifyChunksList(ArrayRef<Chunk> Chunks) {
|
||||
SmallVector<Chunk> Res;
|
||||
Res.push_back(Chunks.front());
|
||||
for (unsigned Idx = 1; Idx < Chunks.size(); Idx++) {
|
||||
if (Chunks[Idx].Begin == Res.back().End + 1)
|
||||
Res.back().End = Chunks[Idx].End;
|
||||
else
|
||||
Res.push_back(Chunks[Idx]);
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
bool isStillInteresting(ArrayRef<Chunk> Chunks) {
|
||||
SmallVector<Chunk> SimpleChunks = simplifyChunksList(Chunks);
|
||||
bool isStillInteresting(ArrayRef<IntegerInclusiveInterval> Chunks) {
|
||||
IntegerInclusiveIntervalUtils::IntervalList SimpleChunks =
|
||||
IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Chunks);
|
||||
|
||||
std::string ChunkStr;
|
||||
{
|
||||
raw_string_ostream OS(ChunkStr);
|
||||
DebugCounter::printChunks(OS, SimpleChunks);
|
||||
IntegerInclusiveIntervalUtils::printIntervals(OS, SimpleChunks);
|
||||
}
|
||||
|
||||
errs() << "Checking with: " << ChunkStr << "\n";
|
||||
@ -73,18 +59,18 @@ bool isStillInteresting(ArrayRef<Chunk> Chunks) {
|
||||
return Res;
|
||||
}
|
||||
|
||||
bool increaseGranularity(SmallVector<Chunk> &Chunks) {
|
||||
bool increaseGranularity(IntegerInclusiveIntervalUtils::IntervalList &Chunks) {
|
||||
errs() << "Increasing granularity\n";
|
||||
SmallVector<Chunk> NewChunks;
|
||||
IntegerInclusiveIntervalUtils::IntervalList NewChunks;
|
||||
bool SplitOne = false;
|
||||
|
||||
for (auto &C : Chunks) {
|
||||
if (C.Begin == C.End) {
|
||||
if (C.getBegin() == C.getEnd()) {
|
||||
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.getBegin() + C.getEnd()) / 2;
|
||||
NewChunks.push_back(IntegerInclusiveInterval(C.getBegin(), Half));
|
||||
NewChunks.push_back(IntegerInclusiveInterval(Half + 1, C.getEnd()));
|
||||
SplitOne = true;
|
||||
}
|
||||
}
|
||||
@ -99,10 +85,16 @@ bool increaseGranularity(SmallVector<Chunk> &Chunks) {
|
||||
int main(int argc, char **argv) {
|
||||
cl::ParseCommandLineOptions(argc, argv);
|
||||
|
||||
SmallVector<Chunk> CurrChunks;
|
||||
if (DebugCounter::parseChunks(StartChunks, CurrChunks)) {
|
||||
auto ExpectedChunks =
|
||||
IntegerInclusiveIntervalUtils::parseIntervals(StartChunks, ',');
|
||||
if (!ExpectedChunks) {
|
||||
handleAllErrors(ExpectedChunks.takeError(), [](const StringError &E) {
|
||||
errs() << "Error parsing chunks: " << E.getMessage() << "\n";
|
||||
});
|
||||
return 1;
|
||||
}
|
||||
IntegerInclusiveIntervalUtils::IntervalList CurrChunks =
|
||||
std::move(*ExpectedChunks);
|
||||
|
||||
auto Program = sys::findProgramByName(ReproductionCmd);
|
||||
if (!Program) {
|
||||
@ -126,7 +118,7 @@ int main(int argc, char **argv) {
|
||||
if (CurrChunks.size() == 1)
|
||||
break;
|
||||
|
||||
Chunk Testing = CurrChunks[Idx];
|
||||
IntegerInclusiveInterval Testing = CurrChunks[Idx];
|
||||
errs() << "Trying to remove : ";
|
||||
Testing.print(errs());
|
||||
errs() << "\n";
|
||||
@ -142,6 +134,8 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
|
||||
errs() << "Minimal Chunks = ";
|
||||
DebugCounter::printChunks(llvm::errs(), simplifyChunksList(CurrChunks));
|
||||
IntegerInclusiveIntervalUtils::printIntervals(
|
||||
llvm::errs(),
|
||||
IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(CurrChunks));
|
||||
errs() << "\n";
|
||||
}
|
||||
|
||||
@ -76,6 +76,7 @@ add_llvm_unittest(SupportTests
|
||||
ProcessTest.cpp
|
||||
ProgramTest.cpp
|
||||
ProgramStackTest.cpp
|
||||
IntegerInclusiveIntervalTest.cpp
|
||||
RecyclerTest.cpp
|
||||
RegexTest.cpp
|
||||
ReverseIterationTest.cpp
|
||||
|
||||
245
llvm/unittests/Support/IntegerInclusiveIntervalTest.cpp
Normal file
245
llvm/unittests/Support/IntegerInclusiveIntervalTest.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
//===- llvm/unittest/Support/IntegerInclusiveIntervalTest.cpp -------------===//
|
||||
//
|
||||
// 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/IntegerInclusiveInterval.h"
|
||||
#include "llvm/Testing/Support/Error.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
TEST(IntegerInclusiveIntervalTest, BasicInterval) {
|
||||
IntegerInclusiveInterval I(5, 10);
|
||||
EXPECT_EQ(I.getBegin(), 5);
|
||||
EXPECT_EQ(I.getEnd(), 10);
|
||||
EXPECT_TRUE(I.contains(5));
|
||||
EXPECT_TRUE(I.contains(7));
|
||||
EXPECT_TRUE(I.contains(10));
|
||||
EXPECT_FALSE(I.contains(4));
|
||||
EXPECT_FALSE(I.contains(11));
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalTest, SingleValueInterval) {
|
||||
IntegerInclusiveInterval I(42);
|
||||
EXPECT_EQ(I.getBegin(), 42);
|
||||
EXPECT_EQ(I.getEnd(), 42);
|
||||
EXPECT_TRUE(I.contains(42));
|
||||
EXPECT_FALSE(I.contains(41));
|
||||
EXPECT_FALSE(I.contains(43));
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalTest, IntervalOverlaps) {
|
||||
IntegerInclusiveInterval I1(1, 5);
|
||||
IntegerInclusiveInterval I2(3, 8);
|
||||
IntegerInclusiveInterval I3(6, 10);
|
||||
IntegerInclusiveInterval I4(11, 15);
|
||||
|
||||
EXPECT_TRUE(I1.overlaps(I2));
|
||||
EXPECT_TRUE(I2.overlaps(I1));
|
||||
EXPECT_TRUE(I2.overlaps(I3));
|
||||
EXPECT_FALSE(I1.overlaps(I3));
|
||||
EXPECT_FALSE(I1.overlaps(I4));
|
||||
EXPECT_FALSE(I3.overlaps(I4));
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, ParseSingleNumber) {
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("42");
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
EXPECT_EQ(Intervals.size(), 1U);
|
||||
EXPECT_EQ(Intervals[0].getBegin(), 42);
|
||||
EXPECT_EQ(Intervals[0].getEnd(), 42);
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, ParseSingleInterval) {
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("10-20");
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
EXPECT_EQ(Intervals.size(), 1U);
|
||||
EXPECT_EQ(Intervals[0].getBegin(), 10);
|
||||
EXPECT_EQ(Intervals[0].getEnd(), 20);
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, ParseMultipleIntervals) {
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("1-5,10,15-20");
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
EXPECT_EQ(Intervals.size(), 3U);
|
||||
|
||||
// Intervals are in input order (DebugCounter style).
|
||||
EXPECT_EQ(Intervals[0].getBegin(), 1);
|
||||
EXPECT_EQ(Intervals[0].getEnd(), 5);
|
||||
EXPECT_EQ(Intervals[1].getBegin(), 10);
|
||||
EXPECT_EQ(Intervals[1].getEnd(), 10);
|
||||
EXPECT_EQ(Intervals[2].getBegin(), 15);
|
||||
EXPECT_EQ(Intervals[2].getEnd(), 20);
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, ParseColonSeparatedIntervals) {
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("1-5:10:15-20", ':');
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
EXPECT_EQ(Intervals.size(), 3U);
|
||||
EXPECT_EQ(Intervals[0].getBegin(), 1);
|
||||
EXPECT_EQ(Intervals[0].getEnd(), 5);
|
||||
EXPECT_EQ(Intervals[1].getBegin(), 10);
|
||||
EXPECT_EQ(Intervals[1].getEnd(), 10);
|
||||
EXPECT_EQ(Intervals[2].getBegin(), 15);
|
||||
EXPECT_EQ(Intervals[2].getEnd(), 20);
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, ParseEmptyString) {
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("");
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
EXPECT_TRUE(Intervals.empty());
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, ParseInvalidIntervals) {
|
||||
// Invalid number.
|
||||
auto ER1 = IntegerInclusiveIntervalUtils::parseIntervals("abc");
|
||||
EXPECT_THAT_EXPECTED(ER1, Failed());
|
||||
consumeError(ER1.takeError());
|
||||
|
||||
// Invalid interval (begin > end).
|
||||
auto ER2 = IntegerInclusiveIntervalUtils::parseIntervals("10-5");
|
||||
EXPECT_THAT_EXPECTED(ER2, Failed());
|
||||
consumeError(ER2.takeError());
|
||||
|
||||
// Out of order intervals (DebugCounter constraint and overlap).
|
||||
auto ER3 = IntegerInclusiveIntervalUtils::parseIntervals("10,5");
|
||||
EXPECT_THAT_EXPECTED(ER3, Failed());
|
||||
consumeError(ER3.takeError());
|
||||
|
||||
auto ER4 = IntegerInclusiveIntervalUtils::parseIntervals("1-5,3-7");
|
||||
EXPECT_THAT_EXPECTED(ER4, Failed());
|
||||
consumeError(ER4.takeError());
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, Contains) {
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("1-5,10,15-20");
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 1));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 3));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 5));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 10));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 15));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 18));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(Intervals, 20));
|
||||
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(Intervals, 6));
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(Intervals, 9));
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(Intervals, 11));
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(Intervals, 14));
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(Intervals, 21));
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, SeparatorParameter) {
|
||||
IntegerInclusiveIntervalUtils::IntervalList ColonIntervals, CommaIntervals;
|
||||
|
||||
// Test explicit separator parameters.
|
||||
auto ERC = IntegerInclusiveIntervalUtils::parseIntervals("1-5:10:15-20", ':');
|
||||
ASSERT_THAT_EXPECTED(ERC, Succeeded());
|
||||
ColonIntervals = std::move(*ERC);
|
||||
|
||||
auto ERM = IntegerInclusiveIntervalUtils::parseIntervals("1-5,10,15-20", ',');
|
||||
ASSERT_THAT_EXPECTED(ERM, Succeeded());
|
||||
CommaIntervals = std::move(*ERM);
|
||||
|
||||
EXPECT_EQ(ColonIntervals.size(), CommaIntervals.size());
|
||||
for (size_t I = 0; I < ColonIntervals.size(); ++I) {
|
||||
EXPECT_EQ(ColonIntervals[I].getBegin(), CommaIntervals[I].getBegin());
|
||||
EXPECT_EQ(ColonIntervals[I].getEnd(), CommaIntervals[I].getEnd());
|
||||
}
|
||||
|
||||
// Test that both work with contains().
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(ColonIntervals, 3));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(CommaIntervals, 3));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(ColonIntervals, 10));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(CommaIntervals, 10));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(ColonIntervals, 18));
|
||||
EXPECT_TRUE(IntegerInclusiveIntervalUtils::contains(CommaIntervals, 18));
|
||||
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(ColonIntervals, 8));
|
||||
EXPECT_FALSE(IntegerInclusiveIntervalUtils::contains(CommaIntervals, 8));
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalUtilsTest, DefaultCommaSeparator) {
|
||||
// Test that comma is the default separator.
|
||||
auto ER = IntegerInclusiveIntervalUtils::parseIntervals("1-5,10,15-20");
|
||||
ASSERT_THAT_EXPECTED(ER, Succeeded());
|
||||
auto Intervals = std::move(*ER);
|
||||
EXPECT_EQ(Intervals.size(), 3U);
|
||||
EXPECT_EQ(Intervals[0].getBegin(), 1);
|
||||
EXPECT_EQ(Intervals[0].getEnd(), 5);
|
||||
EXPECT_EQ(Intervals[1].getBegin(), 10);
|
||||
EXPECT_EQ(Intervals[1].getEnd(), 10);
|
||||
EXPECT_EQ(Intervals[2].getBegin(), 15);
|
||||
EXPECT_EQ(Intervals[2].getEnd(), 20);
|
||||
}
|
||||
|
||||
TEST(IntegerInclusiveIntervalTest, MergeAdjacentIntervals) {
|
||||
IntegerInclusiveIntervalUtils::IntervalList Input, Expected, Result;
|
||||
|
||||
// Empty input
|
||||
Result = IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Input);
|
||||
EXPECT_TRUE(Result.empty());
|
||||
|
||||
// Single interval - no change.
|
||||
Input.push_back(IntegerInclusiveInterval(5, 10));
|
||||
Expected.push_back(IntegerInclusiveInterval(5, 10));
|
||||
Result = IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Input);
|
||||
EXPECT_EQ(Expected, Result);
|
||||
|
||||
// Adjacent intervals should merge.
|
||||
Input.clear();
|
||||
Expected.clear();
|
||||
Input.push_back(IntegerInclusiveInterval(1, 3));
|
||||
Input.push_back(IntegerInclusiveInterval(4, 6));
|
||||
Input.push_back(IntegerInclusiveInterval(7, 9));
|
||||
Expected.push_back(IntegerInclusiveInterval(1, 9));
|
||||
Result = IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Input);
|
||||
EXPECT_EQ(Expected, Result);
|
||||
|
||||
// Non-adjacent intervals should not merge.
|
||||
Input.clear();
|
||||
Expected.clear();
|
||||
Input.push_back(IntegerInclusiveInterval(1, 3));
|
||||
Input.push_back(IntegerInclusiveInterval(5, 7)); // Gap between 3 and 5.
|
||||
Input.push_back(IntegerInclusiveInterval(10, 12)); // Gap between 7 and 10.
|
||||
Expected.push_back(IntegerInclusiveInterval(1, 3));
|
||||
Expected.push_back(IntegerInclusiveInterval(5, 7));
|
||||
Expected.push_back(IntegerInclusiveInterval(10, 12));
|
||||
Result = IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Input);
|
||||
EXPECT_EQ(Expected, Result);
|
||||
|
||||
// Mixed adjacent and non-adjacent intervals.
|
||||
Input.clear();
|
||||
Expected.clear();
|
||||
Input.push_back(IntegerInclusiveInterval(1, 3));
|
||||
Input.push_back(IntegerInclusiveInterval(4, 6)); // Adjacent to first.
|
||||
Input.push_back(IntegerInclusiveInterval(8, 10)); // Gap.
|
||||
Input.push_back(IntegerInclusiveInterval(11, 13)); // Adjacent to third.
|
||||
Input.push_back(IntegerInclusiveInterval(14, 16)); // Adjacent to fourth.
|
||||
Expected.push_back(IntegerInclusiveInterval(1, 6)); // Merged 1-3 and 4-6.
|
||||
Expected.push_back(
|
||||
IntegerInclusiveInterval(8, 16)); // Merged 8-10, 11-13, 14-16.
|
||||
Result = IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Input);
|
||||
EXPECT_EQ(Expected, Result);
|
||||
|
||||
// Single numbers that are adjacent.
|
||||
Input.clear();
|
||||
Expected.clear();
|
||||
Input.push_back(IntegerInclusiveInterval(5));
|
||||
Input.push_back(IntegerInclusiveInterval(6));
|
||||
Input.push_back(IntegerInclusiveInterval(7));
|
||||
Expected.push_back(IntegerInclusiveInterval(5, 7));
|
||||
Result = IntegerInclusiveIntervalUtils::mergeAdjacentIntervals(Input);
|
||||
EXPECT_EQ(Expected, Result);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user