Skip to content

Commit bcdf883

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

File tree

1 file changed

+49
-3
lines changed

1 file changed

+49
-3
lines changed

clang/lib/AST/ParentMapContext.cpp

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,30 @@ 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+
using MapInfo = IndirectDenseMapInfo<const DynTypedNode>;
86+
6387
/// Contains parents of a node.
6488
class ParentVector {
6589
public:
@@ -70,16 +94,38 @@ class ParentMapContext::ParentMap {
7094
push_back(Value);
7195
}
7296
bool contains(const DynTypedNode &Value) {
73-
return Seen.contains(Value);
97+
assert(Value.getMemoizationData());
98+
bool found = FragileLazySeenCache.contains(&Value);
99+
while (!found && ItemsProcessed < Items.size()) {
100+
const auto it = Items.begin() + ItemsProcessed;
101+
found |= MapInfo::isEqual(&*it, &Value);
102+
FragileLazySeenCache.insert(&*it);
103+
++ItemsProcessed;
104+
}
105+
return found;
74106
}
75107
void push_back(const DynTypedNode &Value) {
76-
if (!Value.getMemoizationData() || Seen.insert(Value).second)
108+
if (!Value.getMemoizationData() || !contains(Value)) {
109+
const size_t OldCapacity = Items.capacity();
77110
Items.push_back(Value);
111+
if (OldCapacity != Items.capacity()) {
112+
// Pointers are invalidated; remove them.
113+
ItemsProcessed = 0;
114+
// Free memory to avoid doubling peak memory usage during rehashing
115+
FragileLazySeenCache.clear();
116+
}
117+
}
78118
}
79119
llvm::ArrayRef<DynTypedNode> view() const { return Items; }
80120
private:
121+
// BE CAREFUL. Pointers into this container are stored in the
122+
// `FragileLazySeenCache` set below.
81123
llvm::SmallVector<DynTypedNode, 2> Items;
82-
llvm::SmallDenseSet<DynTypedNode, 2> Seen;
124+
// This cache is fragile because it contains pointers that are invalidated
125+
// when the vector capacity changes.
126+
llvm::SmallDenseSet<const DynTypedNode *, 2, MapInfo> FragileLazySeenCache;
127+
// Lazily tracks which items have been processed for the cache.
128+
size_t ItemsProcessed = 0;
83129
};
84130

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

0 commit comments

Comments
 (0)