Skip to content

Commit 9e95c49

Browse files
authored
[DSE] Fix non-determinism due to address reuse (#84943)
The malloc->calloc fold creates a new MemoryAccess, which may end of at the same address as a previously deleted access inside SkipStores. To the most part, this is not a problem, because SkipStores is normally only used together with MemDefs. Neither the old malloc access nor the new calloc access will be part of MemDefs, so there is no problem here. However, SkipStores is also used in one more place: In the main DSE loop, ToCheck entries are checked against it. Fix this by not using SkipStores here, and instead using a separate set to track deletions inside this loop. This way it is not affected by the calloc optimization that happens outside it. This is all pretty ugly, but I haven't found another good way to fix it. Suggestions welcome. No test case as I don't have a reliable DSE-only test-case for this. Fixes #84458.
1 parent b9bed1f commit 9e95c49

File tree

1 file changed

+17
-5
lines changed

1 file changed

+17
-5
lines changed

llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,7 +1699,9 @@ struct DSEState {
16991699

17001700
/// Delete dead memory defs and recursively add their operands to ToRemove if
17011701
/// they became dead.
1702-
void deleteDeadInstruction(Instruction *SI) {
1702+
void
1703+
deleteDeadInstruction(Instruction *SI,
1704+
SmallPtrSetImpl<MemoryAccess *> *Deleted = nullptr) {
17031705
MemorySSAUpdater Updater(&MSSA);
17041706
SmallVector<Instruction *, 32> NowDeadInsts;
17051707
NowDeadInsts.push_back(SI);
@@ -1720,6 +1722,8 @@ struct DSEState {
17201722
if (IsMemDef) {
17211723
auto *MD = cast<MemoryDef>(MA);
17221724
SkipStores.insert(MD);
1725+
if (Deleted)
1726+
Deleted->insert(MD);
17231727
if (auto *SI = dyn_cast<StoreInst>(MD->getMemoryInst())) {
17241728
if (SI->getValueOperand()->getType()->isPointerTy()) {
17251729
const Value *UO = getUnderlyingObject(SI->getValueOperand());
@@ -2168,14 +2172,19 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
21682172
unsigned PartialLimit = MemorySSAPartialStoreLimit;
21692173
// Worklist of MemoryAccesses that may be killed by KillingDef.
21702174
SmallSetVector<MemoryAccess *, 8> ToCheck;
2175+
// Track MemoryAccesses that have been deleted in the loop below, so we can
2176+
// skip them. Don't use SkipStores for this, which may contain reused
2177+
// MemoryAccess addresses.
2178+
SmallPtrSet<MemoryAccess *, 8> Deleted;
2179+
[[maybe_unused]] unsigned OrigNumSkipStores = State.SkipStores.size();
21712180
ToCheck.insert(KillingDef->getDefiningAccess());
21722181

21732182
bool Shortend = false;
21742183
bool IsMemTerm = State.isMemTerminatorInst(KillingI);
21752184
// Check if MemoryAccesses in the worklist are killed by KillingDef.
21762185
for (unsigned I = 0; I < ToCheck.size(); I++) {
21772186
MemoryAccess *Current = ToCheck[I];
2178-
if (State.SkipStores.count(Current))
2187+
if (Deleted.contains(Current))
21792188
continue;
21802189

21812190
std::optional<MemoryAccess *> MaybeDeadAccess = State.getDomMemoryDef(
@@ -2222,7 +2231,7 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
22222231
continue;
22232232
LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *DeadI
22242233
<< "\n KILLER: " << *KillingI << '\n');
2225-
State.deleteDeadInstruction(DeadI);
2234+
State.deleteDeadInstruction(DeadI, &Deleted);
22262235
++NumFastStores;
22272236
MadeChange = true;
22282237
} else {
@@ -2259,7 +2268,7 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
22592268
Shortend = true;
22602269
// Remove killing store and remove any outstanding overlap
22612270
// intervals for the updated store.
2262-
State.deleteDeadInstruction(KillingSI);
2271+
State.deleteDeadInstruction(KillingSI, &Deleted);
22632272
auto I = State.IOLs.find(DeadSI->getParent());
22642273
if (I != State.IOLs.end())
22652274
I->second.erase(DeadSI);
@@ -2271,13 +2280,16 @@ static bool eliminateDeadStores(Function &F, AliasAnalysis &AA, MemorySSA &MSSA,
22712280
if (OR == OW_Complete) {
22722281
LLVM_DEBUG(dbgs() << "DSE: Remove Dead Store:\n DEAD: " << *DeadI
22732282
<< "\n KILLER: " << *KillingI << '\n');
2274-
State.deleteDeadInstruction(DeadI);
2283+
State.deleteDeadInstruction(DeadI, &Deleted);
22752284
++NumFastStores;
22762285
MadeChange = true;
22772286
}
22782287
}
22792288
}
22802289

2290+
assert(State.SkipStores.size() - OrigNumSkipStores == Deleted.size() &&
2291+
"SkipStores and Deleted out of sync?");
2292+
22812293
// Check if the store is a no-op.
22822294
if (!Shortend && State.storeIsNoop(KillingDef, KillingUndObj)) {
22832295
LLVM_DEBUG(dbgs() << "DSE: Remove No-Op Store:\n DEAD: " << *KillingI

0 commit comments

Comments
 (0)