Skip to content

Commit eaefea5

Browse files
Reduce memory usage in AST parent map generation by lazily checking if nodes have been seen
1 parent c017cdf commit eaefea5

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

clang/lib/AST/ParentMapContext.cpp

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,29 @@ class ParentMapContext::ParentMap {
6060

6161
template <typename, typename...> friend struct ::MatchParents;
6262

63+
template <class T> struct IndirectDenseMapInfo {
64+
using Ptr = T *;
65+
using Base = llvm::DenseMapInfo<std::remove_cv_t<T>>;
66+
static inline Ptr getEmptyKey() {
67+
return static_cast<Ptr>(llvm::DenseMapInfo<void *>::getEmptyKey());
68+
}
69+
static inline Ptr getTombstoneKey() {
70+
return static_cast<Ptr>(llvm::DenseMapInfo<void *>::getTombstoneKey());
71+
}
72+
static unsigned getHashValue(Ptr Val) {
73+
return Val == getEmptyKey() || Val == getTombstoneKey()
74+
? 0
75+
: Base::getHashValue(*Val);
76+
}
77+
static bool isEqual(Ptr LHS, Ptr RHS) {
78+
if (LHS == getEmptyKey() || LHS == getTombstoneKey() ||
79+
RHS == getEmptyKey() || RHS == getTombstoneKey()) {
80+
return LHS == RHS;
81+
}
82+
return Base::isEqual(*LHS, *RHS);
83+
}
84+
};
85+
6386
/// Contains parents of a node.
6487
class ParentVector {
6588
public:
@@ -70,16 +93,38 @@ class ParentMapContext::ParentMap {
7093
push_back(Value);
7194
}
7295
bool contains(const DynTypedNode &Value) {
73-
return Seen.contains(Value);
96+
assert(Value.getMemoizationData());
97+
bool found = FragileLazySeenCache.contains(&Value);
98+
while (!found && ItemsProcessed < Items.size()) {
99+
found |= FragileLazySeenCache.insert(&Items[ItemsProcessed]).second;
100+
++ItemsProcessed;
101+
}
102+
return found;
74103
}
75104
void push_back(const DynTypedNode &Value) {
76-
if (!Value.getMemoizationData() || Seen.insert(Value).second)
105+
if (!Value.getMemoizationData() || !contains(Value)) {
106+
const size_t OldCapacity = Items.capacity();
77107
Items.push_back(Value);
108+
if (OldCapacity != Items.capacity()) {
109+
// Pointers are invalidated; remove them.
110+
ItemsProcessed = 0;
111+
// Free memory to avoid doubling peak memory usage during rehashing
112+
FragileLazySeenCache.clear();
113+
}
114+
}
78115
}
79116
llvm::ArrayRef<DynTypedNode> view() const { return Items; }
80117
private:
118+
// BE CAREFUL. Pointers into this container are stored in the
119+
// `FragileLazySeenCache` set below.
81120
llvm::SmallVector<DynTypedNode, 2> Items;
82-
llvm::SmallDenseSet<DynTypedNode, 2> Seen;
121+
// This cache is fragile because it contains pointers that are invalidated
122+
// when the vector capacity changes.
123+
llvm::SmallDenseSet<const DynTypedNode *, 2,
124+
IndirectDenseMapInfo<const DynTypedNode>>
125+
FragileLazySeenCache;
126+
// Lazily tracks which items have been processed for the cache.
127+
size_t ItemsProcessed = 0;
83128
};
84129

85130
/// Maps from a node to its parents. This is used for nodes that have

0 commit comments

Comments
 (0)