Skip to content

Commit cbcea38

Browse files
authored
Merge pull request #7858 from gottesmm/end_lifetime_and_deallocating_deinits
2 parents df8efc2 + f92693a commit cbcea38

File tree

19 files changed

+115
-11
lines changed

19 files changed

+115
-11
lines changed

include/swift/SIL/SILBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1280,6 +1280,11 @@ class SILBuilder {
12801280
getSILDebugLocation(Loc), Operand, atomicity));
12811281
}
12821282

1283+
EndLifetimeInst *createEndLifetime(SILLocation Loc, SILValue Operand) {
1284+
return insert(new (F.getModule())
1285+
EndLifetimeInst(getSILDebugLocation(Loc), Operand));
1286+
}
1287+
12831288
FixLifetimeInst *createFixLifetime(SILLocation Loc, SILValue Operand) {
12841289
return insert(new (F.getModule())
12851290
FixLifetimeInst(getSILDebugLocation(Loc), Operand));

include/swift/SIL/SILCloner.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,9 +1700,16 @@ SILCloner<ImplClass>::visitFixLifetimeInst(FixLifetimeInst *Inst) {
17001700
getOpValue(Inst->getOperand())));
17011701
}
17021702

1703-
template<typename ImplClass>
1704-
void
1705-
SILCloner<ImplClass>::visitMarkDependenceInst(MarkDependenceInst *Inst) {
1703+
template <typename ImplClass>
1704+
void SILCloner<ImplClass>::visitEndLifetimeInst(EndLifetimeInst *Inst) {
1705+
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1706+
doPostProcess(Inst,
1707+
getBuilder().createEndLifetime(getOpLocation(Inst->getLoc()),
1708+
getOpValue(Inst->getOperand())));
1709+
}
1710+
1711+
template <typename ImplClass>
1712+
void SILCloner<ImplClass>::visitMarkDependenceInst(MarkDependenceInst *Inst) {
17061713
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
17071714
doPostProcess(Inst,
17081715
getBuilder().createMarkDependence(getOpLocation(Inst->getLoc()),

include/swift/SIL/SILInstruction.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4337,6 +4337,28 @@ class FixLifetimeInst :
43374337
: UnaryInstructionBase(DebugLoc, Operand) {}
43384338
};
43394339

4340+
/// EndLifetimeInst - An artificial end lifetime use of a value for the purpose
4341+
/// of working around verification problems.
4342+
///
4343+
/// Specifically, the signature of destroying deinit takes self at +0 and
4344+
/// returns self at +1. This is an issue since a deallocating deinit takes in
4345+
/// self at +1. Previously, we could rely on the deallocating bit being set in
4346+
/// the object header to allow SILGen to statically balance the +1 from the
4347+
/// deallocating deinit. This is because deallocating values used to be
4348+
/// immortal. The runtime now asserts if we release a deallocating value,
4349+
/// meaning such an approach does not work. This instruction acts as a "fake"
4350+
/// lifetime ending use allowing for static verification of deallocating
4351+
/// destroyers, without an actual release being emitted (avoiding the runtime
4352+
/// assert).
4353+
class EndLifetimeInst
4354+
: public UnaryInstructionBase<ValueKind::EndLifetimeInst, SILInstruction,
4355+
/*HAS_RESULT*/ false> {
4356+
friend SILBuilder;
4357+
4358+
EndLifetimeInst(SILDebugLocation DebugLoc, SILValue Operand)
4359+
: UnaryInstructionBase(DebugLoc, Operand) {}
4360+
};
4361+
43404362
/// MarkDependenceInst - Marks that one value depends on another for
43414363
/// validity in a non-obvious way.
43424364
class MarkDependenceInst : public SILInstruction {

include/swift/SIL/SILNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ ABSTRACT_VALUE(SILInstruction, ValueBase)
164164
INST(UnmanagedAutoreleaseValueInst, RefCountingInst, unmanaged_autorelease_value, MayHaveSideEffects, DoesNotRelease)
165165
INST(CopyUnownedValueInst, SILInstruction, copy_unowned_value, MayHaveSideEffects, DoesNotRelease)
166166
INST(DestroyValueInst, SILInstruction, destroy_value, MayHaveSideEffects, MayRelease)
167+
INST(EndLifetimeInst, SILInstruction, end_lifetime, MayHaveSideEffects, MayRelease)
167168

168169
// IsUnique does not actually write to memory but should be modeled
169170
// as such. Its operand is a pointer to an object reference. The

lib/IRGen/IRGenSIL.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,9 @@ class IRGenSILFunction :
898898
void visitInitBlockStorageHeaderInst(InitBlockStorageHeaderInst *i);
899899

900900
void visitFixLifetimeInst(FixLifetimeInst *i);
901+
void visitEndLifetimeInst(EndLifetimeInst *i) {
902+
llvm_unreachable("unimplemented");
903+
}
901904
void visitBeginBorrowInst(BeginBorrowInst *i) {
902905
llvm_unreachable("unimplemented");
903906
}

lib/Parse/ParseSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,6 +2124,7 @@ bool SILParser::parseSILInstruction(SILBasicBlock *BB, SILBuilder &B) {
21242124
} break;
21252125

21262126
UNARY_INSTRUCTION(FixLifetime)
2127+
UNARY_INSTRUCTION(EndLifetime)
21272128
UNARY_INSTRUCTION(CopyBlock)
21282129
UNARY_INSTRUCTION(IsUnique)
21292130
UNARY_INSTRUCTION(IsUniqueOrPinned)

lib/SIL/SILOwnershipVerifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ CONSTANT_OWNERSHIP_INST(Owned, true, StrongUnpin)
383383
CONSTANT_OWNERSHIP_INST(Owned, true, UnownedRelease)
384384
CONSTANT_OWNERSHIP_INST(Owned, true, InitExistentialRef)
385385
CONSTANT_OWNERSHIP_INST(Owned, true, OpenExistentialOpaque)
386+
CONSTANT_OWNERSHIP_INST(Owned, true, EndLifetime)
386387
CONSTANT_OWNERSHIP_INST(Trivial, false, AddressToPointer)
387388
CONSTANT_OWNERSHIP_INST(Trivial, false, BindMemory)
388389
CONSTANT_OWNERSHIP_INST(Trivial, false, CheckedCastAddrBranch)

lib/SIL/SILPrinter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,11 @@ class SILPrinter : public SILVisitor<SILPrinter> {
15491549
void visitFixLifetimeInst(FixLifetimeInst *RI) {
15501550
*this << getIDAndType(RI->getOperand());
15511551
}
1552+
1553+
void visitEndLifetimeInst(EndLifetimeInst *ELI) {
1554+
*this << getIDAndType(ELI->getOperand());
1555+
}
1556+
15521557
void visitMarkDependenceInst(MarkDependenceInst *MDI) {
15531558
*this << getIDAndType(MDI->getValue()) << " on "
15541559
<< getIDAndType(MDI->getBase());

lib/SIL/SILValue.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ NO_RESULT_OWNERSHIP_INST(InjectEnumAddr)
333333
NO_RESULT_OWNERSHIP_INST(DeinitExistentialAddr)
334334
NO_RESULT_OWNERSHIP_INST(DeinitExistentialOpaque)
335335
NO_RESULT_OWNERSHIP_INST(CondFail)
336+
NO_RESULT_OWNERSHIP_INST(EndLifetime)
336337

337338
// Terminators. These do not produce SILValue, so they do not have a
338339
// ValueOwnershipKind. They do have ownership implications in terms of the

lib/SILGen/SILGenDestructor.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,29 +85,45 @@ void SILGenFunction::emitDeallocatingDestructor(DestructorDecl *dd) {
8585
loc.markAutoGenerated();
8686

8787
// Emit the prolog.
88-
SILValue selfValue = emitSelfDecl(dd->getImplicitSelfDecl());
88+
SILValue initialSelfValue = emitSelfDecl(dd->getImplicitSelfDecl());
8989

9090
// Form a reference to the destroying destructor.
9191
SILDeclRef dtorConstant(dd, SILDeclRef::Kind::Destroyer);
92-
auto classTy = selfValue->getType();
92+
auto classTy = initialSelfValue->getType();
9393
ManagedValue dtorValue;
9494
SILType dtorTy;
9595
SubstitutionList subs = classTy.gatherAllSubstitutions(SGM.M);
9696
std::tie(dtorValue, dtorTy, subs)
97-
= emitSiblingMethodRef(loc, selfValue, dtorConstant, subs);
97+
= emitSiblingMethodRef(loc, initialSelfValue, dtorConstant, subs);
9898

9999
// Call the destroying destructor.
100+
SILValue selfForDealloc;
100101
{
101102
FullExpr CleanupScope(Cleanups, CleanupLocation::get(loc));
102-
ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, selfValue);
103+
ManagedValue borrowedSelf = emitManagedBeginBorrow(loc, initialSelfValue);
103104
SILType objectPtrTy = SILType::getNativeObjectType(F.getASTContext());
104-
selfValue = B.createApply(loc, dtorValue.forward(*this),
105-
dtorTy, objectPtrTy, subs, borrowedSelf.getUnmanagedValue());
105+
selfForDealloc = B.createApply(loc, dtorValue.forward(*this),
106+
dtorTy, objectPtrTy, subs, borrowedSelf.getUnmanagedValue());
106107
}
107108

109+
// Balance out the +1 from the self argument using end_lifetime.
110+
//
111+
// The issue here is that:
112+
//
113+
// 1. Self is passed into deallocating deinits at +1.
114+
// 2. Destroying deinits take in self as a +0 value that is then returned at
115+
// +1.
116+
//
117+
// This means that the lifetime of self can not be modeled statically in a
118+
// deallocating deinit without analyzing the body of the destroying deinit
119+
// (something that violates semantic sil). Thus we add an artifical destroy of
120+
// self before the actual destroy of self so that the verifier can understand
121+
// that self is being properly balanced.
122+
B.createEndLifetime(loc, initialSelfValue);
123+
108124
// Deallocate the object.
109-
selfValue = B.createUncheckedRefCast(loc, selfValue, classTy);
110-
B.createDeallocRef(loc, selfValue, false);
125+
selfForDealloc = B.createUncheckedRefCast(loc, selfForDealloc, classTy);
126+
B.createDeallocRef(loc, selfForDealloc, false);
111127

112128
// Return.
113129
B.createReturn(loc, emitEmptyTuple(loc));

lib/SILOptimizer/Transforms/OwnershipModelEliminator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ struct OwnershipModelEliminatorVisitor
6464
EBI->eraseFromParent();
6565
return true;
6666
}
67+
bool visitEndLifetimeInst(EndLifetimeInst *ELI) {
68+
ELI->eraseFromParent();
69+
return true;
70+
}
6771
bool visitUnmanagedRetainValueInst(UnmanagedRetainValueInst *URVI);
6872
bool visitUnmanagedReleaseValueInst(UnmanagedReleaseValueInst *URVI);
6973
bool visitUnmanagedAutoreleaseValueInst(UnmanagedAutoreleaseValueInst *UAVI);

lib/SILOptimizer/Utils/SILInliner.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ InlineCost swift::instructionInlineCost(SILInstruction &I) {
233233
case ValueKind::FunctionRefInst:
234234
case ValueKind::AllocGlobalInst:
235235
case ValueKind::GlobalAddrInst:
236+
case ValueKind::EndLifetimeInst:
236237
return InlineCost::Free;
237238

238239
// Typed GEPs are free.

lib/Serialization/DeserializeSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,7 @@ bool SILDeserializer::readSILInstruction(SILFunction *Fn, SILBasicBlock *BB,
13641364
UNARY_INSTRUCTION(Return)
13651365
UNARY_INSTRUCTION(Throw)
13661366
UNARY_INSTRUCTION(FixLifetime)
1367+
UNARY_INSTRUCTION(EndLifetime)
13671368
UNARY_INSTRUCTION(CopyBlock)
13681369
UNARY_INSTRUCTION(LoadBorrow)
13691370
UNARY_INSTRUCTION(BeginBorrow)

lib/Serialization/SerializeSIL.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ void SILSerializer::writeSILInstruction(const SILInstruction &SI) {
10571057
case ValueKind::LoadWeakInst:
10581058
case ValueKind::MarkUninitializedInst:
10591059
case ValueKind::FixLifetimeInst:
1060+
case ValueKind::EndLifetimeInst:
10601061
case ValueKind::CopyBlockInst:
10611062
case ValueKind::StrongPinInst:
10621063
case ValueKind::StrongReleaseInst:

test/SIL/Parser/basic.sil

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,14 @@ bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
15741574
return %28 : $()
15751575
}
15761576

1577+
// CHECK-LABEL: sil @test_end_lifetime : $@convention(thin) (@owned Builtin.NativeObject) -> () {
1578+
// CHECK: end_lifetime {{%.*}} : $Builtin.NativeObject
1579+
sil @test_end_lifetime : $@convention(thin) (@owned Builtin.NativeObject) -> () {
1580+
bb0(%0 : $Builtin.NativeObject):
1581+
end_lifetime %0 : $Builtin.NativeObject
1582+
return undef : $()
1583+
}
1584+
15771585
// CHECK-LABEL: sil_vtable Foo {
15781586
// CHECK: #Foo.subscript!getter.1: {{.*}} : hidden _TFC3tmp3Foog9subscriptFTVs5Int32S1__S1_
15791587
// CHECK: #Foo.subscript!setter.1: {{.*}} : _TFC3tmp3Foos9subscriptFTVs5Int32S1__S1_

test/SIL/Serialization/basic.sil

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// First parse this and then emit a *.sib. Then read in the *.sib, then recreate
2+
// RUN: rm -rfv %t
3+
// RUN: mkdir %t
4+
// RUN: %target-sil-opt %s -emit-sib -o %t/tmp.sib -module-name borrow
5+
// RUN: %target-sil-opt %t/tmp.sib -o %t/tmp.2.sib -module-name borrow
6+
// RUN: %target-sil-opt %t/tmp.2.sib -module-name borrow | %FileCheck %s
7+
8+
import Builtin
9+
10+
// CHECK-LABEL: sil @test_end_lifetime : $@convention(thin) (@owned Builtin.NativeObject) -> () {
11+
// CHECK: end_lifetime {{%.*}} : $Builtin.NativeObject
12+
sil @test_end_lifetime : $@convention(thin) (@owned Builtin.NativeObject) -> () {
13+
bb0(%0 : $Builtin.NativeObject):
14+
end_lifetime %0 : $Builtin.NativeObject
15+
return undef : $()
16+
}

test/SILGen/lifetime.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ class Foo<T> {
530530
// CHECK-NEXT: [[BORROWED_SELF:%.*]] = begin_borrow [[SELF]]
531531
// CHECK-NEXT: [[RESULT_SELF:%[0-9]+]] = apply [[DESTROYING_REF]]<T>([[BORROWED_SELF]]) : $@convention(method) <τ_0_0> (@guaranteed Foo<τ_0_0>) -> @owned Builtin.NativeObject
532532
// CHECK-NEXT: end_borrow [[BORROWED_SELF]] from [[SELF]]
533+
// CHECK-NEXT: end_lifetime [[SELF]]
533534
// CHECK-NEXT: [[SELF:%[0-9]+]] = unchecked_ref_cast [[RESULT_SELF]] : $Builtin.NativeObject to $Foo<T>
534535
// CHECK-NEXT: dealloc_ref [[SELF]] : $Foo<T>
535536
// CHECK-NEXT: [[RESULT:%[0-9]+]] = tuple ()

test/SILOptimizer/ownership_model_eliminator.sil

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,12 @@ bb3:
174174
%9999 = tuple()
175175
return %9999 : $()
176176
}
177+
178+
// CHECK-LABEL: sil @end_lifetime_test : $@convention(thin) (@owned Builtin.NativeObject) -> () {
179+
// CHECK-NOT: end_lifetime {{%.*}} : $Builtin.NativeObject
180+
sil @end_lifetime_test : $@convention(thin) (@owned Builtin.NativeObject) -> () {
181+
bb0(%0 : @owned $Builtin.NativeObject):
182+
end_lifetime %0 : $Builtin.NativeObject
183+
%9999 = tuple()
184+
return %9999 : $()
185+
}

utils/sil-mode.el

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"load_weak" "store_weak"
8484
"load_unowned" "store_unowned"
8585
"fix_lifetime" "mark_dependence"
86+
"end_lifetime"
8687
"is_unique" "is_unique_or_pinned"
8788
"copy_block"
8889
"strong_unpin" "strong_pin" "is_unique" "is_unique_or_pinned")

0 commit comments

Comments
 (0)