1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-25 12:12:47 +01:00

[ADT] CoalescingBitVector: Avoid initial heap allocation, NFC

Avoid making a heap allocation when constructing a CoalescingBitVector.

This reduces time spent in LiveDebugValues when compiling sqlite3 by
700ms (0.5% of the total User Time).

rdar://60046261

Differential Revision: https://reviews.llvm.org/D76465
This commit is contained in:
Vedant Kumar 2020-03-19 16:21:52 -07:00
parent d737b91aa4
commit c1c993b3e8
3 changed files with 38 additions and 53 deletions

View File

@ -21,7 +21,6 @@
#include <algorithm> #include <algorithm>
#include <initializer_list> #include <initializer_list>
#include <memory>
namespace llvm { namespace llvm {
@ -34,8 +33,7 @@ namespace llvm {
/// performance for non-sequential find() operations. /// performance for non-sequential find() operations.
/// ///
/// \tparam IndexT - The type of the index into the bitvector. /// \tparam IndexT - The type of the index into the bitvector.
/// \tparam N - The first N coalesced intervals of set bits are stored in-place /// \tparam N - The first N coalesced intervals of set bits are stored in-place.
/// (in the initial heap allocation).
template <typename IndexT, unsigned N = 16> class CoalescingBitVector { template <typename IndexT, unsigned N = 16> class CoalescingBitVector {
static_assert(std::is_unsigned<IndexT>::value, static_assert(std::is_unsigned<IndexT>::value,
"Index must be an unsigned integer."); "Index must be an unsigned integer.");
@ -55,13 +53,13 @@ public:
/// Construct by passing in a CoalescingBitVector<IndexT>::Allocator /// Construct by passing in a CoalescingBitVector<IndexT>::Allocator
/// reference. /// reference.
CoalescingBitVector(Allocator &Alloc) CoalescingBitVector(Allocator &Alloc)
: Alloc(&Alloc), Intervals(std::make_unique<MapT>(Alloc)) {} : Alloc(&Alloc), Intervals(Alloc) {}
/// \name Copy/move constructors and assignment operators. /// \name Copy/move constructors and assignment operators.
/// @{ /// @{
CoalescingBitVector(const ThisT &Other) CoalescingBitVector(const ThisT &Other)
: Alloc(Other.Alloc), Intervals(std::make_unique<MapT>(*Other.Alloc)) { : Alloc(Other.Alloc), Intervals(*Other.Alloc) {
set(Other); set(Other);
} }
@ -71,27 +69,21 @@ public:
return *this; return *this;
} }
CoalescingBitVector(ThisT &&Other) CoalescingBitVector(ThisT &&Other) = delete;
: Alloc(Other.Alloc), Intervals(std::move(Other.Intervals)) {} ThisT &operator=(ThisT &&Other) = delete;
ThisT &operator=(ThisT &&Other) {
Alloc = Other.Alloc;
Intervals = std::move(Other.Intervals);
return *this;
}
/// @} /// @}
/// Clear all the bits. /// Clear all the bits.
void clear() { Intervals->clear(); } void clear() { Intervals.clear(); }
/// Check whether no bits are set. /// Check whether no bits are set.
bool empty() const { return Intervals->empty(); } bool empty() const { return Intervals.empty(); }
/// Count the number of set bits. /// Count the number of set bits.
unsigned count() const { unsigned count() const {
unsigned Bits = 0; unsigned Bits = 0;
for (auto It = Intervals->begin(), End = Intervals->end(); It != End; ++It) for (auto It = Intervals.begin(), End = Intervals.end(); It != End; ++It)
Bits += 1 + It.stop() - It.start(); Bits += 1 + It.stop() - It.start();
return Bits; return Bits;
} }
@ -112,7 +104,7 @@ public:
/// This method does /not/ support setting already-set bits, see \ref set /// This method does /not/ support setting already-set bits, see \ref set
/// for the rationale. For a safe set union operation, use \ref operator|=. /// for the rationale. For a safe set union operation, use \ref operator|=.
void set(const ThisT &Other) { void set(const ThisT &Other) {
for (auto It = Other.Intervals->begin(), End = Other.Intervals->end(); for (auto It = Other.Intervals.begin(), End = Other.Intervals.end();
It != End; ++It) It != End; ++It)
insert(It.start(), It.stop()); insert(It.start(), It.stop());
} }
@ -125,8 +117,8 @@ public:
/// Check whether the bit at \p Index is set. /// Check whether the bit at \p Index is set.
bool test(IndexT Index) const { bool test(IndexT Index) const {
const auto It = Intervals->find(Index); const auto It = Intervals.find(Index);
if (It == Intervals->end()) if (It == Intervals.end())
return false; return false;
assert(It.stop() >= Index && "Interval must end after Index"); assert(It.stop() >= Index && "Interval must end after Index");
return It.start() <= Index; return It.start() <= Index;
@ -140,8 +132,8 @@ public:
/// Reset the bit at \p Index. Supports resetting an already-unset bit. /// Reset the bit at \p Index. Supports resetting an already-unset bit.
void reset(IndexT Index) { void reset(IndexT Index) {
auto It = Intervals->find(Index); auto It = Intervals.find(Index);
if (It == Intervals->end()) if (It == Intervals.end())
return; return;
// Split the interval containing Index into up to two parts: one from // Split the interval containing Index into up to two parts: one from
@ -169,7 +161,7 @@ public:
getOverlaps(RHS, Overlaps); getOverlaps(RHS, Overlaps);
// Insert the non-overlapping parts of all the intervals from RHS. // Insert the non-overlapping parts of all the intervals from RHS.
for (auto It = RHS.Intervals->begin(), End = RHS.Intervals->end(); for (auto It = RHS.Intervals.begin(), End = RHS.Intervals.end();
It != End; ++It) { It != End; ++It) {
IndexT Start = It.start(); IndexT Start = It.start();
IndexT Stop = It.stop(); IndexT Stop = It.stop();
@ -205,7 +197,7 @@ public:
IndexT OlapStart, OlapStop; IndexT OlapStart, OlapStop;
std::tie(OlapStart, OlapStop) = Overlap; std::tie(OlapStart, OlapStop) = Overlap;
auto It = Intervals->find(OlapStart); auto It = Intervals.find(OlapStart);
IndexT CurrStart = It.start(); IndexT CurrStart = It.start();
IndexT CurrStop = It.stop(); IndexT CurrStop = It.stop();
assert(CurrStart <= OlapStart && OlapStop <= CurrStop && assert(CurrStart <= OlapStart && OlapStop <= CurrStop &&
@ -227,14 +219,14 @@ public:
// We cannot just use std::equal because it checks the dereferenced values // We cannot just use std::equal because it checks the dereferenced values
// of an iterator pair for equality, not the iterators themselves. In our // of an iterator pair for equality, not the iterators themselves. In our
// case that results in comparison of the (unused) IntervalMap values. // case that results in comparison of the (unused) IntervalMap values.
auto ItL = Intervals->begin(); auto ItL = Intervals.begin();
auto ItR = RHS.Intervals->begin(); auto ItR = RHS.Intervals.begin();
while (ItL != Intervals->end() && ItR != RHS.Intervals->end() && while (ItL != Intervals.end() && ItR != RHS.Intervals.end() &&
ItL.start() == ItR.start() && ItL.stop() == ItR.stop()) { ItL.start() == ItR.start() && ItL.stop() == ItR.stop()) {
++ItL; ++ItL;
++ItR; ++ItR;
} }
return ItL == Intervals->end() && ItR == RHS.Intervals->end(); return ItL == Intervals.end() && ItR == RHS.Intervals.end();
} }
bool operator!=(const ThisT &RHS) const { return !operator==(RHS); } bool operator!=(const ThisT &RHS) const { return !operator==(RHS); }
@ -324,15 +316,15 @@ public:
} }
}; };
const_iterator begin() const { return const_iterator(Intervals->begin()); } const_iterator begin() const { return const_iterator(Intervals.begin()); }
const_iterator end() const { return const_iterator(); } const_iterator end() const { return const_iterator(); }
/// Return an iterator pointing to the first set bit AT, OR AFTER, \p Index. /// Return an iterator pointing to the first set bit AT, OR AFTER, \p Index.
/// If no such set bit exists, return end(). This is like std::lower_bound. /// If no such set bit exists, return end(). This is like std::lower_bound.
const_iterator find(IndexT Index) const { const_iterator find(IndexT Index) const {
auto UnderlyingIt = Intervals->find(Index); auto UnderlyingIt = Intervals.find(Index);
if (UnderlyingIt == Intervals->end()) if (UnderlyingIt == Intervals.end())
return end(); return end();
auto It = const_iterator(UnderlyingIt); auto It = const_iterator(UnderlyingIt);
It.advanceTo(Index); It.advanceTo(Index);
@ -341,7 +333,7 @@ public:
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;
++It) { ++It) {
OS << "[" << It.start(); OS << "[" << It.start();
if (It.start() != It.stop()) if (It.start() != It.stop())
@ -362,13 +354,13 @@ public:
#endif #endif
private: private:
void insert(IndexT Start, IndexT End) { Intervals->insert(Start, End, 0); } void insert(IndexT Start, IndexT End) { Intervals.insert(Start, End, 0); }
/// Record the overlaps between \p this and \p Other in \p Overlaps. Return /// Record the overlaps between \p this and \p Other in \p Overlaps. Return
/// true if there is any overlap. /// true if there is any overlap.
bool getOverlaps(const ThisT &Other, bool getOverlaps(const ThisT &Other,
SmallVectorImpl<IntervalT> &Overlaps) const { SmallVectorImpl<IntervalT> &Overlaps) const {
for (IntervalMapOverlaps<MapT, MapT> I(*Intervals, *Other.Intervals); for (IntervalMapOverlaps<MapT, MapT> I(Intervals, Other.Intervals);
I.valid(); ++I) I.valid(); ++I)
Overlaps.emplace_back(I.start(), I.stop()); Overlaps.emplace_back(I.start(), I.stop());
assert(std::is_sorted(Overlaps.begin(), Overlaps.end(), assert(std::is_sorted(Overlaps.begin(), Overlaps.end(),
@ -409,7 +401,7 @@ private:
} }
Allocator *Alloc; Allocator *Alloc;
std::unique_ptr<MapT> Intervals; MapT Intervals;
}; };
} // namespace llvm } // namespace llvm

View File

@ -482,7 +482,8 @@ private:
} }
}; };
using VarLocInMBB = SmallDenseMap<const MachineBasicBlock *, VarLocSet>; using VarLocInMBB =
SmallDenseMap<const MachineBasicBlock *, std::unique_ptr<VarLocSet>>;
struct TransferDebugPair { struct TransferDebugPair {
MachineInstr *TransferInst; ///< Instruction where this transfer occurs. MachineInstr *TransferInst; ///< Instruction where this transfer occurs.
LocIndex LocationID; ///< Location number for the transfer dest. LocIndex LocationID; ///< Location number for the transfer dest.
@ -573,15 +574,17 @@ private:
SmallVectorImpl<uint32_t> &UsedRegs) const; SmallVectorImpl<uint32_t> &UsedRegs) const;
VarLocSet &getVarLocsInMBB(const MachineBasicBlock *MBB, VarLocInMBB &Locs) { VarLocSet &getVarLocsInMBB(const MachineBasicBlock *MBB, VarLocInMBB &Locs) {
auto Result = Locs.try_emplace(MBB, Alloc); std::unique_ptr<VarLocSet> &VLS = Locs[MBB];
return Result.first->second; if (!VLS)
VLS = std::make_unique<VarLocSet>(Alloc);
return *VLS.get();
} }
const VarLocSet &getVarLocsInMBB(const MachineBasicBlock *MBB, const VarLocSet &getVarLocsInMBB(const MachineBasicBlock *MBB,
const VarLocInMBB &Locs) const { const VarLocInMBB &Locs) const {
auto It = Locs.find(MBB); auto It = Locs.find(MBB);
assert(It != Locs.end() && "MBB not in map"); assert(It != Locs.end() && "MBB not in map");
return It->second; return *It->second.get();
} }
/// Tests whether this instruction is a spill to a stack location. /// Tests whether this instruction is a spill to a stack location.
@ -1479,10 +1482,11 @@ bool LiveDebugValues::join(
// Just copy over the Out locs to incoming locs for the first visited // Just copy over the Out locs to incoming locs for the first visited
// predecessor, and for all other predecessors join the Out locs. // predecessor, and for all other predecessors join the Out locs.
VarLocSet &OutLocVLS = *OL->second.get();
if (!NumVisited) if (!NumVisited)
InLocsT = OL->second; InLocsT = OutLocVLS;
else else
InLocsT &= OL->second; InLocsT &= OutLocVLS;
LLVM_DEBUG({ LLVM_DEBUG({
if (!InLocsT.empty()) { if (!InLocsT.empty()) {
@ -1554,7 +1558,7 @@ void LiveDebugValues::flushPendingLocs(VarLocInMBB &PendingInLocs,
for (auto &Iter : PendingInLocs) { for (auto &Iter : PendingInLocs) {
// Map is keyed on a constant pointer, unwrap it so we can insert insts. // Map is keyed on a constant pointer, unwrap it so we can insert insts.
auto &MBB = const_cast<MachineBasicBlock &>(*Iter.first); auto &MBB = const_cast<MachineBasicBlock &>(*Iter.first);
VarLocSet &Pending = Iter.second; VarLocSet &Pending = *Iter.second.get();
for (uint64_t ID : Pending) { for (uint64_t ID : Pending) {
// The ID location is live-in to MBB -- work out what kind of machine // The ID location is live-in to MBB -- work out what kind of machine
@ -1703,7 +1707,7 @@ bool LiveDebugValues::ExtendRanges(MachineFunction &MF) {
// Initialize per-block structures and scan for fragment overlaps. // Initialize per-block structures and scan for fragment overlaps.
for (auto &MBB : MF) { for (auto &MBB : MF) {
PendingInLocs.try_emplace(&MBB, Alloc); PendingInLocs[&MBB] = std::make_unique<VarLocSet>(Alloc);
for (auto &MI : MBB) { for (auto &MI : MBB) {
if (MI.isDebugValue()) if (MI.isDebugValue())

View File

@ -77,17 +77,6 @@ TEST(CoalescingBitVector, Copy) {
EXPECT_TRUE(elementsMatch(BV2, {0})); EXPECT_TRUE(elementsMatch(BV2, {0}));
} }
TEST(CoalescingBitVector, Move) {
UBitVec::Allocator Alloc;
UBitVec BV1(Alloc);
BV1.set(0);
UBitVec BV2 = std::move(BV1);
EXPECT_TRUE(elementsMatch(BV2, {0}));
BV2.set(5);
BV1 = std::move(BV2);
EXPECT_TRUE(elementsMatch(BV1, {0, 5}));
}
TEST(CoalescingBitVectorTest, Iterators) { TEST(CoalescingBitVectorTest, Iterators) {
UBitVec::Allocator Alloc; UBitVec::Allocator Alloc;
UBitVec BV(Alloc); UBitVec BV(Alloc);