Skip to content

Commit 5182539

Browse files
committed
[CanonOSSALifetime] Run on lexical lifetimes.
Previously, the utility bailed out on lexical lifetimes because it didn't respect deinit barriers. Here, deinit barriers are found and added to liveness if the value is lexical. This enables copies to be propagated without hoisting destroys over deinit barriers. rdar://104630103
1 parent 4ab4683 commit 5182539

File tree

12 files changed

+112
-23
lines changed

12 files changed

+112
-23
lines changed

include/swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@
109109

110110
namespace swift {
111111

112+
class BasicCalleeAnalysis;
113+
112114
extern llvm::Statistic NumCopiesAndMovesEliminated;
113115
extern llvm::Statistic NumCopiesGenerated;
114116

@@ -225,6 +227,8 @@ class CanonicalizeOSSALifetime final {
225227

226228
DominanceInfo *domTree = nullptr;
227229

230+
BasicCalleeAnalysis *calleeAnalysis;
231+
228232
InstructionDeleter &deleter;
229233

230234
/// The SILValue to canonicalize.
@@ -296,10 +300,12 @@ class CanonicalizeOSSALifetime final {
296300
CanonicalizeOSSALifetime(bool pruneDebugMode, bool maximizeLifetime,
297301
SILFunction *function,
298302
NonLocalAccessBlockAnalysis *accessBlockAnalysis,
299-
DominanceInfo *domTree, InstructionDeleter &deleter)
303+
DominanceInfo *domTree,
304+
BasicCalleeAnalysis *calleeAnalysis,
305+
InstructionDeleter &deleter)
300306
: pruneDebugMode(pruneDebugMode), maximizeLifetime(maximizeLifetime),
301307
accessBlockAnalysis(accessBlockAnalysis), domTree(domTree),
302-
deleter(deleter) {}
308+
calleeAnalysis(calleeAnalysis), deleter(deleter) {}
303309

304310
SILValue getCurrentDef() const { return currentDef; }
305311

@@ -405,6 +411,9 @@ class CanonicalizeOSSALifetime final {
405411
void findExtendedBoundary(PrunedLivenessBoundary const &originalBoundary,
406412
PrunedLivenessBoundary &boundary);
407413

414+
void findDestroysOutsideBoundary(SmallVectorImpl<SILInstruction *> &destroys);
415+
void extendLivenessToDeinitBarriers();
416+
408417
void extendUnconsumedLiveness(PrunedLivenessBoundary const &boundary);
409418

410419
void insertDestroysOnBoundary(PrunedLivenessBoundary const &boundary);

lib/SILOptimizer/Mandatory/MoveOnlyObjectCheckerUtils.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ struct OSSACanonicalizer {
5050
InstructionDeleter &deleter)
5151
: canonicalizer(false /*pruneDebugMode*/,
5252
!fn->shouldOptimize() /*maximizeLifetime*/, fn,
53-
nullptr /*accessBlockAnalysis*/, domTree, deleter) {}
53+
nullptr /*accessBlockAnalysis*/, domTree,
54+
nullptr /*calleeAnalysis*/, deleter) {}
5455

5556
void clear() {
5657
consumingUsesNeedingCopy.clear();

lib/SILOptimizer/SILCombiner/SILCombine.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ SILCombiner::SILCombiner(SILFunctionTransform *trans,
165165
bool removeCondFails, bool enableCopyPropagation) :
166166
parentTransform(trans),
167167
AA(trans->getPassManager()->getAnalysis<AliasAnalysis>(trans->getFunction())),
168+
CA(trans->getPassManager()->getAnalysis<BasicCalleeAnalysis>()),
168169
DA(trans->getPassManager()->getAnalysis<DominanceAnalysis>()),
169170
PCA(trans->getPassManager()->getAnalysis<ProtocolConformanceAnalysis>()),
170171
CHA(trans->getPassManager()->getAnalysis<ClassHierarchyAnalysis>()),
@@ -352,7 +353,7 @@ void SILCombiner::canonicalizeOSSALifetimes(SILInstruction *currentInst) {
352353
CanonicalizeOSSALifetime canonicalizer(
353354
false /*prune debug*/,
354355
!parentTransform->getFunction()->shouldOptimize() /*maximize lifetime*/,
355-
parentTransform->getFunction(), NLABA, domTree, deleter);
356+
parentTransform->getFunction(), NLABA, domTree, CA, deleter);
356357
CanonicalizeBorrowScope borrowCanonicalizer(parentTransform->getFunction(),
357358
deleter);
358359

lib/SILOptimizer/SILCombiner/SILCombiner.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,22 @@
2323

2424
#include "swift/Basic/Defer.h"
2525
#include "swift/SIL/BasicBlockUtils.h"
26+
#include "swift/SIL/InstructionUtils.h"
2627
#include "swift/SIL/SILBuilder.h"
2728
#include "swift/SIL/SILInstruction.h"
2829
#include "swift/SIL/SILInstructionWorklist.h"
2930
#include "swift/SIL/SILValue.h"
3031
#include "swift/SIL/SILVisitor.h"
31-
#include "swift/SIL/InstructionUtils.h"
32+
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
3233
#include "swift/SILOptimizer/Analysis/ClassHierarchyAnalysis.h"
3334
#include "swift/SILOptimizer/Analysis/NonLocalAccessBlockAnalysis.h"
3435
#include "swift/SILOptimizer/Analysis/ProtocolConformanceAnalysis.h"
3536
#include "swift/SILOptimizer/OptimizerBridging.h"
37+
#include "swift/SILOptimizer/PassManager/PassManager.h"
3638
#include "swift/SILOptimizer/Utils/CastOptimizer.h"
3739
#include "swift/SILOptimizer/Utils/Existential.h"
3840
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
3941
#include "swift/SILOptimizer/Utils/OwnershipOptUtils.h"
40-
#include "swift/SILOptimizer/PassManager/PassManager.h"
4142

4243
#include "llvm/ADT/DenseMap.h"
4344
#include "llvm/ADT/SmallVector.h"
@@ -55,6 +56,8 @@ class SILCombiner :
5556

5657
AliasAnalysis *AA;
5758

59+
BasicCalleeAnalysis *CA;
60+
5861
DominanceAnalysis *DA;
5962

6063
/// Determine the set of types a protocol conforms to in whole-module

lib/SILOptimizer/Transforms/CopyPropagation.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@ void CopyPropagation::run() {
432432
auto *postOrderAnalysis = getAnalysis<PostOrderAnalysis>();
433433
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
434434
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
435+
auto *calleeAnalysis = getAnalysis<BasicCalleeAnalysis>();
435436
DominanceInfo *domTree = dominanceAnalysis->get(f);
436437

437438
// Label for unit testing with debug output.
@@ -475,9 +476,7 @@ void CopyPropagation::run() {
475476
// don't need to explicitly check for changes.
476477
CanonicalizeOSSALifetime canonicalizer(
477478
pruneDebug, /*maximizeLifetime=*/!getFunction()->shouldOptimize(),
478-
getFunction(), accessBlockAnalysis, domTree, deleter);
479-
auto *calleeAnalysis = getAnalysis<BasicCalleeAnalysis>();
480-
479+
getFunction(), accessBlockAnalysis, domTree, calleeAnalysis, deleter);
481480
// NOTE: We assume that the function is in reverse post order so visiting the
482481
// blocks and pushing begin_borrows as we see them and then popping them
483482
// off the end will result in shrinking inner borrow scopes first.

lib/SILOptimizer/UtilityPasses/UnitTestRunner.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,13 +421,15 @@ struct CanonicalizeOSSALifetimeTest : UnitTest {
421421
auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
422422
auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
423423
DominanceInfo *domTree = dominanceAnalysis->get(getFunction());
424+
auto *calleeAnalysis = getAnalysis<BasicCalleeAnalysis>();
424425
auto pruneDebug = arguments.takeBool();
425426
auto maximizeLifetimes = arguments.takeBool();
426427
auto respectAccessScopes = arguments.takeBool();
427428
InstructionDeleter deleter;
428429
CanonicalizeOSSALifetime canonicalizer(
429430
pruneDebug, maximizeLifetimes, getFunction(),
430-
respectAccessScopes ? accessBlockAnalysis : nullptr, domTree, deleter);
431+
respectAccessScopes ? accessBlockAnalysis : nullptr, domTree,
432+
calleeAnalysis, deleter);
431433
auto value = arguments.takeValue();
432434
canonicalizer.canonicalizeValueLifetime(value);
433435
getFunction()->dump();
@@ -826,6 +828,7 @@ void UnitTestRunner::withTest(StringRef name, Doit doit) {
826828
ADD_UNIT_TEST_SUBCLASS("interior-liveness", InteriorLivenessTest)
827829
ADD_UNIT_TEST_SUBCLASS("is-deinit-barrier", IsDeinitBarrierTest)
828830
ADD_UNIT_TEST_SUBCLASS("is-lexical", IsLexicalTest)
831+
ADD_UNIT_TEST_SUBCLASS("lexical-destroy-folding", LexicalDestroyFoldingTest)
829832
ADD_UNIT_TEST_SUBCLASS("linear-liveness", LinearLivenessTest)
830833
ADD_UNIT_TEST_SUBCLASS("multidef-liveness", MultiDefLivenessTest)
831834
ADD_UNIT_TEST_SUBCLASS("ossa-lifetime-completion", OSSALifetimeCompletionTest)

lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@
7070
#include "swift/SIL/NodeDatastructures.h"
7171
#include "swift/SIL/OwnershipUtils.h"
7272
#include "swift/SIL/PrunedLiveness.h"
73+
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
74+
#include "swift/SILOptimizer/Analysis/Reachability.h"
7375
#include "swift/SILOptimizer/Utils/CFGOptUtils.h"
7476
#include "swift/SILOptimizer/Utils/DebugOptUtils.h"
7577
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
@@ -222,6 +224,42 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
222224
return true;
223225
}
224226

227+
void CanonicalizeOSSALifetime::findDestroysOutsideBoundary(
228+
SmallVectorImpl<SILInstruction *> &outsideDestroys) {
229+
for (auto destroy : destroys) {
230+
if (liveness->isWithinBoundary(destroy))
231+
continue;
232+
outsideDestroys.push_back(destroy);
233+
}
234+
}
235+
236+
void CanonicalizeOSSALifetime::extendLivenessToDeinitBarriers() {
237+
SmallVector<SILInstruction *, 4> outsideDestroys;
238+
findDestroysOutsideBoundary(outsideDestroys);
239+
240+
auto *def = getCurrentDef()->getDefiningInstruction();
241+
ReachableBarriers barriers;
242+
findBarriersBeforeInstructions(
243+
outsideDestroys, getCurrentDef()->getParentBlock(),
244+
*getCurrentDef()->getFunction(), barriers, [&](auto *inst) {
245+
if (inst == def)
246+
return true;
247+
return isDeinitBarrier(inst, calleeAnalysis);
248+
});
249+
for (auto *barrier : barriers.instructions) {
250+
liveness->updateForUse(barrier, /*lifetimeEnding*/ false);
251+
}
252+
for (auto *barrier : barriers.phis) {
253+
for (auto *predecessor : barrier->getPredecessorBlocks()) {
254+
liveness->updateForUse(predecessor->getTerminator(),
255+
/*lifetimeEnding*/ false);
256+
}
257+
}
258+
// Ignore barriers.edges. The beginning of the targets of such edges should
259+
// not be added to liveness. These edges will be rediscovered when computing
260+
// the liveness boundary.
261+
}
262+
225263
// Return true if \p inst is an end_access whose access scope overlaps the end
226264
// of the pruned live range. This means that a hoisted destroy might execute
227265
// within the access scope which previously executed outside the access scope.
@@ -1007,9 +1045,6 @@ bool CanonicalizeOSSALifetime::computeLiveness() {
10071045
if (currentDef->getOwnershipKind() != OwnershipKind::Owned)
10081046
return false;
10091047

1010-
if (currentDef->isLexical())
1011-
return false;
1012-
10131048
LLVM_DEBUG(llvm::dbgs() << " Canonicalizing: " << currentDef);
10141049

10151050
// Note: There is no need to register callbacks with this utility. 'onDelete'
@@ -1032,6 +1067,9 @@ bool CanonicalizeOSSALifetime::computeLiveness() {
10321067
invalidateLiveness();
10331068
return false;
10341069
}
1070+
if (currentDef->isLexical()) {
1071+
extendLivenessToDeinitBarriers();
1072+
}
10351073
if (accessBlockAnalysis) {
10361074
extendLivenessThroughOverlappingAccess();
10371075
}

test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
class C {}
44
sil @getOwned : $@convention(thin) () -> @owned C
5+
sil @barrier : $@convention(thin) () -> ()
56

67
// When access scopes are respected, the lifetime which previously extended
78
// beyond the access scope still extends beyond it.
@@ -74,3 +75,43 @@ exit(%out : @owned $C):
7475
destroy_value %instance : $C
7576
return %out : $C
7677
}
78+
79+
// CHECK-LABEL: begin running test 1 of 1 on store_arg_to_out_addr: canonicalize-ossa-lifetime with: true, false, true, @trace
80+
// CHECK-LABEL: sil [ossa] @store_arg_to_out_addr : $@convention(thin) (@owned C) -> @out C {
81+
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : $*C, [[INSTANCE:%[^,]+]] :
82+
// CHECK: store [[INSTANCE]] to [init] [[ADDR]]
83+
// CHECK-LABEL: } // end sil function 'store_arg_to_out_addr'
84+
// CHECK-LABEL: end running test 1 of 1 on store_arg_to_out_addr: canonicalize-ossa-lifetime with: true, false, true, @trace
85+
sil [ossa] @store_arg_to_out_addr : $@convention(thin) (@owned C) -> @out C {
86+
bb0(%0 : $*C, %instance : @owned $C):
87+
debug_value [trace] %instance : $C
88+
test_specification "canonicalize-ossa-lifetime true false true @trace"
89+
%copy = copy_value %instance : $C
90+
store %copy to [init] %0 : $*C
91+
destroy_value %instance : $C
92+
%retval = tuple ()
93+
return %retval : $()
94+
}
95+
96+
// CHECK-LABEL: begin running test 1 of 1 on store_arg_to_out_addr_with_barrier: canonicalize-ossa-lifetime with: true, false, true, @trace
97+
// CHECK-LABEL: sil [ossa] @store_arg_to_out_addr_with_barrier : $@convention(thin) (@owned C) -> @out C {
98+
// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : $*C, [[INSTANCE:%[^,]+]] :
99+
// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier
100+
// CHECK: [[REGISTER_3:%[^,]+]] = copy_value [[INSTANCE]]
101+
// CHECK: store [[REGISTER_3]] to [init] [[ADDR]]
102+
// CHECK: apply [[BARRIER]]()
103+
// CHECK: destroy_value [[INSTANCE]]
104+
// CHECK-LABEL: } // end sil function 'store_arg_to_out_addr_with_barrier'
105+
// CHECK-LABEL: end running test 1 of 1 on store_arg_to_out_addr_with_barrier: canonicalize-ossa-lifetime with: true, false, true, @trace
106+
sil [ossa] @store_arg_to_out_addr_with_barrier : $@convention(thin) (@owned C) -> @out C {
107+
bb0(%0 : $*C, %instance : @owned $C):
108+
%barrier = function_ref @barrier : $@convention(thin) () -> ()
109+
debug_value [trace] %instance : $C
110+
test_specification "canonicalize-ossa-lifetime true false true @trace"
111+
%copy = copy_value %instance : $C
112+
store %copy to [init] %0 : $*C
113+
apply %barrier() : $@convention(thin) () -> ()
114+
destroy_value %instance : $C
115+
%retval = tuple ()
116+
return %retval : $()
117+
}

test/SILOptimizer/copy_propagation.sil

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -917,10 +917,10 @@ exit:
917917

918918
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : {{.*}} {
919919
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C
920-
// CHECK: [[COPY:%[^,]+]] = copy_value [[INSTANCE]]
920+
// CHECK-NOT: copy_value
921921
// CHECK: begin_access
922922
// CHECK-NOT: copy_value
923-
// CHECK: store [[COPY]] to [init] {{%[^,]+}}
923+
// CHECK: store [[INSTANCE]] to [init] {{%[^,]+}}
924924
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block'
925925
sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : $@convention(thin) (@owned C) -> () {
926926
bb0(%instance : @owned $C):

test/SILOptimizer/copy_propagation_opaque.sil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,7 @@ bb0:
440440
//
441441
// CHECK-LABEL: sil [ossa] @testCopyBorrow : $@convention(thin) <T> (@in T) -> () {
442442
// CHECK: bb0(%0 : @owned $T):
443-
// CHECK: [[COPY:%[^,]+]] = copy_value %0
444443
// CHECK: destroy_value %0 : $T
445-
// CHECK: destroy_value [[COPY]]
446444
// CHECK-NEXT: tuple
447445
// CHECK-NEXT: return
448446
// CHECK-LABEL: } // end sil function 'testCopyBorrow'

test/SILOptimizer/lexical_destroy_hoisting.sil

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,8 @@ exit:
332332
//
333333
// CHECK-LABEL: sil [ossa] @dont_hoist_over_apply_at_copy : {{.*}} {
334334
// CHECK: {{bb[0-9]+}}([[REGISTER_0:%[^,]+]] :
335-
// CHECK: [[COPY:%[^,]+]] = copy_value [[INSTANCE]]
336335
// CHECK: apply
337336
// CHECK: destroy_value [[INSTANCE]]
338-
// CHECK: destroy_value [[COPY]]
339337
// CHECK-LABEL: } // end sil function 'dont_hoist_over_apply_at_copy'
340338
sil [ossa] @dont_hoist_over_apply_at_copy : $@convention(thin) (@owned C) -> () {
341339
entry(%instance : @owned $C):

test/SILOptimizer/ossa_rauw_tests.sil

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,23 +189,21 @@ bb0(%0 : @owned $Klass):
189189
// CHECK-NOT: destroy_value
190190
//
191191
// CHECK: bb2:
192-
// CHECK-NEXT: [[COPY:%.*]] = copy_value [[ARG]]
193192
// CHECK-NEXT: cond_br undef, bb3, bb4
194193
//
195194
// CHECK: bb3:
196-
// CHECK-NEXT: destroy_value [[COPY]]
197195
// CHECK-NEXT: br bb2
198196
//
199197
// CHECK: bb4:
200-
// CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, [[COPY]]
198+
// CHECK-NEXT: [[ENUM_SOME_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.some!enumelt, [[ARG]]
201199
// CHECK-NEXT: br bb6([[ENUM_SOME_RESULT]] : $FakeOptional<Klass>)
202200
//
203201
// CHECK: bb5:
202+
// CHECK-NEXT: destroy_value [[ARG]]
204203
// CHECK-NEXT: [[ENUM_NONE_RESULT:%.*]] = enum $FakeOptional<Klass>, #FakeOptional.none!enumelt
205204
// CHECK-NEXT: br bb6([[ENUM_NONE_RESULT]] :
206205
//
207206
// CHECK: bb6([[RESULT:%.*]] : @owned $FakeOptional<Klass>):
208-
// CHECK-NEXT: destroy_value [[ARG]]
209207
// CHECK-NEXT: return [[RESULT]]
210208
// CHECK: } // end sil function 'unowned_to_owned_rauw_loop'
211209
sil [ossa] @unowned_to_owned_rauw_loop : $@convention(thin) (@owned Klass) -> @owned FakeOptional<Klass> {

0 commit comments

Comments
 (0)