Skip to content

Commit d02c793

Browse files
authored
[MSSA] Don't require clone creation to succeed (llvm#76819)
Sometimes, we create a MemoryAccess for an instruction, which is later simplified (e.g. via devirtualization) such that the new instruction has no memory effects anymore. If we later clone the instruction (e.g. during unswitching), then MSSA will not create a MemoryAccess for the new instruction, triggering an assert. Disable the assertion (by passing CreationMustSucceed=false) and adjust getDefiningAccessForClone() to work correctly in that case. This PR implements the alternative suggestion by alinas from llvm#76142.
1 parent 2c213c4 commit d02c793

File tree

2 files changed

+121
-13
lines changed

2 files changed

+121
-13
lines changed

llvm/lib/Analysis/MemorySSAUpdater.cpp

+4-13
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,6 @@ static MemoryAccess *onlySingleValue(MemoryPhi *MP) {
568568
static MemoryAccess *getNewDefiningAccessForClone(MemoryAccess *MA,
569569
const ValueToValueMapTy &VMap,
570570
PhiToDefMap &MPhiMap,
571-
bool CloneWasSimplified,
572571
MemorySSA *MSSA) {
573572
MemoryAccess *InsnDefining = MA;
574573
if (MemoryDef *DefMUD = dyn_cast<MemoryDef>(InsnDefining)) {
@@ -578,18 +577,10 @@ static MemoryAccess *getNewDefiningAccessForClone(MemoryAccess *MA,
578577
if (Instruction *NewDefMUDI =
579578
cast_or_null<Instruction>(VMap.lookup(DefMUDI))) {
580579
InsnDefining = MSSA->getMemoryAccess(NewDefMUDI);
581-
if (!CloneWasSimplified)
582-
assert(InsnDefining && "Defining instruction cannot be nullptr.");
583-
else if (!InsnDefining || isa<MemoryUse>(InsnDefining)) {
580+
if (!InsnDefining || isa<MemoryUse>(InsnDefining)) {
584581
// The clone was simplified, it's no longer a MemoryDef, look up.
585-
auto DefIt = DefMUD->getDefsIterator();
586-
// Since simplified clones only occur in single block cloning, a
587-
// previous definition must exist, otherwise NewDefMUDI would not
588-
// have been found in VMap.
589-
assert(DefIt != MSSA->getBlockDefs(DefMUD->getBlock())->begin() &&
590-
"Previous def must exist");
591582
InsnDefining = getNewDefiningAccessForClone(
592-
&*(--DefIt), VMap, MPhiMap, CloneWasSimplified, MSSA);
583+
DefMUD->getDefiningAccess(), VMap, MPhiMap, MSSA);
593584
}
594585
}
595586
}
@@ -624,9 +615,9 @@ void MemorySSAUpdater::cloneUsesAndDefs(BasicBlock *BB, BasicBlock *NewBB,
624615
MemoryAccess *NewUseOrDef = MSSA->createDefinedAccess(
625616
NewInsn,
626617
getNewDefiningAccessForClone(MUD->getDefiningAccess(), VMap,
627-
MPhiMap, CloneWasSimplified, MSSA),
618+
MPhiMap, MSSA),
628619
/*Template=*/CloneWasSimplified ? nullptr : MUD,
629-
/*CreationMustSucceed=*/CloneWasSimplified ? false : true);
620+
/*CreationMustSucceed=*/false);
630621
if (NewUseOrDef)
631622
MSSA->insertIntoListsForBlock(NewUseOrDef, NewBB, MemorySSA::End);
632623
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4
2+
; RUN: opt -S -passes="loop-mssa(loop-instsimplify,simple-loop-unswitch<nontrivial>)" < %s | FileCheck %s
3+
4+
@vtable = constant ptr @foo
5+
6+
declare void @foo() memory(none)
7+
declare void @bar()
8+
9+
; The call becomes known readnone after simplification, but still have a
10+
; MemoryAccess. Make sure this does not lead to an assertion failure.
11+
define void @test(i1 %c) {
12+
; CHECK-LABEL: define void @test(
13+
; CHECK-SAME: i1 [[C:%.*]]) {
14+
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
15+
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
16+
; CHECK: .split.us:
17+
; CHECK-NEXT: br label [[LOOP_US:%.*]]
18+
; CHECK: loop.us:
19+
; CHECK-NEXT: call void @foo()
20+
; CHECK-NEXT: br label [[EXIT_SPLIT_US:%.*]]
21+
; CHECK: exit.split.us:
22+
; CHECK-NEXT: br label [[EXIT:%.*]]
23+
; CHECK: .split:
24+
; CHECK-NEXT: br label [[LOOP:%.*]]
25+
; CHECK: loop:
26+
; CHECK-NEXT: call void @foo()
27+
; CHECK-NEXT: br label [[LOOP]]
28+
; CHECK: exit:
29+
; CHECK-NEXT: ret void
30+
;
31+
br label %loop
32+
33+
loop:
34+
%fn = load ptr, ptr @vtable, align 8
35+
call void %fn()
36+
br i1 %c, label %exit, label %loop
37+
38+
exit:
39+
ret void
40+
}
41+
42+
; Variant with another access after the call.
43+
define void @test2(i1 %c, ptr %p) {
44+
; CHECK-LABEL: define void @test2(
45+
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
46+
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
47+
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
48+
; CHECK: .split.us:
49+
; CHECK-NEXT: br label [[LOOP_US:%.*]]
50+
; CHECK: loop.us:
51+
; CHECK-NEXT: call void @foo()
52+
; CHECK-NEXT: call void @bar()
53+
; CHECK-NEXT: br label [[EXIT_SPLIT_US:%.*]]
54+
; CHECK: exit.split.us:
55+
; CHECK-NEXT: br label [[EXIT:%.*]]
56+
; CHECK: .split:
57+
; CHECK-NEXT: br label [[LOOP:%.*]]
58+
; CHECK: loop:
59+
; CHECK-NEXT: call void @foo()
60+
; CHECK-NEXT: call void @bar()
61+
; CHECK-NEXT: br label [[LOOP]]
62+
; CHECK: exit:
63+
; CHECK-NEXT: ret void
64+
;
65+
br label %loop
66+
67+
loop:
68+
%fn = load ptr, ptr @vtable, align 8
69+
call void %fn()
70+
call void @bar()
71+
br i1 %c, label %exit, label %loop
72+
73+
exit:
74+
ret void
75+
}
76+
77+
; Variant with another access after the call and no access before the call.
78+
define void @test3(i1 %c, ptr %p) {
79+
; CHECK-LABEL: define void @test3(
80+
; CHECK-SAME: i1 [[C:%.*]], ptr [[P:%.*]]) {
81+
; CHECK-NEXT: [[C_FR:%.*]] = freeze i1 [[C]]
82+
; CHECK-NEXT: br i1 [[C_FR]], label [[DOTSPLIT_US:%.*]], label [[DOTSPLIT:%.*]]
83+
; CHECK: .split.us:
84+
; CHECK-NEXT: br label [[LOOP_US:%.*]]
85+
; CHECK: loop.us:
86+
; CHECK-NEXT: br label [[SPLIT_US:%.*]]
87+
; CHECK: split.us:
88+
; CHECK-NEXT: call void @foo()
89+
; CHECK-NEXT: call void @bar()
90+
; CHECK-NEXT: br label [[EXIT_SPLIT_US:%.*]]
91+
; CHECK: exit.split.us:
92+
; CHECK-NEXT: br label [[EXIT:%.*]]
93+
; CHECK: .split:
94+
; CHECK-NEXT: br label [[LOOP:%.*]]
95+
; CHECK: loop:
96+
; CHECK-NEXT: br label [[SPLIT:%.*]]
97+
; CHECK: split:
98+
; CHECK-NEXT: call void @foo()
99+
; CHECK-NEXT: call void @bar()
100+
; CHECK-NEXT: br label [[LOOP]]
101+
; CHECK: exit:
102+
; CHECK-NEXT: ret void
103+
;
104+
br label %loop
105+
106+
loop:
107+
%fn = load ptr, ptr @vtable, align 8
108+
br label %split
109+
110+
split:
111+
call void %fn()
112+
call void @bar()
113+
br i1 %c, label %exit, label %loop
114+
115+
exit:
116+
ret void
117+
}

0 commit comments

Comments
 (0)