Skip to content

Commit 00dac23

Browse files
authored
Merge pull request #72839 from atrick/60-fix-lifedep-throw
[6.0] LifetimeDependence: handle dependent values in throwing calls.
2 parents 5d7ae8a + 2e3e85d commit 00dac23

13 files changed

+137
-24
lines changed

SwiftCompilerSources/Sources/Optimizer/DataStructures/InstructionRange.swift

+1-3
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,8 @@ struct InstructionRange : CustomStringConvertible, NoReflectionChildren {
6464
}
6565

6666
static func beginningInstruction(for value: Value) -> Instruction {
67-
if let def = value.definingInstruction {
67+
if let def = value.definingInstructionOrTerminator {
6868
return def
69-
} else if let result = TerminatorResult(value) {
70-
return result.terminator
7169
}
7270
assert(Phi(value) != nil || value is FunctionArgument)
7371
return value.parentBlock.instructions.first!

SwiftCompilerSources/Sources/Optimizer/FunctionPasses/LifetimeDependenceInsertion.swift

+2-4
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,13 @@ extension LifetimeDependentApply {
105105
/// dependent value within each scope.
106106
private func insertDependencies(for apply: LifetimeDependentApply,
107107
_ context: FunctionPassContext ) {
108-
precondition(apply.applySite.results.count > 0,
109-
"a lifetime-dependent instruction must have at least one result")
110-
111108
let bases = findDependenceBases(of: apply, context)
112-
let builder = Builder(after: apply.applySite, context)
113109
for dependentValue in apply.applySite.resultOrYields {
110+
let builder = Builder(before: dependentValue.nextInstruction, context)
114111
insertMarkDependencies(value: dependentValue, initializer: nil,
115112
bases: bases, builder: builder, context)
116113
}
114+
let builder = Builder(after: apply.applySite, context)
117115
for resultOper in apply.applySite.indirectResultOperands {
118116
let accessBase = resultOper.value.accessBase
119117
guard let (initialAddress, initializingStore) =

SwiftCompilerSources/Sources/Optimizer/Utilities/LifetimeDependenceUtils.swift

+6-5
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ extension LifetimeDependence {
222222
if value.isEscapable {
223223
return nil
224224
}
225-
if (value.definingInstruction as! FullApplySite).hasResultDependence {
225+
if (value.definingInstructionOrTerminator as! FullApplySite).hasResultDependence {
226226
return nil
227227
}
228228
assert(value.ownership == .owned, "apply result must be owned")
@@ -766,11 +766,12 @@ extension LifetimeDependenceUseDefWalker {
766766

767767
/// Walk down dependent values.
768768
///
769-
/// Delegates value def-use walking to the ForwardingUseDefWalker and
770-
/// overrides copy, move, borrow, and mark_dependence.
769+
/// First classifies all values using OwnershipUseVisitor. Delegates forwarding uses to the ForwardingUseDefWalker.
770+
/// Transitively follows OwnershipTransitionInstructions (copy, move, borrow, and mark_dependence). Transitively
771+
/// follows interior pointers using AddressUseVisitor. Handles stores to and loads from local variables using
772+
/// LocalVariableReachabilityCache.
771773
///
772-
/// Ignores trivial values (~Escapable types are never
773-
/// trivial. Escapable types may only be lifetime-depenent values if
774+
/// Ignores trivial values (~Escapable types are never trivial. Escapable types may only be lifetime-depenent values if
774775
/// they are non-trivial).
775776
///
776777
/// Skips uses within nested borrow scopes.

SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,9 @@ extension Value {
6666
// If this value is produced by a ForwardingInstruction, return that instruction. This is convenient for following the forwarded value chain.
6767
// Unlike definingInstruction, a value's forwardingInstruction is not necessarily a valid insertion point.
6868
public var forwardingInstruction: ForwardingInstruction? {
69-
if let inst = definingInstruction {
69+
if let inst = definingInstructionOrTerminator {
7070
return inst as? ForwardingInstruction
7171
}
72-
if let termResult = TerminatorResult(self) {
73-
return termResult.terminator as? ForwardingInstruction
74-
}
7572
return nil
7673
}
7774
}
@@ -319,6 +316,12 @@ extension UncheckedValueCastInst : ConversionInstruction {
319316
public var canForwardOwnedValues: Bool { true }
320317
}
321318

319+
extension DropDeinitInst : ConversionInstruction {
320+
public var preservesRepresentation: Bool { true }
321+
public var canForwardGuaranteedValues: Bool { false }
322+
public var canForwardOwnedValues: Bool { true }
323+
}
324+
322325
// -----------------------------------------------------------------------------
323326
// other forwarding instructions
324327

SwiftCompilerSources/Sources/SIL/Value.swift

+9
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,15 @@ extension Value {
138138
}
139139
}
140140

141+
public var definingInstructionOrTerminator: Instruction? {
142+
if let def = definingInstruction {
143+
return def
144+
} else if let result = TerminatorResult(self) {
145+
return result.terminator
146+
}
147+
return nil
148+
}
149+
141150
public var nextInstruction: Instruction {
142151
if self is Argument {
143152
return parentBlock.instructions.first!

include/swift/SIL/InstWrappers.h

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ struct ConversionOperation {
140140
case SILInstructionKind::CopyableToMoveOnlyWrapperValueInst:
141141
case SILInstructionKind::MoveOnlyWrapperToCopyableValueInst:
142142
case SILInstructionKind::MoveOnlyWrapperToCopyableBoxInst:
143+
case SILInstructionKind::DropDeinitInst:
143144
return true;
144145
default:
145146
return false;

include/swift/SIL/SILInstruction.h

+9-2
Original file line numberDiff line numberDiff line change
@@ -8678,6 +8678,11 @@ class DestroyValueInst
86788678
}
86798679

86808680
public:
8681+
/// True if this destroy fully deinitializes the type by invoking the
8682+
/// user-defined deinitializer if present. This returns false if a prior
8683+
/// drop_deinit is present.
8684+
bool isFullDeinitialization();
8685+
86818686
/// If true, then all references within the destroyed value will be
86828687
/// overwritten with a sentinel. This is used in debug builds when shortening
86838688
/// non-trivial value lifetimes to ensure the debugger cannot inspect invalid
@@ -8748,11 +8753,12 @@ class MoveValueInst
87488753
/// for details. See SILVerifier.cpp for constraints on valid uses.
87498754
class DropDeinitInst
87508755
: public UnaryInstructionBase<SILInstructionKind::DropDeinitInst,
8751-
SingleValueInstruction> {
8756+
OwnershipForwardingSingleValueInstruction> {
87528757
friend class SILBuilder;
87538758

87548759
DropDeinitInst(SILDebugLocation DebugLoc, SILValue operand)
8755-
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
8760+
: UnaryInstructionBase(DebugLoc, operand, operand->getType(),
8761+
OwnershipKind::Owned) {}
87568762
};
87578763

87588764
/// Equivalent to a copy_addr to [init] except that it is used for diagnostics
@@ -11133,6 +11139,7 @@ OwnershipForwardingSingleValueInstruction::classof(SILInstructionKind kind) {
1113311139
case SILInstructionKind::ThinToThickFunctionInst:
1113411140
case SILInstructionKind::UnconditionalCheckedCastInst:
1113511141
case SILInstructionKind::FunctionExtractIsolationInst:
11142+
case SILInstructionKind::DropDeinitInst:
1113611143
return true;
1113711144
default:
1113811145
return false;

lib/SIL/IR/SILInstruction.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1882,6 +1882,10 @@ bool MarkDependenceInst::visitNonEscapingLifetimeEnds(
18821882
return !noUsers;
18831883
}
18841884

1885+
bool DestroyValueInst::isFullDeinitialization() {
1886+
return !isa<DropDeinitInst>(lookThroughOwnershipInsts(getOperand()));
1887+
}
1888+
18851889
PartialApplyInst *
18861890
DestroyValueInst::getNonescapingClosureAllocation() const {
18871891
SILValue operand = getOperand();

lib/SILOptimizer/Utils/CanonicalizeInstruction.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -547,10 +547,13 @@ static SILBasicBlock::iterator
547547
eliminateUnneededForwardingUnarySingleValueInst(SingleValueInstruction *inst,
548548
CanonicalizeInstruction &pass) {
549549
auto next = std::next(inst->getIterator());
550-
551-
for (auto *use : getNonDebugUses(inst))
552-
if (!isa<DestroyValueInst>(use->getUser()))
553-
return next;
550+
for (auto *use : getNonDebugUses(inst)) {
551+
if (auto *destroy = dyn_cast<DestroyValueInst>(use->getUser())) {
552+
if (destroy->isFullDeinitialization())
553+
continue;
554+
}
555+
return next;
556+
}
554557
deleteAllDebugUses(inst, pass.callbacks);
555558
SILValue op = inst->getOperand(0);
556559
inst->replaceAllUsesWith(op);

test/SILOptimizer/lifetime_dependence_diagnostics.swift

+16
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,19 @@ func bv_borrow_borrow(bv: borrowing BV) -> dependsOn(scoped bv) BV {
8080
func ncint_capture(ncInt: inout NCInt) {
8181
takeClosure { _ = ncInt.i }
8282
}
83+
84+
func neint_throws(ncInt: borrowing NCInt) throws -> NEInt {
85+
return NEInt(owner: ncInt)
86+
}
87+
88+
// CHECK-LABEL: sil hidden @$s4test9neint_try5ncIntAA5NEIntVAA5NCIntVYls_tKF : $@convention(thin) (@guaranteed NCInt) -> _scope(1) (@owned NEInt, @error any Error) {
89+
// CHECK: try_apply %{{.*}}(%0) : $@convention(thin) (@guaranteed NCInt) -> _scope(1) (@owned NEInt, @error any Error), normal bb1, error bb2
90+
// CHECK: bb1([[R:%.*]] : $NEInt):
91+
// CHECK: [[MD:%.*]] = mark_dependence [nonescaping] %5 : $NEInt on %0 : $NCInt
92+
// CHECK: return [[MD]] : $NEInt
93+
// CHECK: bb2([[E:%.*]] : $any Error):
94+
// CHECK: throw [[E]] : $any Error
95+
// CHECK-LABEL: } // end sil function '$s4test9neint_try5ncIntAA5NEIntVAA5NCIntVYls_tKF'
96+
func neint_try(ncInt: borrowing NCInt) throws -> NEInt {
97+
try neint_throws(ncInt: ncInt)
98+
}

test/SILOptimizer/lifetime_dependence_util.sil

+22-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@ sil_stage canonical
1111

1212
import Builtin
1313

14-
@_marker public protocol Escapable {}
14+
@_marker protocol Copyable: ~Escapable {}
15+
@_marker protocol Escapable: ~Copyable {}
16+
17+
struct NCNEInt: ~Copyable, ~Escapable {
18+
var i: Builtin.Int64
19+
@_unsafeNonescapableResult
20+
init() {}
21+
deinit {}
22+
}
1523

1624
enum FakeOptional<T> {
1725
case none
@@ -324,3 +332,16 @@ bb0(%0 : @owned $NE):
324332
%99 = tuple()
325333
return %99 : $()
326334
}
335+
336+
// CHECK-LABEL: begin running test 1 of 1 on testInteriorDropDeinit: lifetime_dependence_use with: %0
337+
// CHECK: LifetimeDependence uses of: %0 = argument of bb0 : $NCNEInt
338+
// CHECK-NEXT: Leaf use: operand #0 of destroy_value
339+
// CHECK-LABEL: end running test 1 of 1 on testInteriorDropDeinit: lifetime_dependence_use with: %0
340+
sil [ossa] @testInteriorDropDeinit : $@convention(thin) (@owned NCNEInt) -> () {
341+
bb0(%0 : @owned $NCNEInt):
342+
specify_test "lifetime_dependence_use %0"
343+
%nd = drop_deinit %0 : $NCNEInt
344+
destroy_value %nd : $NCNEInt
345+
%99 = tuple()
346+
return %99 : $()
347+
}

test/SILOptimizer/ownership_liveness_unit.sil

+40-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ sil_stage canonical
1010

1111
import Builtin
1212

13+
struct NCInt : ~Copyable {
14+
var i: Builtin.Int64
15+
deinit {}
16+
}
17+
1318
enum FakeOptional<T> {
1419
case none
1520
case some(T)
@@ -243,7 +248,7 @@ exit(%reborrow : @guaranteed $C, %phi : @guaranteed $D):
243248
}
244249

245250
// =============================================================================
246-
// InteriorLiveness and visitAdjacentPhis
251+
// InteriorLiveness
247252
// =============================================================================
248253

249254
// CHECK-LABEL: testInteriorRefElementEscape: interior-liveness with: %0
@@ -312,6 +317,40 @@ bb0(%0 : $*D, %1 : @guaranteed $D):
312317
return %99 : $()
313318
}
314319

320+
// CHECK-LABEL: begin running test 1 of 2 on testInteriorDropDeinit: interior-liveness with: %0
321+
// CHECK: Interior liveness: %0 = argument of bb0 : $NCInt
322+
// CHECK-NEXT: bb0: LiveWithin
323+
// CHECK-NEXT: lifetime-ending user: [[DD:%.*]] = drop_deinit %0 : $NCInt
324+
// CHECK-NEXT: Complete liveness
325+
// CHECK-NEXT: Unenclosed phis {
326+
// CHECK-NEXT: }
327+
// CHECK-NEXT: last user: [[DD]] = drop_deinit %0 : $NCInt
328+
// CHECK-LABEL: end running test 1 of 2 on testInteriorDropDeinit: interior-liveness with: %0
329+
330+
// CHECK-LABEL: begin running test 2 of 2 on testInteriorDropDeinit: interior_liveness_swift with: %0
331+
// CHECK: Interior liveness: %0 = argument of bb0 : $NCInt
332+
// CHECK-NEXT: begin: [[DD:%.*]] = drop_deinit %0 : $NCInt
333+
// CHECK-NEXT: ends: [[DD]] = drop_deinit %0 : $NCInt
334+
// CHECK-NEXT: exits:
335+
// CHECK-NEXT: interiors:
336+
// CHECK-NEXT: Unenclosed phis {
337+
// CHECK-NEXT: }
338+
// CHECK-NEXT: last user: [[DD]] = drop_deinit %0 : $NCInt
339+
// CHECK-LABEL: end running test 2 of 2 on testInteriorDropDeinit: interior_liveness_swift with: %0
340+
sil [ossa] @testInteriorDropDeinit : $@convention(thin) (@owned NCInt) -> () {
341+
bb0(%0 : @owned $NCInt):
342+
specify_test "interior-liveness %0"
343+
specify_test "interior_liveness_swift %0"
344+
%nd = drop_deinit %0 : $NCInt
345+
destroy_value %nd : $NCInt
346+
%99 = tuple()
347+
return %99 : $()
348+
}
349+
350+
// =============================================================================
351+
// InteriorLiveness and visitAdjacentPhis
352+
// =============================================================================
353+
315354
// CHECK-LABEL: testInteriorReborrow: interior-liveness with: %borrow
316355
// CHECK: Complete liveness
317356
// CHECK-NEXT: Unenclosed phis {

test/SILOptimizer/sil_combine_moveonly.sil

+13
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,16 @@ bb0(%0 : $*T, %1 : $*Wrapper<T>):
9595
%9 = tuple ()
9696
return %9 : $()
9797
}
98+
99+
// CHECK-LABEL: sil hidden [ossa] @testSDeinit : $@convention(method) (@owned S) -> () {
100+
// CHECK: bb0(%0 : @owned $S):
101+
// CHECK: [[DD:%.*]] = drop_deinit %0 : $S
102+
// CHECK: destroy_value [[DD]] : $S
103+
// CHECK-LABEL: } // end sil function 'testSDeinit'
104+
sil hidden [ossa] @testSDeinit : $@convention(method) (@owned S) -> () {
105+
bb0(%0 : @owned $S):
106+
%1 = drop_deinit %0 : $S
107+
destroy_value %1 : $S
108+
%64 = tuple ()
109+
return %64 : $()
110+
}

0 commit comments

Comments
 (0)