Skip to content

Commit cd04e9e

Browse files
Merge pull request #60792 from nate-chandler/canonicalize_ossa_lifetime/access_scope_ends
[CopyProp] Don't extend lifetime over end_access.
2 parents b6377ff + 477461b commit cd04e9e

File tree

3 files changed

+95
-19
lines changed

3 files changed

+95
-19
lines changed

include/swift/SILOptimizer/Utils/CanonicalOSSALifetime.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ class CanonicalizeOSSALifetime {
298298
/// ending". end_borrows do not end a liverange that may include owned copies.
299299
PrunedLiveness liveness;
300300

301+
/// The destroys of the value. These are not uses, but need to be recorded so
302+
/// that we know when the last use in a consuming block is (without having to
303+
/// repeatedly do use-def walks from destroys).
304+
SmallPtrSet<SILInstruction *, 8> destroys;
305+
301306
/// remnantLiveOutBlocks are part of the original extended lifetime that are
302307
/// not in canonical pruned liveness. There is a path from a PrunedLiveness
303308
/// boundary to an original destroy that passes through a remnant block.

lib/SILOptimizer/Utils/CanonicalOSSALifetime.cpp

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,10 @@ bool CanonicalizeOSSALifetime::computeCanonicalLiveness() {
162162
liveness.updateForUse(user, /*lifetimeEnding*/ true);
163163
break;
164164
case OperandOwnership::DestroyingConsume:
165-
// destroy_value does not force pruned liveness (but store etc. does).
166-
if (!isa<DestroyValueInst>(user)) {
165+
if (isa<DestroyValueInst>(user)) {
166+
destroys.insert(user);
167+
} else {
168+
// destroy_value does not force pruned liveness (but store etc. does).
167169
liveness.updateForUse(user, /*lifetimeEnding*/ true);
168170
}
169171
recordConsumingUse(use);
@@ -335,18 +337,33 @@ void CanonicalizeOSSALifetime::extendLivenessThroughOverlappingAccess() {
335337
bool changed = true;
336338
while (changed) {
337339
changed = false;
338-
blockWorklist.initializeRange(consumingBlocks);
339-
while (auto *bb = blockWorklist.pop()) {
340+
// The blocks in which we may have to extend liveness over access scopes.
341+
//
342+
// It must be populated first so that we can test membership during the loop
343+
// (see findLastConsume).
344+
BasicBlockSetVector blocksToVisit(currentDef->getFunction());
345+
for (auto *block : consumingBlocks) {
346+
blocksToVisit.insert(block);
347+
}
348+
for (auto iterator = blocksToVisit.begin(); iterator != blocksToVisit.end();
349+
++iterator) {
350+
auto *bb = *iterator;
351+
// If the block isn't dead, then we won't need to extend liveness within
352+
// any of its predecessors (though we may within it).
353+
if (liveness.getBlockLiveness(bb) != PrunedLiveBlocks::Dead)
354+
continue;
355+
// Continue searching upward to find the pruned liveness boundary.
356+
for (auto *predBB : bb->getPredecessorBlocks()) {
357+
blocksToVisit.insert(predBB);
358+
}
359+
}
360+
for (auto *bb : blocksToVisit) {
340361
auto blockLiveness = liveness.getBlockLiveness(bb);
341362
// Ignore blocks within pruned liveness.
342363
if (blockLiveness == PrunedLiveBlocks::LiveOut) {
343364
continue;
344365
}
345366
if (blockLiveness == PrunedLiveBlocks::Dead) {
346-
// Continue searching upward to find the pruned liveness boundary.
347-
for (auto *predBB : bb->getPredecessorBlocks()) {
348-
blockWorklist.insert(predBB);
349-
}
350367
// Otherwise, ignore dead blocks with no nonlocal end_access.
351368
if (!accessBlocks->containsNonLocalEndAccess(bb)) {
352369
continue;
@@ -356,7 +373,23 @@ void CanonicalizeOSSALifetime::extendLivenessThroughOverlappingAccess() {
356373
// Find the latest partially overlapping access scope, if one exists:
357374
// use %def // pruned liveness ends here
358375
// end_access
376+
377+
// Whether to look for the last consume in the block.
378+
//
379+
// We need to avoid extending liveness over end_accesses that occur after
380+
// original liveness ended.
381+
bool findLastConsume =
382+
consumingBlocks.contains(bb) &&
383+
llvm::none_of(bb->getSuccessorBlocks(), [&](auto *successor) {
384+
return blocksToVisit.contains(successor) &&
385+
liveness.getBlockLiveness(successor) ==
386+
PrunedLiveBlocks::Dead;
387+
});
359388
for (auto &inst : llvm::reverse(*bb)) {
389+
if (findLastConsume) {
390+
findLastConsume = !destroys.contains(&inst);
391+
continue;
392+
}
360393
// Stop at the latest use. An earlier end_access does not overlap.
361394
if (blockHasUse && liveness.isInterestingUser(&inst)) {
362395
break;

test/SILOptimizer/copy_propagation.sil

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -616,24 +616,21 @@ bb2:
616616

617617
// Original Overlapping (unnecessarilly extends pruned liveness):
618618
//
619-
// TODO: this copy could be avoided but is probably an unusual case,
620-
// and sinking the destroy outside the access scope might help to
621-
// optimize the access itself.
622-
//
623619
// %def
624620
// begin_access // access scope unrelated to def
625621
// use %def // pruned liveness ends here
626622
// destroy %def
627623
// end_access
628624
//
629625
// CHECK-LABEL: sil [ossa] @testOriginalOverlapInLiveBlock : $@convention(thin) () -> () {
630-
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
631-
// CHECK: begin_access
632-
// CHECK: [[COPY:%.*]] = copy_value [[DEF]] : $AnyObject
633-
// CHECK: store [[COPY]] to [init] %{{.*}} : $*AnyObject
634-
// CHECK: end_access
635-
// CHECK: destroy_value [[DEF]] : $AnyObject
636-
// CHECK: br bb1
626+
// CHECK: [[DEF:%.*]] = apply %{{.*}}() : $@convention(thin) () -> @owned AnyObject
627+
// CHECK: begin_access
628+
// CHECK-OPT: store [[DEF]] to [init] %{{.*}} : $*AnyObject
629+
// CHECK-ONONE: [[COPY:%[^,]+]] = copy_value [[DEF]]
630+
// CHECK-ONONE: store [[COPY]] to [init] %{{.*}} : $*AnyObject
631+
// CHECK-ONONE: destroy_value [[DEF]]
632+
// CHECK: end_access
633+
// CHECK: br bb1
637634
// CHECK-LABEL: } // end sil function 'testOriginalOverlapInLiveBlock'
638635
sil [ossa] @testOriginalOverlapInLiveBlock : $@convention(thin) () -> () {
639636
bb0:
@@ -901,3 +898,44 @@ exit:
901898
%retval = tuple ()
902899
return %retval : $()
903900
}
901+
902+
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : {{.*}} {
903+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $C
904+
// CHECK: [[COPY:%[^,]+]] = copy_value [[INSTANCE]]
905+
// CHECK: begin_access
906+
// CHECK-NOT: copy_value
907+
// CHECK: store [[COPY]] to [init] {{%[^,]+}}
908+
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block'
909+
sil [ossa] @dont_extend_beyond_nonoverlapping_end_access_after_store_in_consuming_block : $@convention(thin) (@owned C) -> () {
910+
bb0(%instance : @owned $C):
911+
%addr = alloc_stack [lexical] $C, var
912+
%borrow = begin_borrow %instance : $C
913+
%copy = copy_value %borrow : $C
914+
end_borrow %borrow : $C
915+
destroy_value %instance : $C
916+
%access = begin_access [static] [modify] %addr : $*C
917+
store %copy to [init] %addr : $*C
918+
end_access %access : $*C
919+
destroy_addr %addr : $*C
920+
dealloc_stack %addr : $*C
921+
%retval = tuple()
922+
return %retval : $()
923+
}
924+
925+
// CHECK-LABEL: sil [ossa] @dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block : $@convention(thin) () -> () {
926+
// CHECK: begin_access
927+
// CHECK: destroy_value
928+
// CHECK: end_access
929+
// CHECK-NOT: destroy_value
930+
// CHECK-LABEL: } // end sil function 'dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block'
931+
sil [ossa] @dont_extend_beyond_nonoverlapping_endaccess_after_destroyvalue_in_consuming_block : $@convention(thin) () -> () {
932+
%instance = apply undef() : $@convention(thin) () -> @owned C
933+
%addr = alloc_stack [lexical] $C, var
934+
%access = begin_access [static] [modify] %addr : $*C
935+
apply undef(%instance) : $@convention(thin) (@guaranteed C) -> ()
936+
destroy_value %instance : $C
937+
end_access %access : $*C
938+
dealloc_stack %addr : $*C
939+
%retval = tuple()
940+
return %retval : $()
941+
}

0 commit comments

Comments
 (0)