Skip to content

Commit 7195572

Browse files
authored
[SSAUpdater] Add a SmallPtrSet reserve method for IDFcalc (#97823)
As per the LLVM programmers manual, SmallPtrSets do linear scans on insertion and then turn into a hash-table if the set gets big. Here in the IDFCalculator, the SmallPtrSets have been configured to have 32 elements in each static allocation... which means that we linearly scan for all problems with up to 32 elements, which I feel is quite a large N. Shorten the SmallPtrSet size, and add a reserve method to avoid any repeated allocations, plus corresponding unit tests. Doing this yields a 0.13% compile-time improvement for debug-info builds, as we hit IDFCalculator pretty hard in InstrRefBasedLDV.
1 parent cb5912a commit 7195572

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-2
lines changed

llvm/include/llvm/ADT/SmallPtrSet.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "llvm/ADT/EpochTracker.h"
1919
#include "llvm/Support/Compiler.h"
20+
#include "llvm/Support/MathExtras.h"
2021
#include "llvm/Support/ReverseIteration.h"
2122
#include "llvm/Support/type_traits.h"
2223
#include <cassert>
@@ -92,6 +93,7 @@ class SmallPtrSetImplBase : public DebugEpochBase {
9293

9394
[[nodiscard]] bool empty() const { return size() == 0; }
9495
size_type size() const { return NumNonEmpty - NumTombstones; }
96+
size_type capacity() const { return CurArraySize; }
9597

9698
void clear() {
9799
incrementEpoch();
@@ -108,6 +110,27 @@ class SmallPtrSetImplBase : public DebugEpochBase {
108110
NumTombstones = 0;
109111
}
110112

113+
void reserve(size_type NumEntries) {
114+
incrementEpoch();
115+
// Do nothing if we're given zero as a reservation size.
116+
if (NumEntries == 0)
117+
return;
118+
// No need to expand if we're small and NumEntries will fit in the space.
119+
if (isSmall() && NumEntries <= CurArraySize)
120+
return;
121+
// insert_imp_big will reallocate if stores is more than 75% full, on the
122+
// /final/ insertion.
123+
if (!isSmall() && ((NumEntries - 1) * 4) < (CurArraySize * 3))
124+
return;
125+
// We must Grow -- find the size where we'd be 75% full, then round up to
126+
// the next power of two.
127+
size_type NewSize = NumEntries + (NumEntries / 3);
128+
NewSize = 1 << (Log2_32_Ceil(NewSize) + 1);
129+
// Like insert_imp_big, always allocate at least 128 elements.
130+
NewSize = std::max(128u, NewSize);
131+
Grow(NewSize);
132+
}
133+
111134
protected:
112135
static void *getTombstoneMarker() { return reinterpret_cast<void*>(-2); }
113136

llvm/include/llvm/Support/GenericIteratedDominanceFrontier.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,12 @@ void IDFCalculatorBase<NodeTy, IsPostDom>::calculate(
145145
DT.updateDFSNumbers();
146146

147147
SmallVector<DomTreeNodeBase<NodeTy> *, 32> Worklist;
148-
SmallPtrSet<DomTreeNodeBase<NodeTy> *, 32> VisitedPQ;
149-
SmallPtrSet<DomTreeNodeBase<NodeTy> *, 32> VisitedWorklist;
148+
SmallPtrSet<DomTreeNodeBase<NodeTy> *, 16> VisitedPQ;
149+
SmallPtrSet<DomTreeNodeBase<NodeTy> *, 16> VisitedWorklist;
150+
if (useLiveIn) {
151+
VisitedPQ.reserve(LiveInBlocks->size());
152+
VisitedWorklist.reserve(LiveInBlocks->size());
153+
}
150154

151155
for (NodeTy *BB : *DefBlocks)
152156
if (DomTreeNodeBase<NodeTy> *Node = DT.getNode(BB)) {

llvm/unittests/ADT/SmallPtrSetTest.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,11 @@
1414
#include "llvm/ADT/PointerIntPair.h"
1515
#include "llvm/ADT/STLExtras.h"
1616
#include "llvm/Support/PointerLikeTypeTraits.h"
17+
#include "gmock/gmock.h"
1718
#include "gtest/gtest.h"
1819

1920
using namespace llvm;
21+
using testing::UnorderedElementsAre;
2022

2123
TEST(SmallPtrSetTest, Assignment) {
2224
int buf[8];
@@ -408,3 +410,50 @@ TEST(SmallPtrSetTest, RemoveIf) {
408410
Removed = Set.remove_if([](int *Ptr) { return false; });
409411
EXPECT_FALSE(Removed);
410412
}
413+
414+
TEST(SmallPtrSetTest, Reserve) {
415+
// Check that we don't do anything silly when using reserve().
416+
SmallPtrSet<int *, 4> Set;
417+
int Vals[8] = {0, 1, 2, 3, 4, 5, 6, 7};
418+
419+
Set.insert(&Vals[0]);
420+
421+
// We shouldn't reallocate when this happens.
422+
Set.reserve(4);
423+
EXPECT_EQ(Set.capacity(), 4u);
424+
425+
Set.insert(&Vals[1]);
426+
Set.insert(&Vals[2]);
427+
Set.insert(&Vals[3]);
428+
429+
// We shouldn't reallocate this time either.
430+
Set.reserve(4);
431+
EXPECT_EQ(Set.capacity(), 4u);
432+
EXPECT_EQ(Set.size(), 4u);
433+
EXPECT_THAT(Set,
434+
UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3]));
435+
436+
// Reserving further should lead to a reallocation. And matching the existing
437+
// insertion approach, we immediately allocate up to 128 elements.
438+
Set.reserve(5);
439+
EXPECT_EQ(Set.capacity(), 128u);
440+
EXPECT_EQ(Set.size(), 4u);
441+
EXPECT_THAT(Set,
442+
UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3]));
443+
444+
// And we should be able to insert another two or three elements without
445+
// reallocating.
446+
Set.insert(&Vals[4]);
447+
Set.insert(&Vals[5]);
448+
449+
// Calling a smaller reserve size should have no effect.
450+
Set.reserve(1);
451+
EXPECT_EQ(Set.capacity(), 128u);
452+
EXPECT_EQ(Set.size(), 6u);
453+
454+
// Reserving zero should have no effect either.
455+
Set.reserve(0);
456+
EXPECT_EQ(Set.capacity(), 128u);
457+
EXPECT_EQ(Set.size(), 6u);
458+
EXPECT_THAT(Set, UnorderedElementsAre(&Vals[0], &Vals[1], &Vals[2], &Vals[3], &Vals[4], &Vals[5]));
459+
}

0 commit comments

Comments
 (0)