@@ -60,6 +60,34 @@ class ParentMapContext::ParentMap {
60
60
61
61
template <typename , typename ...> friend struct ::MatchParents;
62
62
63
+ template <class T > struct IndirectDenseMapInfo {
64
+ using Ptr = T *;
65
+ using Base = llvm::DenseMapInfo<std::remove_cv_t <T>>;
66
+ static T &Verify (T &Val) {
67
+ assert (Val.getMemoizationData ());
68
+ return Val;
69
+ }
70
+ static inline Ptr getEmptyKey () {
71
+ return static_cast <Ptr >(llvm::DenseMapInfo<void *>::getEmptyKey ());
72
+ }
73
+ static inline Ptr getTombstoneKey () {
74
+ return static_cast <Ptr >(llvm::DenseMapInfo<void *>::getTombstoneKey ());
75
+ }
76
+ static unsigned getHashValue (Ptr Val) {
77
+ return Val == getEmptyKey () || Val == getTombstoneKey ()
78
+ ? 0
79
+ : Base::getHashValue (Verify (*Val));
80
+ }
81
+ static bool isEqual (Ptr LHS, Ptr RHS) {
82
+ if (LHS == getEmptyKey () || LHS == getTombstoneKey () ||
83
+ RHS == getEmptyKey () || RHS == getTombstoneKey () || LHS == RHS) {
84
+ return LHS == RHS;
85
+ }
86
+ return Base::isEqual (Verify (*LHS), Verify (*RHS));
87
+ }
88
+ };
89
+ using MapInfo = IndirectDenseMapInfo<const DynTypedNode>;
90
+
63
91
// / Contains parents of a node.
64
92
class ParentVector {
65
93
public:
@@ -69,17 +97,70 @@ class ParentMapContext::ParentMap {
69
97
for (; N > 0 ; --N)
70
98
push_back (Value);
71
99
}
72
- bool contains (const DynTypedNode &Value) {
73
- return Seen.contains (Value);
74
- }
100
+ bool contains (const DynTypedNode &Value) { return CacheUntil (Value); }
75
101
void push_back (const DynTypedNode &Value) {
76
- if (!Value.getMemoizationData () || Seen.insert (Value).second )
102
+ if (!Value.getMemoizationData () || !FragileDedupCache.contains (&Value)) {
103
+ const size_t OldCapacity = Items.capacity ();
77
104
Items.push_back (Value);
105
+ // Danger: Clear the cache if its pointers might be invalidated.
106
+ if (OldCapacity != Items.capacity ()) {
107
+ ItemsProcessed = 0 ;
108
+ FragileDedupCache.clear ();
109
+ }
110
+ }
111
+
112
+ // Cache some nodes every time we attempt an insertion.
113
+ // This is important to guarantee that our (cheap) cache catches up with
114
+ // the (expensive) main vector upon repeated insertions, thus bounding
115
+ // element duplication.
116
+ // It also amortizes insertions to constant-time, preventing us from
117
+ // spending unnecessary time rebuilding the cache when it is invalidated.
118
+ CacheUntil (2 );
119
+ }
120
+ llvm::ArrayRef<DynTypedNode> view () {
121
+ CacheUntil ();
122
+ assert (ItemsProcessed == Items.size ());
123
+ return Items;
78
124
}
79
- llvm::ArrayRef<DynTypedNode> view () const { return Items; }
125
+
80
126
private:
127
+ // Places at most `MaxCount` items into the cache, stopping early if the
128
+ // node is seen.
129
+ bool CacheUntil (size_t MaxCount = std::numeric_limits<size_t >::max(),
130
+ const DynTypedNode *OptionalNeedle = nullptr) {
131
+ assert (!OptionalNeedle || OptionalNeedle->getMemoizationData ());
132
+
133
+ bool Found = OptionalNeedle && FragileDedupCache.contains (OptionalNeedle);
134
+ while (!Found && ItemsProcessed < Items.size () && MaxCount > 0 ) {
135
+ const auto *Item = &Items[ItemsProcessed];
136
+
137
+ if (Item->getMemoizationData ()) {
138
+ FragileDedupCache.insert (Item);
139
+ }
140
+ ++ItemsProcessed;
141
+ --MaxCount;
142
+
143
+ assert (!Found);
144
+ Found = OptionalNeedle && Item->getMemoizationData () &&
145
+ MapInfo::isEqual (OptionalNeedle, Item);
146
+ }
147
+
148
+ return Found;
149
+ }
150
+ bool CacheUntil (const DynTypedNode &Needle) {
151
+ return CacheUntil (std::numeric_limits<size_t >::max (), &Needle);
152
+ }
153
+
154
+ // A partitioned vector of nodes, where the first `ItemsProcessed` elements
155
+ // are already processed into the cache, and the remaining have not.
156
+ // BE CAREFUL. Pointers into this container are stored in the
157
+ // `FragileDedupCache` set below.
81
158
llvm::SmallVector<DynTypedNode, 2 > Items;
82
- llvm::SmallDenseSet<DynTypedNode, 2 > Seen;
159
+ // This cache is fragile because it contains pointers that are invalidated
160
+ // when the vector capacity changes.
161
+ llvm::SmallDenseSet<const DynTypedNode *, 2 , MapInfo> FragileDedupCache;
162
+ // Lazily tracks which items have been processed for the cache.
163
+ size_t ItemsProcessed = 0 ;
83
164
};
84
165
85
166
// / Maps from a node to its parents. This is used for nodes that have
@@ -117,7 +198,7 @@ class ParentMapContext::ParentMap {
117
198
if (I == Map.end ()) {
118
199
return llvm::ArrayRef<DynTypedNode>();
119
200
}
120
- if (const auto *V = dyn_cast<ParentVector *>(I->second )) {
201
+ if (auto *V = dyn_cast<ParentVector *>(I->second )) {
121
202
return V->view ();
122
203
}
123
204
return getSingleDynTypedNodeFromParentMap (I->second );
0 commit comments