mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 02:33:06 +01:00
[LiveDebugValues] Speed up removeEntryValue, NFC
Summary: Instead of iterating over all VarLoc IDs in removeEntryValue(), just iterate over the interval reserved for entry value VarLocs. This changes the iteration order, hence the test update -- otherwise this is NFC. This appears to give an ~8.5x wall time speed-up for LiveDebugValues when compiling sqlite3.c 3.30.1 with a Release clang (on my machine): ``` ---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name --- Before: 2.5402 ( 18.8%) 0.0050 ( 0.4%) 2.5452 ( 17.3%) 2.5452 ( 17.3%) Live DEBUG_VALUE analysis After: 0.2364 ( 2.1%) 0.0034 ( 0.3%) 0.2399 ( 2.0%) 0.2398 ( 2.0%) Live DEBUG_VALUE analysis ``` The change in removeEntryValue() is the only one that appears to affect wall time, but for consistency (and to resolve a pending TODO), I made the analogous changes for iterating over SpillLocKind VarLocs. Reviewers: nikic, aprantl, jmorse, djtodoro Subscribers: hiraditya, dexonsmith, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D80684
This commit is contained in:
parent
0ba1ec26dd
commit
6f84fd7763
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "llvm/ADT/IntervalMap.h"
|
#include "llvm/ADT/IntervalMap.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
|
#include "llvm/ADT/iterator_range.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
#include "llvm/Support/raw_ostream.h"
|
||||||
|
|
||||||
@ -352,6 +353,19 @@ public:
|
|||||||
return It;
|
return It;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a range iterator which iterates over all of the set bits in the
|
||||||
|
/// half-open range [Start, End).
|
||||||
|
iterator_range<const_iterator> half_open_range(IndexT Start,
|
||||||
|
IndexT End) const {
|
||||||
|
assert(Start < End && "Not a valid range");
|
||||||
|
auto StartIt = find(Start);
|
||||||
|
if (StartIt == end() || *StartIt >= End)
|
||||||
|
return {end(), end()};
|
||||||
|
auto EndIt = StartIt;
|
||||||
|
EndIt.advanceToLowerBound(End);
|
||||||
|
return {StartIt, EndIt};
|
||||||
|
}
|
||||||
|
|
||||||
void print(raw_ostream &OS) const {
|
void print(raw_ostream &OS) const {
|
||||||
OS << "{";
|
OS << "{";
|
||||||
for (auto It = Intervals.begin(), End = Intervals.end(); It != End;
|
for (auto It = Intervals.begin(), End = Intervals.end(); It != End;
|
||||||
|
@ -137,16 +137,24 @@ using VarLocSet = CoalescingBitVector<uint64_t>;
|
|||||||
/// Why encode a location /into/ the VarLocMap index? This makes it possible
|
/// Why encode a location /into/ the VarLocMap index? This makes it possible
|
||||||
/// to find the open VarLocs killed by a register def very quickly. This is a
|
/// to find the open VarLocs killed by a register def very quickly. This is a
|
||||||
/// performance-critical operation for LiveDebugValues.
|
/// performance-critical operation for LiveDebugValues.
|
||||||
///
|
|
||||||
/// TODO: Consider adding reserved intervals for kinds of VarLocs other than
|
|
||||||
/// RegisterKind, like SpillLocKind or EntryValueKind, to optimize iteration
|
|
||||||
/// over open locations.
|
|
||||||
struct LocIndex {
|
struct LocIndex {
|
||||||
uint32_t Location; // Physical registers live in the range [1;2^30) (see
|
uint32_t Location; // Physical registers live in the range [1;2^30) (see
|
||||||
// \ref MCRegister), so we have plenty of range left here
|
// \ref MCRegister), so we have plenty of range left here
|
||||||
// to encode non-register locations.
|
// to encode non-register locations.
|
||||||
uint32_t Index;
|
uint32_t Index;
|
||||||
|
|
||||||
|
/// The first location greater than 0 that is not reserved for VarLocs of
|
||||||
|
/// kind RegisterKind.
|
||||||
|
static constexpr uint32_t kFirstInvalidRegLocation = 1 << 30;
|
||||||
|
|
||||||
|
/// A special location reserved for VarLocs of kind SpillLocKind.
|
||||||
|
static constexpr uint32_t kSpillLocation = kFirstInvalidRegLocation;
|
||||||
|
|
||||||
|
/// A special location reserved for VarLocs of kind EntryValueBackupKind and
|
||||||
|
/// EntryValueCopyBackupKind.
|
||||||
|
static constexpr uint32_t kEntryValueBackupLocation =
|
||||||
|
kFirstInvalidRegLocation + 1;
|
||||||
|
|
||||||
LocIndex(uint32_t Location, uint32_t Index)
|
LocIndex(uint32_t Location, uint32_t Index)
|
||||||
: Location(Location), Index(Index) {}
|
: Location(Location), Index(Index) {}
|
||||||
|
|
||||||
@ -166,6 +174,14 @@ struct LocIndex {
|
|||||||
static uint64_t rawIndexForReg(uint32_t Reg) {
|
static uint64_t rawIndexForReg(uint32_t Reg) {
|
||||||
return LocIndex(Reg, 0).getAsRawInteger();
|
return LocIndex(Reg, 0).getAsRawInteger();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return a range covering all set indices in the interval reserved for
|
||||||
|
/// \p Location in \p Set.
|
||||||
|
static auto indexRangeForLocation(const VarLocSet &Set, uint32_t Location) {
|
||||||
|
uint64_t Start = LocIndex(Location, 0).getAsRawInteger();
|
||||||
|
uint64_t End = LocIndex(Location + 1, 0).getAsRawInteger();
|
||||||
|
return Set.half_open_range(Start, End);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class LiveDebugValues : public MachineFunctionPass {
|
class LiveDebugValues : public MachineFunctionPass {
|
||||||
@ -211,6 +227,9 @@ private:
|
|||||||
bool operator==(const SpillLoc &Other) const {
|
bool operator==(const SpillLoc &Other) const {
|
||||||
return SpillBase == Other.SpillBase && SpillOffset == Other.SpillOffset;
|
return SpillBase == Other.SpillBase && SpillOffset == Other.SpillOffset;
|
||||||
}
|
}
|
||||||
|
bool operator!=(const SpillLoc &Other) const {
|
||||||
|
return !(*this == Other);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Identity of the variable at this location.
|
/// Identity of the variable at this location.
|
||||||
@ -477,10 +496,27 @@ private:
|
|||||||
/// location.
|
/// location.
|
||||||
SmallDenseMap<uint32_t, std::vector<VarLoc>> Loc2Vars;
|
SmallDenseMap<uint32_t, std::vector<VarLoc>> Loc2Vars;
|
||||||
|
|
||||||
|
/// Determine the 32-bit location reserved for \p VL, based on its kind.
|
||||||
|
static uint32_t getLocationForVar(const VarLoc &VL) {
|
||||||
|
switch (VL.Kind) {
|
||||||
|
case VarLoc::RegisterKind:
|
||||||
|
assert((VL.Loc.RegNo < LocIndex::kFirstInvalidRegLocation) &&
|
||||||
|
"Physreg out of range?");
|
||||||
|
return VL.Loc.RegNo;
|
||||||
|
case VarLoc::SpillLocKind:
|
||||||
|
return LocIndex::kSpillLocation;
|
||||||
|
case VarLoc::EntryValueBackupKind:
|
||||||
|
case VarLoc::EntryValueCopyBackupKind:
|
||||||
|
return LocIndex::kEntryValueBackupLocation;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Retrieve a unique LocIndex for \p VL.
|
/// Retrieve a unique LocIndex for \p VL.
|
||||||
LocIndex insert(const VarLoc &VL) {
|
LocIndex insert(const VarLoc &VL) {
|
||||||
uint32_t Location = VL.isDescribedByReg();
|
uint32_t Location = getLocationForVar(VL);
|
||||||
uint32_t &Index = Var2Index[VL];
|
uint32_t &Index = Var2Index[VL];
|
||||||
if (!Index) {
|
if (!Index) {
|
||||||
auto &Vars = Loc2Vars[Location];
|
auto &Vars = Loc2Vars[Location];
|
||||||
@ -577,6 +613,30 @@ private:
|
|||||||
"open ranges are inconsistent");
|
"open ranges are inconsistent");
|
||||||
return VarLocs.empty();
|
return VarLocs.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an empty range of VarLoc IDs.
|
||||||
|
auto getEmptyVarLocRange() const {
|
||||||
|
return iterator_range<VarLocSet::const_iterator>(getVarLocs().end(),
|
||||||
|
getVarLocs().end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all set IDs for VarLocs of kind RegisterKind in \p Reg.
|
||||||
|
auto getRegisterVarLocs(Register Reg) const {
|
||||||
|
return LocIndex::indexRangeForLocation(getVarLocs(), Reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all set IDs for VarLocs of kind SpillLocKind.
|
||||||
|
auto getSpillVarLocs() const {
|
||||||
|
return LocIndex::indexRangeForLocation(getVarLocs(),
|
||||||
|
LocIndex::kSpillLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all set IDs for VarLocs of kind EntryValueBackupKind or
|
||||||
|
/// EntryValueCopyBackupKind.
|
||||||
|
auto getEntryValueBackupVarLocs() const {
|
||||||
|
return LocIndex::indexRangeForLocation(
|
||||||
|
getVarLocs(), LocIndex::kEntryValueBackupLocation);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Collect all VarLoc IDs from \p CollectFrom for VarLocs of kind
|
/// Collect all VarLoc IDs from \p CollectFrom for VarLocs of kind
|
||||||
@ -821,7 +881,10 @@ void LiveDebugValues::getUsedRegs(const VarLocSet &CollectFrom,
|
|||||||
// All register-based VarLocs are assigned indices greater than or equal to
|
// All register-based VarLocs are assigned indices greater than or equal to
|
||||||
// FirstRegIndex.
|
// FirstRegIndex.
|
||||||
uint64_t FirstRegIndex = LocIndex::rawIndexForReg(1);
|
uint64_t FirstRegIndex = LocIndex::rawIndexForReg(1);
|
||||||
for (auto It = CollectFrom.find(FirstRegIndex), End = CollectFrom.end();
|
uint64_t FirstInvalidIndex =
|
||||||
|
LocIndex::rawIndexForReg(LocIndex::kFirstInvalidRegLocation);
|
||||||
|
for (auto It = CollectFrom.find(FirstRegIndex),
|
||||||
|
End = CollectFrom.find(FirstInvalidIndex);
|
||||||
It != End;) {
|
It != End;) {
|
||||||
// We found a VarLoc ID for a VarLoc that lives in a register. Figure out
|
// We found a VarLoc ID for a VarLoc that lives in a register. Figure out
|
||||||
// which register and add it to UsedRegs.
|
// which register and add it to UsedRegs.
|
||||||
@ -924,11 +987,8 @@ bool LiveDebugValues::removeEntryValue(const MachineInstr &MI,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (TrySalvageEntryValue) {
|
if (TrySalvageEntryValue) {
|
||||||
for (uint64_t ID : OpenRanges.getVarLocs()) {
|
for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) {
|
||||||
const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)];
|
const VarLoc &VL = VarLocIDs[LocIndex::fromRawInteger(ID)];
|
||||||
if (!VL.isEntryBackupLoc())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (VL.getEntryValueCopyBackupReg() == Reg &&
|
if (VL.getEntryValueCopyBackupReg() == Reg &&
|
||||||
VL.MI.getOperand(0).getReg() == SrcRegOp->getReg())
|
VL.MI.getOperand(0).getReg() == SrcRegOp->getReg())
|
||||||
return false;
|
return false;
|
||||||
@ -1259,10 +1319,11 @@ void LiveDebugValues::transferSpillOrRestoreInst(MachineInstr &MI,
|
|||||||
VarLocSet KillSet(Alloc);
|
VarLocSet KillSet(Alloc);
|
||||||
if (isSpillInstruction(MI, MF)) {
|
if (isSpillInstruction(MI, MF)) {
|
||||||
Loc = extractSpillBaseRegAndOffset(MI);
|
Loc = extractSpillBaseRegAndOffset(MI);
|
||||||
for (uint64_t ID : OpenRanges.getVarLocs()) {
|
for (uint64_t ID : OpenRanges.getSpillVarLocs()) {
|
||||||
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
||||||
const VarLoc &VL = VarLocIDs[Idx];
|
const VarLoc &VL = VarLocIDs[Idx];
|
||||||
if (VL.Kind == VarLoc::SpillLocKind && VL.Loc.SpillLocation == *Loc) {
|
assert(VL.Kind == VarLoc::SpillLocKind && "Broken VarLocSet?");
|
||||||
|
if (VL.Loc.SpillLocation == *Loc) {
|
||||||
// This location is overwritten by the current instruction -- terminate
|
// This location is overwritten by the current instruction -- terminate
|
||||||
// the open range, and insert an explicit DBG_VALUE $noreg.
|
// the open range, and insert an explicit DBG_VALUE $noreg.
|
||||||
//
|
//
|
||||||
@ -1298,21 +1359,31 @@ void LiveDebugValues::transferSpillOrRestoreInst(MachineInstr &MI,
|
|||||||
<< "\n");
|
<< "\n");
|
||||||
}
|
}
|
||||||
// Check if the register or spill location is the location of a debug value.
|
// Check if the register or spill location is the location of a debug value.
|
||||||
for (uint64_t ID : OpenRanges.getVarLocs()) {
|
auto TransferCandidates = OpenRanges.getEmptyVarLocRange();
|
||||||
|
if (TKind == TransferKind::TransferSpill)
|
||||||
|
TransferCandidates = OpenRanges.getRegisterVarLocs(Reg);
|
||||||
|
else if (TKind == TransferKind::TransferRestore)
|
||||||
|
TransferCandidates = OpenRanges.getSpillVarLocs();
|
||||||
|
for (uint64_t ID : TransferCandidates) {
|
||||||
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
||||||
const VarLoc &VL = VarLocIDs[Idx];
|
const VarLoc &VL = VarLocIDs[Idx];
|
||||||
if (TKind == TransferKind::TransferSpill && VL.isDescribedByReg() == Reg) {
|
if (TKind == TransferKind::TransferSpill) {
|
||||||
|
assert(VL.isDescribedByReg() == Reg && "Broken VarLocSet?");
|
||||||
LLVM_DEBUG(dbgs() << "Spilling Register " << printReg(Reg, TRI) << '('
|
LLVM_DEBUG(dbgs() << "Spilling Register " << printReg(Reg, TRI) << '('
|
||||||
<< VL.Var.getVariable()->getName() << ")\n");
|
<< VL.Var.getVariable()->getName() << ")\n");
|
||||||
} else if (TKind == TransferKind::TransferRestore &&
|
} else {
|
||||||
VL.Kind == VarLoc::SpillLocKind &&
|
assert(TKind == TransferKind::TransferRestore &&
|
||||||
VL.Loc.SpillLocation == *Loc) {
|
VL.Kind == VarLoc::SpillLocKind && "Broken VarLocSet?");
|
||||||
|
if (VL.Loc.SpillLocation != *Loc)
|
||||||
|
// The spill location is not the location of a debug value.
|
||||||
|
continue;
|
||||||
LLVM_DEBUG(dbgs() << "Restoring Register " << printReg(Reg, TRI) << '('
|
LLVM_DEBUG(dbgs() << "Restoring Register " << printReg(Reg, TRI) << '('
|
||||||
<< VL.Var.getVariable()->getName() << ")\n");
|
<< VL.Var.getVariable()->getName() << ")\n");
|
||||||
} else
|
}
|
||||||
continue;
|
|
||||||
insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx, TKind,
|
insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx, TKind,
|
||||||
Reg);
|
Reg);
|
||||||
|
// FIXME: A comment should explain why it's correct to return early here,
|
||||||
|
// if that is in fact correct.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1356,7 +1427,7 @@ void LiveDebugValues::transferRegisterCopy(MachineInstr &MI,
|
|||||||
// a parameter describing only a moving of the value around, rather then
|
// a parameter describing only a moving of the value around, rather then
|
||||||
// modifying it, we are still able to use the entry value if needed.
|
// modifying it, we are still able to use the entry value if needed.
|
||||||
if (isRegOtherThanSPAndFP(*DestRegOp, MI, TRI)) {
|
if (isRegOtherThanSPAndFP(*DestRegOp, MI, TRI)) {
|
||||||
for (uint64_t ID : OpenRanges.getVarLocs()) {
|
for (uint64_t ID : OpenRanges.getEntryValueBackupVarLocs()) {
|
||||||
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
||||||
const VarLoc &VL = VarLocIDs[Idx];
|
const VarLoc &VL = VarLocIDs[Idx];
|
||||||
if (VL.getEntryValueBackupReg() == SrcReg) {
|
if (VL.getEntryValueBackupReg() == SrcReg) {
|
||||||
@ -1378,13 +1449,14 @@ void LiveDebugValues::transferRegisterCopy(MachineInstr &MI,
|
|||||||
if (!SrcRegOp->isKill())
|
if (!SrcRegOp->isKill())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (uint64_t ID : OpenRanges.getVarLocs()) {
|
for (uint64_t ID : OpenRanges.getRegisterVarLocs(SrcReg)) {
|
||||||
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
LocIndex Idx = LocIndex::fromRawInteger(ID);
|
||||||
if (VarLocIDs[Idx].isDescribedByReg() == SrcReg) {
|
assert(VarLocIDs[Idx].isDescribedByReg() == SrcReg && "Broken VarLocSet?");
|
||||||
insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx,
|
insertTransferDebugPair(MI, OpenRanges, Transfers, VarLocIDs, Idx,
|
||||||
TransferKind::TransferCopy, DestReg);
|
TransferKind::TransferCopy, DestReg);
|
||||||
return;
|
// FIXME: A comment should explain why it's correct to return early here,
|
||||||
}
|
// if that is in fact correct.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@
|
|||||||
# CHECK-NEXT: $ebx = MOV32ri 2
|
# CHECK-NEXT: $ebx = MOV32ri 2
|
||||||
# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1)
|
# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1)
|
||||||
# CHECK: bb.3.if.end
|
# CHECK: bb.3.if.end
|
||||||
# CHECK-NEXT: DBG_VALUE $edx, $noreg, ![[ARG_Q]], !DIExpression()
|
# CHECK-DAG: DBG_VALUE $edx, $noreg, ![[ARG_Q]], !DIExpression()
|
||||||
# CHECK-NEXT: DBG_VALUE $ebp, $noreg, ![[ARG_C]], !DIExpression()
|
# CHECK-DAG: DBG_VALUE $ebp, $noreg, ![[ARG_C]], !DIExpression()
|
||||||
# CHECK-NEXT: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1)
|
# CHECK-DAG: DBG_VALUE $esi, $noreg, ![[ARG_B]], !DIExpression(DW_OP_LLVM_entry_value, 1)
|
||||||
--- |
|
--- |
|
||||||
; ModuleID = 'test.c'
|
; ModuleID = 'test.c'
|
||||||
source_filename = "test.c"
|
source_filename = "test.c"
|
||||||
|
@ -31,6 +31,11 @@ bool elementsMatch(const UBitVec &BV, std::initializer_list<unsigned> List) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rangesMatch(iterator_range<UBitVec::const_iterator> R,
|
||||||
|
std::initializer_list<unsigned> List) {
|
||||||
|
return std::equal(R.begin(), R.end(), List.begin(), List.end());
|
||||||
|
}
|
||||||
|
|
||||||
TEST(CoalescingBitVectorTest, Set) {
|
TEST(CoalescingBitVectorTest, Set) {
|
||||||
UBitVec::Allocator Alloc;
|
UBitVec::Allocator Alloc;
|
||||||
UBitVec BV1(Alloc);
|
UBitVec BV1(Alloc);
|
||||||
@ -486,6 +491,56 @@ TEST(CoalescingBitVectorTest, AdvanceToLowerBound) {
|
|||||||
EXPECT_TRUE(It == BV.end());
|
EXPECT_TRUE(It == BV.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(CoalescingBitVectorTest, HalfOpenRange) {
|
||||||
|
UBitVec::Allocator Alloc;
|
||||||
|
|
||||||
|
{
|
||||||
|
UBitVec BV(Alloc);
|
||||||
|
BV.set({1, 2, 3});
|
||||||
|
|
||||||
|
EXPECT_EQ(*BV.find(0), 1U); // find(Start) > Start
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(0, 5), {1, 2, 3}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 4), {1, 2, 3}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 3), {1, 2}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 3), {2}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 4), {2, 3}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(4, 5), {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UBitVec BV(Alloc);
|
||||||
|
BV.set({1, 2, 11, 12});
|
||||||
|
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(0, 15), {1, 2, 11, 12}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 13), {1, 2, 11, 12}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 12), {1, 2, 11}));
|
||||||
|
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(0, 5), {1, 2}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 5), {1, 2}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 5), {2}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(1, 2), {1}));
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(13, 14), {}));
|
||||||
|
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(2, 10), {2}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UBitVec BV(Alloc);
|
||||||
|
BV.set({1});
|
||||||
|
|
||||||
|
EXPECT_EQ(*BV.find(0), 1U); // find(Start) == End
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(0, 1), {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UBitVec BV(Alloc);
|
||||||
|
BV.set({5});
|
||||||
|
|
||||||
|
EXPECT_EQ(*BV.find(3), 5U); // find(Start) > End
|
||||||
|
EXPECT_TRUE(rangesMatch(BV.half_open_range(3, 4), {}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(CoalescingBitVectorTest, Print) {
|
TEST(CoalescingBitVectorTest, Print) {
|
||||||
std::string S;
|
std::string S;
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user