diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp index bfc8bd5970bf2..ed4212d29cef7 100644 --- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp +++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp @@ -1699,7 +1699,9 @@ struct DSEState { /// Delete dead memory defs and recursively add their operands to ToRemove if /// they became dead. - void deleteDeadInstruction(Instruction *SI) { + void + deleteDeadInstruction(Instruction *SI, + SmallPtrSetImpl *Deleted = nullptr) { MemorySSAUpdater Updater(&MSSA); SmallVector NowDeadInsts; NowDeadInsts.push_back(SI); @@ -1720,6 +1722,8 @@ struct DSEState { if (IsMemDef) { auto *MD = cast(MA); SkipStores.insert(MD); + if (Deleted) + Deleted->insert(MD); if (auto *SI = dyn_cast(MD->getMemoryInst())) { if (SI->getValueOperand()->getType()->isPointerTy()) { const Value *UO = getUnderlyingObject(SI->getValueOperand()); @@ -2168,6 +2172,11 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, unsigned PartialLimit = MemorySSAPartialStoreLimit; // Worklist of MemoryAccesses that may be killed by KillingDef. SmallSetVector ToCheck; + // Track MemoryAccesses that have been deleted in the loop below, so we can + // skip them. Don't use SkipStores for this, which may contain reused + // MemoryAccess addresses. + SmallPtrSet Deleted; + [[maybe_unused]] unsigned OrigNumSkipStores = State.SkipStores.size(); ToCheck.insert(KillingDef->getDefiningAccess()); bool Shortend = false; @@ -2175,7 +2184,7 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, // Check if MemoryAccesses in the worklist are killed by KillingDef. for (unsigned I = 0; I < ToCheck.size(); I++) { MemoryAccess *Current = ToCheck[I]; - if (State.SkipStores.count(Current)) + if (Deleted.contains(Current)) continue; std::optional MaybeDeadAccess = State.getDomMemoryDef( @@ -2222,7 +2231,7 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, continue; LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *DeadI << "\n KILLER: " << *KillingI << '\n'); - State.deleteDeadInstruction(DeadI); + State.deleteDeadInstruction(DeadI, &Deleted); ++NumFastStores; MadeChange = true; } else { @@ -2259,7 +2268,7 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, Shortend = true; // Remove killing store and remove any outstanding overlap // intervals for the updated store. - State.deleteDeadInstruction(KillingSI); + State.deleteDeadInstruction(KillingSI, &Deleted); auto I = State.IOLs.find(DeadSI->getParent()); if (I != State.IOLs.end()) I->second.erase(DeadSI); @@ -2271,13 +2280,16 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA, if (OR == OW_Complete) { LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *DeadI << "\n KILLER: " << *KillingI << '\n'); - State.deleteDeadInstruction(DeadI); + State.deleteDeadInstruction(DeadI, &Deleted); ++NumFastStores; MadeChange = true; } } } + assert(State.SkipStores.size() - OrigNumSkipStores == Deleted.size() && + "SkipStores and Deleted out of sync?"); + // Check if the store is a no-op. if (!Shortend && State.storeIsNoop(KillingDef, KillingUndObj)) { LLVM_DEBUG(dbgs() << "DSE: Remove No-Op Store:\n DEAD: " << *KillingI