Skip to content

Commit 6302439

Browse files
authored
[llvm,clang] Don't assume non-erased DenseMap entries remain valid after erase. NFC (llvm#198982)
In preparation for switching DenseMap from tombstone deletion to backward-shift deletion, update call sites that reuse an iterator or a bucket reference after erasing another entry from the same map. These work under tombstone deletion because unrelated buckets stay put, but backward-shift deletion relocates entries to close the gap. Add DenseMap::remove_if, similar to SmallPtrSet::remove_if, as replacement for erase-while-iterating, and use it where applicable. Aided by Claude Opus 4.7
1 parent 69a5cf5 commit 6302439

24 files changed

Lines changed: 212 additions & 134 deletions

clang/lib/AST/ASTImporter.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9864,10 +9864,15 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
98649864
// Failed to import.
98659865

98669866
auto Pos = ImportedDecls.find(FromD);
9867-
if (Pos != ImportedDecls.end()) {
9867+
bool ToDWasCreated = Pos != ImportedDecls.end();
9868+
// Capture the mapped decl before erasing: the iterator is invalidated by
9869+
// the erase below under backward-shift deletion, but it is still needed
9870+
// further down to record the import error.
9871+
Decl *CreatedToD = ToDWasCreated ? Pos->second : nullptr;
9872+
if (ToDWasCreated) {
98689873
// Import failed after the object was created.
98699874
// Remove all references to it.
9870-
auto *ToD = Pos->second;
9875+
auto *ToD = CreatedToD;
98719876
ImportedDecls.erase(Pos);
98729877

98739878
// ImportedDecls and ImportedFromDecls are not symmetric. It may happen
@@ -9903,8 +9908,8 @@ Expected<Decl *> ASTImporter::Import(Decl *FromD) {
99039908
[&ErrOut](const ASTImportError &E) { ErrOut = E; });
99049909
setImportDeclError(FromD, ErrOut);
99059910
// Set the error for the mapped to Decl, which is in the "to" context.
9906-
if (Pos != ImportedDecls.end())
9907-
SharedState->setImportDeclError(Pos->second, ErrOut);
9911+
if (ToDWasCreated)
9912+
SharedState->setImportDeclError(CreatedToD, ErrOut);
99089913

99099914
// Set the error for all nodes which have been created before we
99109915
// recognized the error.

clang/lib/CodeGen/CoverageMappingGen.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,13 +2260,9 @@ struct CounterCoverageMappingBuilder
22602260
(void)FoundCount;
22612261

22622262
// Tell CodeGenPGO not to instrument.
2263-
for (auto I = MCDCState.BranchByStmt.begin(),
2264-
E = MCDCState.BranchByStmt.end();
2265-
I != E;) {
2266-
auto II = I++;
2267-
if (II->second.DecisionStmt == Decision)
2268-
MCDCState.BranchByStmt.erase(II);
2269-
}
2263+
MCDCState.BranchByStmt.remove_if([&](const auto &Entry) {
2264+
return Entry.second.DecisionStmt == Decision;
2265+
});
22702266
MCDCState.DecisionByStmt.erase(Decision);
22712267
}
22722268

clang/lib/Interpreter/IncrementalParser.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ IncrementalParser::Parse(llvm::StringRef input) {
174174

175175
void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
176176
if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
177+
// Collect the keys to erase: erasing during iteration invalidates the map
178+
// iterator under backward-shift deletion.
179+
llvm::SmallVector<DeclarationName, 16> KeysToErase;
177180
for (auto &&[Key, List] : *Map) {
178181
DeclContextLookupResult R = List.getLookupResult();
179182
std::vector<NamedDecl *> NamedDeclsToRemove;
@@ -185,12 +188,14 @@ void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
185188
RemoveAll = false;
186189
}
187190
if (LLVM_LIKELY(RemoveAll)) {
188-
Map->erase(Key);
191+
KeysToErase.push_back(Key);
189192
} else {
190193
for (NamedDecl *D : NamedDeclsToRemove)
191194
List.remove(D);
192195
}
193196
}
197+
for (DeclarationName Key : KeysToErase)
198+
Map->erase(Key);
194199
}
195200

196201
ExternCContextDecl *ECCD = S.getASTContext().getExternCContextDecl();

clang/lib/Sema/HLSLExternalSemaSource.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,10 @@ void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) {
726726
auto It = Completions.find(Record);
727727
if (It == Completions.end())
728728
return;
729-
It->second(Record);
729+
// Move out the callback and erase before invoking it: the callback can
730+
// re-enter CompleteType and mutate Completions, which invalidates It under
731+
// backward-shift deletion.
732+
CompletionFunction Fn = std::move(It->second);
730733
Completions.erase(It);
734+
Fn(Record);
731735
}

llvm/include/llvm/ADT/DenseMap.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,33 @@ class DenseMapBase : public DebugEpochBase {
344344
incrementNumTombstones();
345345
}
346346

347+
/// Remove entries that match the given predicate. \p Pred is invoked
348+
/// with a reference to each live bucket and must not access the map being
349+
/// modified. This is the safe replacement for erase-while-iterating.
350+
///
351+
/// Returns whether anything was removed. If so, all iterators and references
352+
/// into the map are invalidated.
353+
template <typename Predicate> bool remove_if(Predicate Pred) {
354+
const KeyT EmptyKey = KeyInfoT::getEmptyKey();
355+
const KeyT TombstoneKey = KeyInfoT::getTombstoneKey();
356+
bool Removed = false;
357+
for (BucketT &B : buckets()) {
358+
if (KeyInfoT::isEqual(B.getFirst(), EmptyKey) ||
359+
KeyInfoT::isEqual(B.getFirst(), TombstoneKey))
360+
continue;
361+
if (Pred(B)) {
362+
B.getSecond().~ValueT();
363+
B.getFirst() = TombstoneKey;
364+
decrementNumEntries();
365+
incrementNumTombstones();
366+
Removed = true;
367+
}
368+
}
369+
if (Removed)
370+
incrementEpoch();
371+
return Removed;
372+
}
373+
347374
ValueT &operator[](const KeyT &Key) {
348375
return lookupOrInsertIntoBucket(Key).first->second;
349376
}

llvm/include/llvm/ADT/DenseSet.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,16 @@ class DenseSetImpl {
9999

100100
bool erase(const ValueT &V) { return TheMap.erase(V); }
101101

102+
/// Remove all elements for which \p Pred returns true. This is the safe
103+
/// replacement for erase-while-iterating; see DenseMap::remove_if. The
104+
/// predicate must not access the set being modified. Returns whether
105+
/// anything was removed; if so, all iterators are invalidated.
106+
template <typename Predicate> bool remove_if(Predicate Pred) {
107+
return TheMap.remove_if([&](const typename MapTy::value_type &KV) {
108+
return Pred(KV.getFirst());
109+
});
110+
}
111+
102112
void swap(DenseSetImpl &RHS) { TheMap.swap(RHS.TheMap); }
103113

104114
private:

llvm/lib/Analysis/IRSimilarityIdentifier.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,12 @@ bool IRSimilarityCandidate::compareAssignmentMapping(
720720
if (!WasInserted && !ValueMappingIt->second.contains(InstValB))
721721
return false;
722722
else if (ValueMappingIt->second.size() != 1) {
723-
for (unsigned OtherVal : ValueMappingIt->second) {
723+
// Snapshot the set before iterating: when InstValA maps to itself the
724+
// erase below removes InstValA from the very set being iterated, which
725+
// invalidates the range iterator under backward-shift deletion.
726+
SmallVector<unsigned> OtherVals(ValueMappingIt->second.begin(),
727+
ValueMappingIt->second.end());
728+
for (unsigned OtherVal : OtherVals) {
724729
if (OtherVal == InstValB)
725730
continue;
726731
auto OtherValIt = ValueNumberMappingA.find(OtherVal);

llvm/lib/Analysis/LoopAccessAnalysis.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,12 +3229,11 @@ void LoopAccessInfoManager::clear() {
32293229
// analyzed loop or SCEVs that may have been modified or invalidated. At the
32303230
// moment, that is loops requiring memory or SCEV runtime checks, as those cache
32313231
// SCEVs, e.g. for pointer expressions.
3232-
for (const auto &[L, LAI] : LoopAccessInfoMap) {
3233-
if (LAI->getRuntimePointerChecking()->getChecks().empty() &&
3234-
LAI->getPSE().getPredicate().isAlwaysTrue())
3235-
continue;
3236-
LoopAccessInfoMap.erase(L);
3237-
}
3232+
LoopAccessInfoMap.remove_if([](const auto &Entry) {
3233+
const auto &LAI = Entry.second;
3234+
return !(LAI->getRuntimePointerChecking()->getChecks().empty() &&
3235+
LAI->getPSE().getPredicate().isAlwaysTrue());
3236+
});
32383237
}
32393238

32403239
bool LoopAccessInfoManager::invalidate(

llvm/lib/Analysis/ScalarEvolution.cpp

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8763,8 +8763,8 @@ void ScalarEvolution::visitAndClearUsers(
87638763
ValueExprMapType::iterator It =
87648764
ValueExprMap.find_as(static_cast<Value *>(I));
87658765
if (It != ValueExprMap.end()) {
8766-
eraseValueFromMap(It->first);
87678766
ToForget.push_back(It->second);
8767+
eraseValueFromMap(It->first);
87688768
if (PHINode *PN = dyn_cast<PHINode>(I))
87698769
ConstantEvolutionLoopExitValue.erase(PN);
87708770
}
@@ -8788,14 +8788,8 @@ void ScalarEvolution::forgetLoop(const Loop *L) {
87888788
forgetBackedgeTakenCounts(CurrL, /* Predicated */ true);
87898789

87908790
// Drop information about predicated SCEV rewrites for this loop.
8791-
for (auto I = PredicatedSCEVRewrites.begin();
8792-
I != PredicatedSCEVRewrites.end();) {
8793-
std::pair<const SCEV *, const Loop *> Entry = I->first;
8794-
if (Entry.second == CurrL)
8795-
PredicatedSCEVRewrites.erase(I++);
8796-
else
8797-
++I;
8798-
}
8791+
PredicatedSCEVRewrites.remove_if(
8792+
[&](const auto &Entry) { return Entry.first.second == CurrL; });
87998793

88008794
auto LoopUsersItr = LoopUsers.find(CurrL);
88018795
if (LoopUsersItr != LoopUsers.end())
@@ -14581,14 +14575,8 @@ void ScalarEvolution::forgetMemoizedResults(ArrayRef<SCEVUse> SCEVs) {
1458114575
for (const auto *S : ToForget)
1458214576
forgetMemoizedResultsImpl(S);
1458314577

14584-
for (auto I = PredicatedSCEVRewrites.begin();
14585-
I != PredicatedSCEVRewrites.end();) {
14586-
std::pair<const SCEV *, const Loop *> Entry = I->first;
14587-
if (ToForget.count(Entry.first))
14588-
PredicatedSCEVRewrites.erase(I++);
14589-
else
14590-
++I;
14591-
}
14578+
PredicatedSCEVRewrites.remove_if(
14579+
[&](const auto &Entry) { return ToForget.count(Entry.first.first); });
1459214580
}
1459314581

1459414582
void ScalarEvolution::forgetMemoizedResultsImpl(const SCEV *S) {

llvm/lib/CodeGen/AssignmentTrackingAnalysis.cpp

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -543,18 +543,15 @@ class MemLocFragmentFill {
543543
// Meet A and B.
544544
//
545545
// Result = meet(a, b) for a in A, b in B where Var(a) == Var(b)
546-
for (auto It = A.begin(), End = A.end(); It != End; ++It) {
547-
unsigned AVar = It->first;
548-
FragsInMemMap &AFrags = It->second;
549-
auto BIt = B.find(AVar);
550-
if (BIt == B.end()) {
551-
A.erase(It);
552-
continue; // Var has no bits defined in B.
553-
}
546+
A.remove_if([&](auto &Entry) {
547+
auto BIt = B.find(Entry.first);
548+
if (BIt == B.end())
549+
return true; // Var has no bits defined in B.
554550
LLVM_DEBUG(dbgs() << "meet fragment maps for "
555-
<< Aggregates[AVar].first->getName() << "\n");
556-
AFrags = meetFragments(AFrags, BIt->second);
557-
}
551+
<< Aggregates[Entry.first].first->getName() << "\n");
552+
Entry.second = meetFragments(Entry.second, BIt->second);
553+
return false;
554+
});
558555
}
559556

560557
bool meet(const BasicBlock &BB,

0 commit comments

Comments
 (0)