Skip to content

Commit 0a7ff09

Browse files
committed
[Coroutines] Don't transform cmpinst prematurely in simplifyTerminatorLeadingToRet
Previously, we would try to transform cmpinst in simplifyTerminatorLeadingToRet if we found it was a constant. However, this is incorrect. Since the resolved constants in simplifyTerminatorLeadingToRet are not truely constants. They are basically constants along cerntain code paths. In this way, it is clearly incorrect to transform the compare instruction to a constant. It will cause confusing miscompilations. This patch tries to fix this.
1 parent 19b1d3b commit 0a7ff09

File tree

2 files changed

+116
-34
lines changed

2 files changed

+116
-34
lines changed

llvm/lib/Transforms/Coroutines/CoroSplit.cpp

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,8 +1249,11 @@ scanPHIsAndUpdateValueMap(Instruction *Prev, BasicBlock *NewBlock,
12491249
// instruction. Suspend instruction represented by a switch, track the PHI
12501250
// values and select the correct case successor when possible.
12511251
static bool simplifyTerminatorLeadingToRet(Instruction *InitialInst) {
1252+
// There is nothing to simplify.
1253+
if (isa<ReturnInst>(InitialInst))
1254+
return false;
1255+
12521256
DenseMap<Value *, Value *> ResolvedValues;
1253-
BasicBlock *UnconditionalSucc = nullptr;
12541257
assert(InitialInst->getModule());
12551258
const DataLayout &DL = InitialInst->getModule()->getDataLayout();
12561259

@@ -1280,39 +1283,35 @@ static bool simplifyTerminatorLeadingToRet(Instruction *InitialInst) {
12801283
Instruction *I = InitialInst;
12811284
while (I->isTerminator() || isa<CmpInst>(I)) {
12821285
if (isa<ReturnInst>(I)) {
1283-
if (I != InitialInst) {
1284-
// If InitialInst is an unconditional branch,
1285-
// remove PHI values that come from basic block of InitialInst
1286-
if (UnconditionalSucc)
1287-
UnconditionalSucc->removePredecessor(InitialInst->getParent(), true);
1288-
ReplaceInstWithInst(InitialInst, I->clone());
1289-
}
1286+
ReplaceInstWithInst(InitialInst, I->clone());
12901287
return true;
12911288
}
1289+
12921290
if (auto *BR = dyn_cast<BranchInst>(I)) {
1293-
if (BR->isUnconditional()) {
1294-
BasicBlock *Succ = BR->getSuccessor(0);
1295-
if (I == InitialInst)
1296-
UnconditionalSucc = Succ;
1297-
scanPHIsAndUpdateValueMap(I, Succ, ResolvedValues);
1298-
I = GetFirstValidInstruction(Succ->getFirstNonPHIOrDbgOrLifetime());
1299-
continue;
1291+
unsigned SuccIndex = 0;
1292+
if (BR->isConditional()) {
1293+
// Handle the case the condition of the conditional branch is constant.
1294+
// e.g.,
1295+
//
1296+
// br i1 false, label %cleanup, label %CoroEnd
1297+
//
1298+
// It is possible during the transformation. We could continue the
1299+
// simplifying in this case.
1300+
ConstantInt *Cond = TryResolveConstant(BR->getCondition());
1301+
if (!Cond)
1302+
return false;
1303+
1304+
SuccIndex = Cond->isOne() ? 0 : 1;
13001305
}
13011306

1302-
BasicBlock *BB = BR->getParent();
1303-
// Handle the case the condition of the conditional branch is constant.
1304-
// e.g.,
1305-
//
1306-
// br i1 false, label %cleanup, label %CoroEnd
1307-
//
1308-
// It is possible during the transformation. We could continue the
1309-
// simplifying in this case.
1310-
if (ConstantFoldTerminator(BB, /*DeleteDeadConditions=*/true)) {
1311-
// Handle this branch in next iteration.
1312-
I = BB->getTerminator();
1313-
continue;
1314-
}
1315-
} else if (auto *CondCmp = dyn_cast<CmpInst>(I)) {
1307+
BasicBlock *Succ = BR->getSuccessor(SuccIndex);
1308+
scanPHIsAndUpdateValueMap(I, Succ, ResolvedValues);
1309+
I = GetFirstValidInstruction(Succ->getFirstNonPHIOrDbgOrLifetime());
1310+
1311+
continue;
1312+
}
1313+
1314+
if (auto *CondCmp = dyn_cast<CmpInst>(I)) {
13161315
// If the case number of suspended switch instruction is reduced to
13171316
// 1, then it is simplified to CmpInst in llvm::ConstantFoldTerminator.
13181317
auto *BR = dyn_cast<BranchInst>(
@@ -1336,13 +1335,14 @@ static bool simplifyTerminatorLeadingToRet(Instruction *InitialInst) {
13361335
if (!ConstResult)
13371336
return false;
13381337

1339-
CondCmp->replaceAllUsesWith(ConstResult);
1340-
CondCmp->eraseFromParent();
1338+
ResolvedValues[BR->getCondition()] = ConstResult;
13411339

13421340
// Handle this branch in next iteration.
13431341
I = BR;
13441342
continue;
1345-
} else if (auto *SI = dyn_cast<SwitchInst>(I)) {
1343+
}
1344+
1345+
if (auto *SI = dyn_cast<SwitchInst>(I)) {
13461346
ConstantInt *Cond = TryResolveConstant(SI->getCondition());
13471347
if (!Cond)
13481348
return false;
@@ -1352,9 +1352,8 @@ static bool simplifyTerminatorLeadingToRet(Instruction *InitialInst) {
13521352
I = GetFirstValidInstruction(BB->getFirstNonPHIOrDbgOrLifetime());
13531353
continue;
13541354
}
1355-
1356-
return false;
13571355
}
1356+
13581357
return false;
13591358
}
13601359

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
; Tests that coro-split won't convert the cmp instruction prematurely.
2+
; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
3+
4+
declare void @fakeresume1(ptr)
5+
declare void @print()
6+
7+
define void @f(i1 %cond) #0 {
8+
entry:
9+
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
10+
%alloc = call ptr @malloc(i64 16) #3
11+
%vFrame = call noalias nonnull ptr @llvm.coro.begin(token %id, ptr %alloc)
12+
13+
%save = call token @llvm.coro.save(ptr null)
14+
15+
%init_suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
16+
switch i8 %init_suspend, label %coro.end [
17+
i8 0, label %await.ready
18+
i8 1, label %coro.end
19+
]
20+
await.ready:
21+
%save2 = call token @llvm.coro.save(ptr null)
22+
br i1 %cond, label %then, label %else
23+
24+
then:
25+
call fastcc void @fakeresume1(ptr align 8 null)
26+
br label %merge
27+
28+
else:
29+
br label %merge
30+
31+
merge:
32+
%v0 = phi i1 [0, %then], [1, %else]
33+
br label %compare
34+
35+
compare:
36+
%cond.cmp = icmp eq i1 %v0, 0
37+
br i1 %cond.cmp, label %ready, label %prepare
38+
39+
prepare:
40+
call void @print()
41+
br label %ready
42+
43+
ready:
44+
%suspend = call i8 @llvm.coro.suspend(token %save2, i1 true)
45+
%switch = icmp ult i8 %suspend, 2
46+
br i1 %switch, label %cleanup, label %coro.end
47+
48+
cleanup:
49+
%free.handle = call ptr @llvm.coro.free(token %id, ptr %vFrame)
50+
%.not = icmp eq ptr %free.handle, null
51+
br i1 %.not, label %coro.end, label %coro.free
52+
53+
coro.free:
54+
call void @delete(ptr nonnull %free.handle) #2
55+
br label %coro.end
56+
57+
coro.end:
58+
call i1 @llvm.coro.end(ptr null, i1 false)
59+
ret void
60+
}
61+
62+
; CHECK-LABEL: @f.resume(
63+
; CHECK-NOT: }
64+
; CHECK: call void @print()
65+
66+
67+
declare token @llvm.coro.id(i32, ptr readnone, ptr nocapture readonly, ptr) #1
68+
declare i1 @llvm.coro.alloc(token) #2
69+
declare i64 @llvm.coro.size.i64() #3
70+
declare ptr @llvm.coro.begin(token, ptr writeonly) #2
71+
declare token @llvm.coro.save(ptr) #2
72+
declare ptr @llvm.coro.frame() #3
73+
declare i8 @llvm.coro.suspend(token, i1) #2
74+
declare ptr @llvm.coro.free(token, ptr nocapture readonly) #1
75+
declare i1 @llvm.coro.end(ptr, i1) #2
76+
declare ptr @llvm.coro.subfn.addr(ptr nocapture readonly, i8) #1
77+
declare ptr @malloc(i64)
78+
declare void @delete(ptr nonnull) #2
79+
80+
attributes #0 = { presplitcoroutine }
81+
attributes #1 = { argmemonly nounwind readonly }
82+
attributes #2 = { nounwind }
83+
attributes #3 = { nounwind readnone }

0 commit comments

Comments
 (0)