Skip to content

Commit 46e04f7

Browse files
authored
[MemorySSA] Handle MemoryDef optimized away during cloning (llvm#117883)
When determining the replacement access during cloning, we currently leave accesses for instructions that are not in the VMap alone. This is correct if the instruction is not in VMap because it hasn't been cloned, but not if it has been cloned and then removed. In that case, we should walk up to the defining access, like in other simplification cases. To distinguish the two cases, pass in a callback that queries whether the instruction is part of the cloned region. An alternative to this would be to delay removal of dead instructions in SimpleLoopUnswitch until after MSSA cloning. Fixes llvm#116228.
1 parent fbbea89 commit 46e04f7

File tree

4 files changed

+160
-43
lines changed

4 files changed

+160
-43
lines changed

llvm/include/llvm/Analysis/MemorySSAUpdater.h

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -260,23 +260,32 @@ class MemorySSAUpdater {
260260
MemoryAccess *tryRemoveTrivialPhi(MemoryPhi *Phi, RangeType &Operands);
261261
void tryRemoveTrivialPhis(ArrayRef<WeakVH> UpdatedPHIs);
262262
void fixupDefs(const SmallVectorImpl<WeakVH> &);
263-
// Clone all uses and defs from BB to NewBB given a 1:1 map of all
264-
// instructions and blocks cloned, and a map of MemoryPhi : Definition
265-
// (MemoryAccess Phi or Def). VMap maps old instructions to cloned
266-
// instructions and old blocks to cloned blocks. MPhiMap, is created in the
267-
// caller of this private method, and maps existing MemoryPhis to new
268-
// definitions that new MemoryAccesses must point to. These definitions may
269-
// not necessarily be MemoryPhis themselves, they may be MemoryDefs. As such,
270-
// the map is between MemoryPhis and MemoryAccesses, where the MemoryAccesses
271-
// may be MemoryPhis or MemoryDefs and not MemoryUses.
272-
// If CloneWasSimplified = true, the clone was exact. Otherwise, assume that
273-
// the clone involved simplifications that may have: (1) turned a MemoryUse
274-
// into an instruction that MemorySSA has no representation for, or (2) turned
275-
// a MemoryDef into a MemoryUse or an instruction that MemorySSA has no
276-
// representation for. No other cases are supported.
263+
/// Clone all uses and defs from BB to NewBB given a 1:1 map of all
264+
/// instructions and blocks cloned, and a map of MemoryPhi : Definition
265+
/// (MemoryAccess Phi or Def).
266+
///
267+
/// \param VMap Maps old instructions to cloned instructions and old blocks
268+
/// to cloned blocks
269+
/// \param MPhiMap, is created in the caller of this private method, and maps
270+
/// existing MemoryPhis to new definitions that new MemoryAccesses
271+
/// must point to. These definitions may not necessarily be MemoryPhis
272+
/// themselves, they may be MemoryDefs. As such, the map is between
273+
/// MemoryPhis and MemoryAccesses, where the MemoryAccesses may be
274+
/// MemoryPhis or MemoryDefs and not MemoryUses.
275+
/// \param IsInClonedRegion Determines whether a basic block was cloned.
276+
/// References to accesses outside the cloned region will not be
277+
/// remapped.
278+
/// \param CloneWasSimplified If false, the clone was exact. Otherwise,
279+
/// assume that the clone involved simplifications that may have:
280+
/// (1) turned a MemoryUse into an instruction that MemorySSA has no
281+
/// representation for, or (2) turned a MemoryDef into a MemoryUse or
282+
/// an instruction that MemorySSA has no representation for. No other
283+
/// cases are supported.
277284
void cloneUsesAndDefs(BasicBlock *BB, BasicBlock *NewBB,
278285
const ValueToValueMapTy &VMap, PhiToDefMap &MPhiMap,
286+
function_ref<bool(BasicBlock *)> IsInClonedRegion,
279287
bool CloneWasSimplified = false);
288+
280289
template <typename Iter>
281290
void privateUpdateExitBlocksForClonedLoop(ArrayRef<BasicBlock *> ExitBlocks,
282291
Iter ValuesBegin, Iter ValuesEnd,

llvm/lib/Analysis/MemorySSAUpdater.cpp

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -565,24 +565,26 @@ static MemoryAccess *onlySingleValue(MemoryPhi *MP) {
565565
return MA;
566566
}
567567

568-
static MemoryAccess *getNewDefiningAccessForClone(MemoryAccess *MA,
569-
const ValueToValueMapTy &VMap,
570-
PhiToDefMap &MPhiMap,
571-
MemorySSA *MSSA) {
568+
static MemoryAccess *getNewDefiningAccessForClone(
569+
MemoryAccess *MA, const ValueToValueMapTy &VMap, PhiToDefMap &MPhiMap,
570+
MemorySSA *MSSA, function_ref<bool(BasicBlock *BB)> IsInClonedRegion) {
572571
MemoryAccess *InsnDefining = MA;
573572
if (MemoryDef *DefMUD = dyn_cast<MemoryDef>(InsnDefining)) {
574-
if (!MSSA->isLiveOnEntryDef(DefMUD)) {
575-
Instruction *DefMUDI = DefMUD->getMemoryInst();
576-
assert(DefMUDI && "Found MemoryUseOrDef with no Instruction.");
577-
if (Instruction *NewDefMUDI =
578-
cast_or_null<Instruction>(VMap.lookup(DefMUDI))) {
579-
InsnDefining = MSSA->getMemoryAccess(NewDefMUDI);
580-
if (!InsnDefining || isa<MemoryUse>(InsnDefining)) {
581-
// The clone was simplified, it's no longer a MemoryDef, look up.
582-
InsnDefining = getNewDefiningAccessForClone(
583-
DefMUD->getDefiningAccess(), VMap, MPhiMap, MSSA);
584-
}
585-
}
573+
if (MSSA->isLiveOnEntryDef(DefMUD))
574+
return DefMUD;
575+
576+
// If the MemoryDef is not part of the cloned region, leave it alone.
577+
Instruction *DefMUDI = DefMUD->getMemoryInst();
578+
assert(DefMUDI && "Found MemoryUseOrDef with no Instruction.");
579+
if (!IsInClonedRegion(DefMUDI->getParent()))
580+
return DefMUD;
581+
582+
auto *NewDefMUDI = cast_or_null<Instruction>(VMap.lookup(DefMUDI));
583+
InsnDefining = NewDefMUDI ? MSSA->getMemoryAccess(NewDefMUDI) : nullptr;
584+
if (!InsnDefining || isa<MemoryUse>(InsnDefining)) {
585+
// The clone was simplified, it's no longer a MemoryDef, look up.
586+
InsnDefining = getNewDefiningAccessForClone(
587+
DefMUD->getDefiningAccess(), VMap, MPhiMap, MSSA, IsInClonedRegion);
586588
}
587589
} else {
588590
MemoryPhi *DefPhi = cast<MemoryPhi>(InsnDefining);
@@ -593,10 +595,10 @@ static MemoryAccess *getNewDefiningAccessForClone(MemoryAccess *MA,
593595
return InsnDefining;
594596
}
595597

596-
void MemorySSAUpdater::cloneUsesAndDefs(BasicBlock *BB, BasicBlock *NewBB,
597-
const ValueToValueMapTy &VMap,
598-
PhiToDefMap &MPhiMap,
599-
bool CloneWasSimplified) {
598+
void MemorySSAUpdater::cloneUsesAndDefs(
599+
BasicBlock *BB, BasicBlock *NewBB, const ValueToValueMapTy &VMap,
600+
PhiToDefMap &MPhiMap, function_ref<bool(BasicBlock *)> IsInClonedRegion,
601+
bool CloneWasSimplified) {
600602
const MemorySSA::AccessList *Acc = MSSA->getBlockAccesses(BB);
601603
if (!Acc)
602604
return;
@@ -615,7 +617,7 @@ void MemorySSAUpdater::cloneUsesAndDefs(BasicBlock *BB, BasicBlock *NewBB,
615617
MemoryAccess *NewUseOrDef = MSSA->createDefinedAccess(
616618
NewInsn,
617619
getNewDefiningAccessForClone(MUD->getDefiningAccess(), VMap,
618-
MPhiMap, MSSA),
620+
MPhiMap, MSSA, IsInClonedRegion),
619621
/*Template=*/CloneWasSimplified ? nullptr : MUD,
620622
/*CreationMustSucceed=*/false);
621623
if (NewUseOrDef)
@@ -668,8 +670,13 @@ void MemorySSAUpdater::updateForClonedLoop(const LoopBlocksRPO &LoopBlocks,
668670
ArrayRef<BasicBlock *> ExitBlocks,
669671
const ValueToValueMapTy &VMap,
670672
bool IgnoreIncomingWithNoClones) {
671-
PhiToDefMap MPhiMap;
673+
SmallSetVector<BasicBlock *, 16> Blocks;
674+
for (BasicBlock *BB : concat<BasicBlock *const>(LoopBlocks, ExitBlocks))
675+
Blocks.insert(BB);
672676

677+
auto IsInClonedRegion = [&](BasicBlock *BB) { return Blocks.contains(BB); };
678+
679+
PhiToDefMap MPhiMap;
673680
auto FixPhiIncomingValues = [&](MemoryPhi *Phi, MemoryPhi *NewPhi) {
674681
assert(Phi && NewPhi && "Invalid Phi nodes.");
675682
BasicBlock *NewPhiBB = NewPhi->getBlock();
@@ -692,9 +699,10 @@ void MemorySSAUpdater::updateForClonedLoop(const LoopBlocksRPO &LoopBlocks,
692699
continue;
693700

694701
// Determine incoming value and add it as incoming from IncBB.
695-
NewPhi->addIncoming(
696-
getNewDefiningAccessForClone(IncomingAccess, VMap, MPhiMap, MSSA),
697-
IncBB);
702+
NewPhi->addIncoming(getNewDefiningAccessForClone(IncomingAccess, VMap,
703+
MPhiMap, MSSA,
704+
IsInClonedRegion),
705+
IncBB);
698706
}
699707
if (auto *SingleAccess = onlySingleValue(NewPhi)) {
700708
MPhiMap[Phi] = SingleAccess;
@@ -716,13 +724,13 @@ void MemorySSAUpdater::updateForClonedLoop(const LoopBlocksRPO &LoopBlocks,
716724
MPhiMap[MPhi] = NewPhi;
717725
}
718726
// Update Uses and Defs.
719-
cloneUsesAndDefs(BB, NewBlock, VMap, MPhiMap);
727+
cloneUsesAndDefs(BB, NewBlock, VMap, MPhiMap, IsInClonedRegion);
720728
};
721729

722-
for (auto *BB : llvm::concat<BasicBlock *const>(LoopBlocks, ExitBlocks))
730+
for (auto *BB : Blocks)
723731
ProcessBlock(BB);
724732

725-
for (auto *BB : llvm::concat<BasicBlock *const>(LoopBlocks, ExitBlocks))
733+
for (auto *BB : Blocks)
726734
if (MemoryPhi *MPhi = MSSA->getMemoryAccess(BB))
727735
if (MemoryAccess *NewPhi = MPhiMap.lookup(MPhi))
728736
FixPhiIncomingValues(MPhi, cast<MemoryPhi>(NewPhi));
@@ -741,7 +749,9 @@ void MemorySSAUpdater::updateForClonedBlockIntoPred(
741749
PhiToDefMap MPhiMap;
742750
if (MemoryPhi *MPhi = MSSA->getMemoryAccess(BB))
743751
MPhiMap[MPhi] = MPhi->getIncomingValueForBlock(P1);
744-
cloneUsesAndDefs(BB, P1, VM, MPhiMap, /*CloneWasSimplified=*/true);
752+
cloneUsesAndDefs(
753+
BB, P1, VM, MPhiMap, [&](BasicBlock *CheckBB) { return BB == CheckBB; },
754+
/*CloneWasSimplified=*/true);
745755
}
746756

747757
template <typename Iter>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
; RUN: opt -disable-output -passes="loop-mssa(loop-rotate),print<memoryssa>" -verify-memoryssa < %s 2>&1 | FileCheck %s
2+
3+
; CHECK: entry:
4+
; CHECK-NEXT: 3 = MemoryDef(liveOnEntry)
5+
; CHECK-NEXT: store ptr null, ptr %p, align 8
6+
; CHECK-NEXT: MemoryUse(3)
7+
; CHECK-NEXT: %val11 = load ptr, ptr %p, align 8
8+
9+
; CHECK: loop.latch:
10+
; CHECK-NEXT: 5 = MemoryPhi({loop.latch,1},{loop.latch.lr.ph,3})
11+
; CHECK-NEXT: MemoryUse(5)
12+
; CHECK-NEXT: %val2 = load ptr, ptr %p, align 8
13+
; CHECK-NEXT: 1 = MemoryDef(5)
14+
; CHECK-NEXT: store ptr null, ptr %p, align 8
15+
; CHECK-NEXT: MemoryUse(1)
16+
; CHECK-NEXT: %val1 = load ptr, ptr %p, align 8
17+
18+
; CHECK: exit:
19+
; CHECK-NEXT: 4 = MemoryPhi({entry,3},{loop.exit_crit_edge,1})
20+
21+
define void @test(ptr %p) {
22+
entry:
23+
br label %loop
24+
25+
loop:
26+
store ptr null, ptr %p
27+
%val1 = load ptr, ptr %p
28+
%cmp = icmp eq ptr %val1, null
29+
br i1 %cmp, label %exit, label %loop.latch
30+
31+
loop.latch:
32+
%val2 = load ptr, ptr %p
33+
br label %loop
34+
35+
exit:
36+
ret void
37+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
; RUN: opt -disable-output -passes="loop-mssa(simple-loop-unswitch<nontrivial>),print<memoryssa>" -verify-memoryssa < %s 2>&1 | FileCheck %s
2+
3+
declare ptr @malloc() allockind("alloc,uninitialized")
4+
5+
; CHECK-LABEL: MemorySSA for function: test
6+
7+
; CHECK: for.body.us:
8+
; CHECK-NEXT: 3 = MemoryPhi({entry.split.us,liveOnEntry},{for.body.us,3})
9+
10+
; CHECK: for.body:
11+
; CHECK-NEXT: 2 = MemoryPhi({entry.split,liveOnEntry},{for.body,1})
12+
; CHECK-NEXT: 1 = MemoryDef(2)
13+
; CHECK-NEXT: %call.i = call ptr @malloc()
14+
15+
define void @test(i1 %arg) {
16+
entry:
17+
br label %for.body
18+
19+
for.body:
20+
%call.i = call ptr @malloc()
21+
%cmp.i = icmp ne ptr %call.i, null
22+
%or.cond.i = select i1 %cmp.i, i1 %arg, i1 false
23+
br i1 %or.cond.i, label %exit, label %for.body
24+
25+
exit:
26+
ret void
27+
}
28+
29+
; CHECK-LABEL: MemorySSA for function: test_extra_defs
30+
31+
; CHECK: entry:
32+
; CHECK-NEXT: 1 = MemoryDef(liveOnEntry)
33+
; CHECK-NEXT: store i8 1, ptr %p, align 1
34+
35+
; CHECK: for.body.us:
36+
; CHECK-NEXT: 5 = MemoryPhi({entry.split.us,1},{for.body.us,6})
37+
; CHECK-NEXT: 6 = MemoryDef(5)
38+
; CHECK-NEXT: store i8 2, ptr %p, align 1
39+
40+
; CHECK: for.body:
41+
; CHECK-NEXT: 4 = MemoryPhi({entry.split,1},{for.body,3})
42+
; CHECK-NEXT: 2 = MemoryDef(4)
43+
; CHECK-NEXT: store i8 2, ptr %p
44+
; CHECK-NEXT: 3 = MemoryDef(2)
45+
; CHECK-NEXT: %call.i = call ptr @malloc()
46+
47+
define void @test_extra_defs(ptr %p, i1 %arg) {
48+
entry:
49+
store i8 1, ptr %p
50+
br label %for.body
51+
52+
for.body:
53+
store i8 2, ptr %p
54+
%call.i = call ptr @malloc()
55+
%cmp.i = icmp ne ptr %call.i, null
56+
%or.cond.i = select i1 %cmp.i, i1 %arg, i1 false
57+
br i1 %or.cond.i, label %exit, label %for.body
58+
59+
exit:
60+
ret void
61+
}

0 commit comments

Comments
 (0)