Skip to content

Commit 6e88541

Browse files
Merge pull request #65171 from nate-chandler/cherrypick/release/5.9/rdar108014714
5.9: [SemanticARCOpts] Don't shorten owned lexical values lifetimes through deinit barriers.
2 parents 6776feb + 807035d commit 6e88541

File tree

4 files changed

+83
-0
lines changed

4 files changed

+83
-0
lines changed

lib/SIL/Utils/MemAccessUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,7 @@ void swift::visitAccessedAddress(SILInstruction *I,
26902690
case SILInstructionKind::CopyBlockInst:
26912691
case SILInstructionKind::CopyBlockWithoutEscapingInst:
26922692
case SILInstructionKind::CopyValueInst:
2693+
case SILInstructionKind::DebugStepInst:
26932694
case SILInstructionKind::DeinitExistentialAddrInst:
26942695
case SILInstructionKind::DeinitExistentialValueInst:
26952696
case SILInstructionKind::DestroyAddrInst:

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/deinit_barrier.sil

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,15 @@ sil [ossa] @test_hop_to_executor : $@convention(thin) () -> () {
103103
%retval = tuple ()
104104
return %retval : $()
105105
}
106+
107+
// CHECK-LABEL: begin running test 1 of 1 on test_instructions_1: is-deinit-barrier
108+
// CHECK: debug_step
109+
// CHECK: false
110+
// CHECK-LABEL: end running test 1 of 1 on test_instructions_1: is-deinit-barrier
111+
sil [ossa] @test_instructions_1 : $@convention(thin) () -> () {
112+
entry:
113+
test_specification "is-deinit-barrier @instruction"
114+
debug_step
115+
%retval = tuple ()
116+
return %retval : $()
117+
}

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)