Skip to content

Commit a4ac481

Browse files
committed
[SemanticARCOpts] Keep owned lexical lifetimes.
Prevent joining lifetimes of copies with destroyed owned lexical values if there may be deinit barriers between the final consume of the copy and the destroy of the lexical value. rdar://108014714
1 parent 153c9d2 commit a4ac481

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

lib/SILOptimizer/SemanticARC/CopyValueOpts.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "SemanticARCOptVisitor.h"
2525
#include "swift/Basic/Defer.h"
2626
#include "swift/SIL/LinearLifetimeChecker.h"
27+
#include "swift/SIL/MemAccessUtils.h"
2728
#include "swift/SIL/OwnershipUtils.h"
2829
#include "swift/SIL/Projection.h"
2930

@@ -392,6 +393,20 @@ static bool tryJoinIfDestroyConsumingUseInSameBlock(
392393
return true;
393394
}
394395

396+
// The lifetime of the original ends after the lifetime of the copy. If the
397+
// original is lexical, its lifetime must not be shortened through deinit
398+
// barriers.
399+
if (cvi->getOperand()->isLexical()) {
400+
// At this point, visitedInsts contains all the instructions between the
401+
// consuming use of the copy and the destroy. If any of those instructions
402+
// is a deinit barrier, it would be illegal to shorten the original lexical
403+
// value's lifetime to end at that consuming use. Bail if any are.
404+
if (llvm::any_of(visitedInsts, [](auto *inst) {
405+
return mayBeDeinitBarrierNotConsideringSideEffects(inst);
406+
}))
407+
return false;
408+
}
409+
395410
// If we reached this point, isUseBetweenInstAndBlockEnd succeeded implying
396411
// that we found destroy_value to be after our consuming use. Noting that
397412
// additionally, the routine places all instructions in between consuming use

test/SILOptimizer/semantic-arc-opts-lifetime-joining.sil

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ struct NativeObjectWrapper {
116116

117117
sil @owned_user_object_pair : $@convention(thin) (@owned NativeObjectPair) -> ()
118118

119+
class X {}
120+
struct S {}
121+
122+
sil @getX : $@convention(thin) () -> @owned X
123+
sil @getS : $@convention(thin) (@owned X) -> @out S
124+
sil @loadWeakX_from : $@convention(thin) (@in_guaranteed S) -> @owned FakeOptional<X>
125+
119126
///////////
120127
// Tests //
121128
///////////
@@ -923,3 +930,51 @@ bb0:
923930
destroy_value %0 : ${ var Bool }
924931
return %11 : $Bool
925932
}
933+
934+
// Don't do this optimization:
935+
// Eliminate copy of lexical value which ends after lifetime of copy IF there
936+
// may be deinit barriers between the final consume and the copy.
937+
//
938+
// CHECK-LABEL: sil [ossa] @testDestroyedLexicalValue : {{.*}} {
939+
// CHECK: [[GET:%[^,]+]] = function_ref @getX
940+
// CHECK: [[X:%[^,]+]] = apply [[GET]]()
941+
// CHECK: [[MX:%[^,]+]] = move_value [lexical] [[X]]
942+
// CHECK: destroy_value [[MX]] : $X
943+
// CHECK-LABEL: } // end sil function 'testDestroyedLexicalValue'
944+
sil [ossa] @testDestroyedLexicalValue : $@convention(thin) () -> @owned FakeOptional<X> {
945+
bb0:
946+
%getX = function_ref @getX : $@convention(thin) () -> @owned X
947+
%x = apply %getX() : $@convention(thin) () -> @owned X
948+
%mx = move_value [lexical] %x : $X
949+
%a = alloc_stack [lexical] $S, let, name "s"
950+
%c = copy_value %mx : $X
951+
%getS = function_ref @getS : $@convention(thin) (@owned X) -> @out S
952+
%s = apply %getS(%a, %c) : $@convention(thin) (@owned X) -> @out S
953+
%loadWeakX_from = function_ref @loadWeakX_from : $@convention(thin) (@in_guaranteed S) -> @owned FakeOptional<X>
954+
%o = apply %loadWeakX_from(%a) : $@convention(thin) (@in_guaranteed S) -> @owned FakeOptional<X>
955+
// ^^^^^ Deinit barrier
956+
destroy_addr %a : $*S
957+
dealloc_stack %a : $*S
958+
destroy_value %mx : $X
959+
return %o : $FakeOptional<X>
960+
}
961+
962+
// CHECK-LABEL: sil [ossa] @testDestroyedLexicalValueNoBarriers : {{.*}} {
963+
// CHECK-NOT: destroy_value
964+
// CHECK-LABEL: } // end sil function 'testDestroyedLexicalValueNoBarriers'
965+
sil [ossa] @testDestroyedLexicalValueNoBarriers : $@convention(thin) () -> () {
966+
bb0:
967+
%getX = function_ref @getX : $@convention(thin) () -> @owned X
968+
%x = apply %getX() : $@convention(thin) () -> @owned X
969+
%mx = move_value [lexical] %x : $X
970+
%a = alloc_stack [lexical] $S, let, name "s"
971+
%c = copy_value %mx : $X
972+
%getS = function_ref @getS : $@convention(thin) (@owned X) -> @out S
973+
%s = apply %getS(%a, %c) : $@convention(thin) (@owned X) -> @out S
974+
destroy_addr %a : $*S
975+
dealloc_stack %a : $*S
976+
destroy_value %mx : $X
977+
%r = tuple ()
978+
return %r : $()
979+
}
980+

0 commit comments

Comments
 (0)