Skip to content

Commit 7152364

Browse files
committed
Fix logic related to isTriviallyDuplicatable.
In SILInstruction::isTriviallyDuplicatable(): - Make deallocating instructions trivially duplicatable. They are by any useful definition--duplicating an instruction does not imply reordering it. Tail duplication was already treating deallocations as duplicatable, but doing it inconsistently. Sometimes it checks isTriviallyDuplicatable, and sometimes it doesn't, which appears to have been an accident. Disallowing duplication of deallocations will cause severe performance regressions. Instead, consistently allow them to be duplicated, making tail duplication more powerful, which could expose other bugs. - Do not duplicate on-stack AllocRefInst (without special consideration). This is a correctness fix that apparently was never exposed. Fix SILLoop::canDuplicate(): - Handle isDeallocatingStack. It's not clear how we were avoiding an assertion before when a stack allocatable reference was confined to a loop--probably just by luck. - Handle begin/end_access inside a loop. This is extremely important and probably prevented many loop optimizations from working with exclusivity. Update LoopRotate canDuplicateOrMoveToPreheader(). This is NFC.
1 parent cd3ada5 commit 7152364

File tree

4 files changed

+27
-20
lines changed

4 files changed

+27
-20
lines changed

lib/SIL/LoopInfo.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ bool SILLoop::canDuplicate(SILInstruction *I) const {
4747
}
4848
return true;
4949
}
50+
if (I->isDeallocatingStack()) {
51+
SILInstruction *Alloc = nullptr;
52+
if (auto *Dealloc = dyn_cast<DeallocStackInst>(I))
53+
Alloc = dyn_cast<AllocStackInst>(Dealloc->getOperand());
54+
if (auto *Dealloc = dyn_cast<DeallocRefInst>(I))
55+
Alloc = dyn_cast<AllocRefInst>(Dealloc->getOperand());
56+
// The matching alloc_stack must be in the loop.
57+
return Alloc && contains(Alloc);
58+
}
5059

5160
// CodeGen can't build ssa for objc methods.
5261
if (auto *Method = dyn_cast<MethodInst>(I)) {
@@ -71,13 +80,17 @@ bool SILLoop::canDuplicate(SILInstruction *I) const {
7180
return true;
7281
}
7382

74-
if (auto *Dealloc = dyn_cast<DeallocStackInst>(I)) {
75-
// The matching alloc_stack must be in the loop.
76-
if (auto *Alloc = dyn_cast<AllocStackInst>(Dealloc->getOperand()))
77-
return contains(Alloc->getParent());
83+
if (isa<ThrowInst>(I))
7884
return false;
79-
}
8085

86+
// The entire access must be within the loop.
87+
if (auto BAI = dyn_cast<BeginAccessInst>(I)) {
88+
for (auto *UI : BAI->getUses()) {
89+
if (!contains(UI->getUser()))
90+
return false;
91+
}
92+
return true;
93+
}
8194
// The entire coroutine execution must be within the loop.
8295
// Note that we don't have to worry about the reverse --- a loop which
8396
// contains an end_apply or abort_apply of an external begin_apply ---
@@ -92,18 +105,11 @@ bool SILLoop::canDuplicate(SILInstruction *I) const {
92105
return true;
93106
}
94107

95-
if (isa<ThrowInst>(I))
96-
return false;
97-
98-
if (isa<BeginAccessInst>(I))
99-
return false;
100-
101108
if (isa<DynamicMethodBranchInst>(I))
102109
return false;
103110

104-
if (auto *PA = dyn_cast<PartialApplyInst>(I))
105-
return !PA->isOnStack();
106-
111+
// Some special cases above that aren't considered isTriviallyDuplicatable
112+
// return true early.
107113
assert(I->isTriviallyDuplicatable() &&
108114
"Code here must match isTriviallyDuplicatable in SILInstruction");
109115
return true;

lib/SIL/SILInstruction.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,9 +1172,9 @@ SILInstruction *SILInstruction::clone(SILInstruction *InsertPt) {
11721172
/// additional handling. It is important to know this information when
11731173
/// you perform such optimizations like e.g. jump-threading.
11741174
bool SILInstruction::isTriviallyDuplicatable() const {
1175-
if (isa<AllocStackInst>(this) || isa<DeallocStackInst>(this)) {
1175+
if (isAllocatingStack())
11761176
return false;
1177-
}
1177+
11781178
if (auto *ARI = dyn_cast<AllocRefInst>(this)) {
11791179
if (ARI->canAllocOnStack())
11801180
return false;
@@ -1213,9 +1213,6 @@ bool SILInstruction::isTriviallyDuplicatable() const {
12131213
if (isa<DynamicMethodBranchInst>(this))
12141214
return false;
12151215

1216-
if (auto *PA = dyn_cast<PartialApplyInst>(this))
1217-
return !PA->isOnStack();
1218-
12191216
// If you add more cases here, you should also update SILLoop:canDuplicate.
12201217

12211218
return true;

lib/SILOptimizer/LoopTransforms/LoopRotate.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ canDuplicateOrMoveToPreheader(SILLoop *loop, SILBasicBlock *preheader,
7070
invariants.insert(inst);
7171
} else if (!inst->isTriviallyDuplicatable())
7272
return false;
73+
// It wouldn't make sense to rotate dealloc_stack without also rotating the
74+
// alloc_stack, which is covered by isTriviallyDuplicatable.
75+
else if (isa<DeallocStackInst>(inst))
76+
return false;
7377
else if (isa<FunctionRefInst>(inst)) {
7478
moves.push_back(inst);
7579
invariants.insert(inst);

lib/SILOptimizer/Transforms/SimplifyCFG.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ static NullablePtr<EnumElementDecl> getEnumCase(SILValue Val,
783783
}
784784

785785
static int getThreadingCost(SILInstruction *I) {
786-
if (!isa<DeallocStackInst>(I) && !I->isTriviallyDuplicatable())
786+
if (!I->isTriviallyDuplicatable())
787787
return 1000;
788788

789789
// Don't jumpthread function calls.

0 commit comments

Comments
 (0)