@@ -151,29 +151,43 @@ bool FunctionAccessedStorage::updateUnidentifiedAccess(
151
151
// substitution will be performed if possible. However, there's no guarantee
152
152
// that the merged access values will belong to this function.
153
153
//
154
- // Note that we may have `this` == `other` for self-recursion. We still need to
155
- // propagate and merge in that case in case arguments are recursively dependent .
154
+ // Return true if these results changed, requiring further propagation through
155
+ // the call graph .
156
156
bool FunctionAccessedStorage::mergeAccesses (
157
157
const FunctionAccessedStorage &other,
158
158
std::function<StorageAccessInfo(const StorageAccessInfo &)>
159
159
transformStorage) {
160
160
161
- // Insertion in DenseMap invalidates the iterator in the rare case of
162
- // self-recursion (`this` == `other`) that passes accessed storage though an
163
- // argument. Rather than complicate the code, make a temporary copy of the
164
- // AccessedStorage.
165
- //
166
- // Also note that the storageAccessIndex from otherStorage is relative to its
167
- // original context and should not be copied into this context.
168
- SmallVector<StorageAccessInfo, 8 > otherStorageAccesses;
169
- otherStorageAccesses.reserve (other.storageAccessSet .size ());
170
- otherStorageAccesses.append (other.storageAccessSet .begin (),
171
- other.storageAccessSet .end ());
161
+ // The cost of BottomUpIPAnalysis can be quadratic for large recursive call
162
+ // graphs. That cost is multiplied by the size of storageAccessSet. Slowdowns
163
+ // can occur ~1000 elements. 200 is large enough to cover "normal" code,
164
+ // while ensuring compile time isn't affected.
165
+ if (storageAccessSet.size () > 200 ) {
166
+ llvm::dbgs () << " BIG SET " << storageAccessSet.size () << " \n " ;
167
+ setWorstEffects ();
168
+ return true ;
169
+ }
170
+ // To save compile time, if this storage already has worst-case effects, avoid
171
+ // growing its storageAccessSet.
172
+ if (hasWorstEffects ())
173
+ return false ;
172
174
175
+ // When `this` == `other` (for self-recursion), insertion in DenseMap
176
+ // invalidates the iterator. We still need to propagate and merge in that case
177
+ // because arguments can be recursively dependent. The alternative would be
178
+ // treating all self-recursion conservatively.
179
+ const FunctionAccessedStorage *otherFunctionAccesses = &other;
180
+ FunctionAccessedStorage functionAccessCopy;
181
+ if (this == &other) {
182
+ functionAccessCopy = other;
183
+ otherFunctionAccesses = &functionAccessCopy;
184
+ }
173
185
bool changed = false ;
174
- for (auto &rawStorageInfo : otherStorageAccesses) {
186
+ // Nondeterminstically iterate for the sole purpose of inserting into another
187
+ // unordered set.
188
+ for (auto &rawStorageInfo : otherFunctionAccesses->storageAccessSet ) {
175
189
const StorageAccessInfo &otherStorageInfo =
176
- transformStorage (rawStorageInfo);
190
+ transformStorage (rawStorageInfo);
177
191
// If transformStorage() returns invalid storage object for local storage,
178
192
// that should not be merged with the caller.
179
193
if (!otherStorageInfo)
0 commit comments