Skip to content

Commit 9081edd

Browse files
committed
Optimizer: fix an ownership violation when duplicating loops
If a guaranteed value is used in a dead-end exit block and the enclosing value is _not_ destroyed in this block, we end up missing the enclosing value as phi-argument after duplicating the loop. TODO: once we have complete lifetimes we can remove this check again. rdar://159125605
1 parent 5f20a5b commit 9081edd

File tree

5 files changed

+62
-11
lines changed

5 files changed

+62
-11
lines changed

include/swift/SILOptimizer/Utils/LoopUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class SILInstruction;
2828
class SILLoop;
2929
class DominanceInfo;
3030
class SILLoopInfo;
31+
class DeadEndBlocks;
3132

3233
/// Canonicalize the loop for rotation and downstream passes.
3334
///
@@ -40,7 +41,7 @@ bool canonicalizeAllLoops(DominanceInfo *DT, SILLoopInfo *LI);
4041

4142
/// Check whether it is safe to duplicate this instruction when duplicating
4243
/// this loop by unrolling or versioning.
43-
bool canDuplicateLoopInstruction(SILLoop *L, SILInstruction *Inst);
44+
bool canDuplicateLoopInstruction(SILLoop *L, SILInstruction *Inst, DeadEndBlocks *deb);
4445

4546
/// A visitor that visits loops in a function in a bottom up order. It only
4647
/// performs the visit.

lib/SILOptimizer/LoopTransforms/ArrayPropertyOpt.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "swift/SIL/Projection.h"
6262
#include "swift/SIL/SILCloner.h"
6363
#include "swift/SILOptimizer/Analysis/ArraySemantic.h"
64+
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
6465
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
6566
#include "swift/SILOptimizer/PassManager/Transforms.h"
6667
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
@@ -87,6 +88,7 @@ class ArrayPropertiesAnalysis {
8788
SILLoop *Loop;
8889
SILBasicBlock *Preheader;
8990
DominanceInfo *DomTree;
91+
DeadEndBlocks *deb;
9092

9193
SinkAddressProjections sinkProj;
9294

@@ -104,9 +106,9 @@ class ArrayPropertiesAnalysis {
104106
bool reachingBlocksComputed = false;
105107

106108
public:
107-
ArrayPropertiesAnalysis(SILLoop *L, DominanceAnalysis *DA)
109+
ArrayPropertiesAnalysis(SILLoop *L, DominanceAnalysis *DA, DeadEndBlocks *deb)
108110
: Fun(L->getHeader()->getParent()), Loop(L), Preheader(nullptr),
109-
DomTree(DA->get(Fun)), ReachingBlocks(Fun) {}
111+
DomTree(DA->get(Fun)), deb(deb), ReachingBlocks(Fun) {}
110112

111113
/// Check if it is profitable to specialize a loop when you see an apply
112114
/// instruction. We consider it is not profitable to specialize the loop when:
@@ -178,7 +180,7 @@ class ArrayPropertiesAnalysis {
178180
for (auto &Inst : *BB) {
179181
// Can't clone alloc_stack instructions whose dealloc_stack is outside
180182
// the loop.
181-
if (!canDuplicateLoopInstruction(Loop, &Inst))
183+
if (!canDuplicateLoopInstruction(Loop, &Inst, deb))
182184
return false;
183185

184186
if (!sinkProj.analyzeAddressProjections(&Inst)) {
@@ -796,6 +798,7 @@ class SwiftArrayPropertyOptPass : public SILFunctionTransform {
796798
return;
797799

798800
DominanceAnalysis *DA = PM->getAnalysis<DominanceAnalysis>();
801+
DeadEndBlocks *deb = PM->getAnalysis<DeadEndBlocksAnalysis>()->get(Fn);
799802
SILLoopAnalysis *LA = PM->getAnalysis<SILLoopAnalysis>();
800803
SILLoopInfo *LI = LA->get(Fn);
801804

@@ -808,7 +811,7 @@ class SwiftArrayPropertyOptPass : public SILFunctionTransform {
808811
// possible loop-nest.
809812
SmallVector<SILBasicBlock *, 16> HoistableLoopNests;
810813
std::function<void(SILLoop *)> processChildren = [&](SILLoop *L) {
811-
ArrayPropertiesAnalysis Analysis(L, DA);
814+
ArrayPropertiesAnalysis Analysis(L, DA, deb);
812815
if (Analysis.run()) {
813816
// Hoist in the current loop nest.
814817
HasChanged = true;

lib/SILOptimizer/LoopTransforms/LoopUnroll.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/SIL/SILCloner.h"
2020
#include "swift/SILOptimizer/Analysis/LoopAnalysis.h"
2121
#include "swift/SILOptimizer/Analysis/IsSelfRecursiveAnalysis.h"
22+
#include "swift/SILOptimizer/Analysis/DeadEndBlocksAnalysis.h"
2223
#include "swift/SILOptimizer/PassManager/Passes.h"
2324
#include "swift/SILOptimizer/PassManager/Transforms.h"
2425
#include "swift/SILOptimizer/Utils/BasicBlockOptUtils.h"
@@ -211,7 +212,8 @@ static std::optional<uint64_t> getMaxLoopTripCount(SILLoop *Loop,
211212
/// heuristic that looks at the trip count and the cost of the instructions in
212213
/// the loop to determine whether we should unroll this loop.
213214
static bool canAndShouldUnrollLoop(SILLoop *Loop, uint64_t TripCount,
214-
IsSelfRecursiveAnalysis *SRA) {
215+
IsSelfRecursiveAnalysis *SRA,
216+
DeadEndBlocks *deb) {
215217
assert(Loop->getSubLoops().empty() && "Expect innermost loops");
216218
if (TripCount > 32)
217219
return false;
@@ -227,7 +229,7 @@ static bool canAndShouldUnrollLoop(SILLoop *Loop, uint64_t TripCount,
227229
(Loop->getBlocks())[0]->getParent()->getModule().getOptions().UnrollThreshold;
228230
for (auto *BB : Loop->getBlocks()) {
229231
for (auto &Inst : *BB) {
230-
if (!canDuplicateLoopInstruction(Loop, &Inst))
232+
if (!canDuplicateLoopInstruction(Loop, &Inst, deb))
231233
return false;
232234
if (instructionInlineCost(Inst) != InlineCost::Free)
233235
++Cost;
@@ -388,7 +390,7 @@ updateSSA(SILFunction *Fn, SILLoop *Loop,
388390

389391
/// Try to fully unroll the loop if we can determine the trip count and the trip
390392
/// count is below a threshold.
391-
static bool tryToUnrollLoop(SILLoop *Loop, IsSelfRecursiveAnalysis *SRA) {
393+
static bool tryToUnrollLoop(SILLoop *Loop, IsSelfRecursiveAnalysis *SRA, DeadEndBlocks *deb) {
392394
assert(Loop->getSubLoops().empty() && "Expecting innermost loops");
393395

394396
LLVM_DEBUG(llvm::dbgs() << "Trying to unroll loop : \n" << *Loop);
@@ -409,7 +411,7 @@ static bool tryToUnrollLoop(SILLoop *Loop, IsSelfRecursiveAnalysis *SRA) {
409411
return false;
410412
}
411413

412-
if (!canAndShouldUnrollLoop(Loop, MaxTripCount.value(), SRA)) {
414+
if (!canAndShouldUnrollLoop(Loop, MaxTripCount.value(), SRA, deb)) {
413415
LLVM_DEBUG(llvm::dbgs() << "Not unrolling, exceeds cost threshold\n");
414416
return false;
415417
}
@@ -490,6 +492,7 @@ class LoopUnrolling : public SILFunctionTransform {
490492
auto *Fun = getFunction();
491493
SILLoopInfo *LoopInfo = PM->getAnalysis<SILLoopAnalysis>()->get(Fun);
492494
IsSelfRecursiveAnalysis *SRA = PM->getAnalysis<IsSelfRecursiveAnalysis>();
495+
DeadEndBlocks *deb = PM->getAnalysis<DeadEndBlocksAnalysis>()->get(Fun);
493496

494497
LLVM_DEBUG(llvm::dbgs() << "Loop Unroll running on function : "
495498
<< Fun->getName() << "\n");
@@ -517,7 +520,7 @@ class LoopUnrolling : public SILFunctionTransform {
517520

518521
// Try to unroll innermost loops.
519522
for (auto *Loop : InnermostLoops)
520-
Changed |= tryToUnrollLoop(Loop, SRA);
523+
Changed |= tryToUnrollLoop(Loop, SRA, deb);
521524

522525
if (Changed) {
523526
invalidateAnalysis(SILAnalysis::InvalidationKind::FunctionBody);

lib/SILOptimizer/Utils/LoopUtils.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,21 @@ bool swift::canonicalizeAllLoops(DominanceInfo *DT, SILLoopInfo *LI) {
218218
return MadeChange;
219219
}
220220

221-
bool swift::canDuplicateLoopInstruction(SILLoop *L, SILInstruction *I) {
221+
bool swift::canDuplicateLoopInstruction(SILLoop *L, SILInstruction *I, DeadEndBlocks *deb) {
222222
SinkAddressProjections sinkProj;
223223
for (auto res : I->getResults()) {
224+
// If a guaranteed value is used in a dead-end exit block and the enclosing value
225+
// is _not_ destroyed in this block, we end up missing the enclosing value as
226+
// phi-argument after duplicating the loop.
227+
// TODO: once we have complete lifetimes we can remove this check
228+
if (res->getOwnershipKind() == OwnershipKind::Guaranteed) {
229+
for (Operand *use : res->getUses()) {
230+
SILBasicBlock *useBlock = use->getUser()->getParent();
231+
if (!L->contains(useBlock) && deb->isDeadEnd(useBlock))
232+
return false;
233+
}
234+
}
235+
224236
if (!res->getType().isAddress()) {
225237
continue;
226238
}

test/SILOptimizer/array_property_opt_ossa_guaranteed.sil

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,38 @@ bb3:
7676
return %4 : $MyBool
7777
}
7878

79+
// Check that we don't crash with an ownership violation
80+
// CHECK-LABEL: sil [ossa] @borrow_use_in_dead_end :
81+
// CHECK-LABEL: } // end sil function 'borrow_use_in_dead_end'
82+
sil [ossa] @borrow_use_in_dead_end : $@convention(thin) (@guaranteed MyArray<MyClass>) -> () {
83+
bb0(%0 : @guaranteed $MyArray<MyClass>):
84+
br bb1
85+
86+
bb1:
87+
%2 = function_ref @arrayPropertyIsNative : $@convention(method) (@guaranteed MyArray<MyClass>) -> Bool
88+
%5 = apply %2(%0) : $@convention(method) (@guaranteed MyArray<MyClass>) -> Bool
89+
%10 = alloc_ref $MyClass
90+
%11 = begin_borrow %10
91+
cond_br undef, bb2, bb3
92+
93+
bb2:
94+
fix_lifetime %11
95+
unreachable
96+
97+
bb3:
98+
end_borrow %11
99+
destroy_value %10
100+
cond_br undef, bb4, bb5
101+
102+
bb4:
103+
br bb1
104+
105+
bb5:
106+
%r = tuple ()
107+
return %r
108+
}
109+
110+
79111
// CHECK-LABEL: sil [ossa] @load_and_copy_within_loop :
80112
// CHECK: bb1:
81113
// CHECK: [[FUNC1:%.*]] = function_ref @arrayPropertyIsNative

0 commit comments

Comments
 (0)