Skip to content

Commit 581010d

Browse files
committed
SIL: Make drop_deinit a ForwardingInstruction.
drop_deinit forwards ownership while effectively stripping the deinitializer. It is similar to a type cast. Fixes rdar://125590074 ([NonescapableTypes] Nonescapable types cannot have deinits)
1 parent bac1dc9 commit 581010d

File tree

8 files changed

+102
-8
lines changed

8 files changed

+102
-8
lines changed

SwiftCompilerSources/Sources/SIL/ForwardingInstruction.swift

+6
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ extension UncheckedValueCastInst : ConversionInstruction {
316316
public var canForwardOwnedValues: Bool { true }
317317
}
318318

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

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
@@ -8679,6 +8679,11 @@ class DestroyValueInst
86798679
}
86808680

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

87558760
DropDeinitInst(SILDebugLocation DebugLoc, SILValue operand)
8756-
: UnaryInstructionBase(DebugLoc, operand, operand->getType()) {}
8761+
: UnaryInstructionBase(DebugLoc, operand, operand->getType(),
8762+
OwnershipKind::Owned) {}
87578763
};
87588764

87598765
/// Equivalent to a copy_addr to [init] except that it is used for diagnostics
@@ -11134,6 +11140,7 @@ OwnershipForwardingSingleValueInstruction::classof(SILInstructionKind kind) {
1113411140
case SILInstructionKind::ThinToThickFunctionInst:
1113511141
case SILInstructionKind::UnconditionalCheckedCastInst:
1113611142
case SILInstructionKind::FunctionExtractIsolationInst:
11143+
case SILInstructionKind::DropDeinitInst:
1113711144
return true;
1113811145
default:
1113911146
return false;

lib/SIL/IR/SILInstruction.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,10 @@ bool MarkDependenceInst::visitNonEscapingLifetimeEnds(
18851885
return !noUsers;
18861886
}
18871887

1888+
bool DestroyValueInst::isFullDeinitialization() {
1889+
return !isa<DropDeinitInst>(lookThroughOwnershipInsts(getOperand()));
1890+
}
1891+
18881892
PartialApplyInst *
18891893
DestroyValueInst::getNonescapingClosureAllocation() const {
18901894
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_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)