Skip to content

Commit ccc2ff9

Browse files
authored
Merge pull request #4225 from slavapestov/fix-protocol-extension-factory-init
DI: New way of modeling factory initializers
2 parents 26fcd8c + c98ce0c commit ccc2ff9

13 files changed

+384
-142
lines changed

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,12 @@ FUNCTION(GetObjectClass, object_getClass, C_CC,
841841
ARGS(ObjCPtrTy),
842842
ATTRS(NoUnwind, ReadOnly))
843843

844+
// id object_dispose(id object);
845+
FUNCTION(ObjectDispose, object_dispose, C_CC,
846+
RETURNS(ObjCPtrTy),
847+
ARGS(ObjCPtrTy),
848+
ATTRS(NoUnwind))
849+
844850
// Class objc_lookUpClass(const char *name);
845851
FUNCTION(LookUpClass, objc_lookUpClass, C_CC,
846852
RETURNS(ObjCClassPtrTy),

lib/IRGen/GenClass.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,22 @@ void irgen::emitPartialClassDeallocation(IRGenFunction &IGF,
726726
llvm::Value *metadataValue) {
727727
auto *theClass = selfType.getClassOrBoundGenericClass();
728728

729+
// Foreign classes should not be freed by sending -release.
730+
// They should also probably not be freed with object_dispose(),
731+
// either.
732+
//
733+
// However, in practice, the only time we should try to free an
734+
// instance of a foreign class here is inside an initializer
735+
// delegating to a factory initializer. In this case, the object
736+
// was allocated with +allocWithZone:, so calling object_dispose()
737+
// should be OK.
738+
if (theClass->getForeignClassKind() == ClassDecl::ForeignKind::RuntimeOnly) {
739+
selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.ObjCPtrTy);
740+
IGF.Builder.CreateCall(IGF.IGM.getObjectDisposeFn(),
741+
{selfValue});
742+
return;
743+
}
744+
729745
llvm::Value *size, *alignMask;
730746
getInstanceSizeAndAlignMask(IGF, selfType, theClass, selfValue,
731747
size, alignMask);

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,7 @@ static llvm::Value *emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
13401340
// Non-native types are just wrapped in various ways.
13411341
if (auto classDecl = dyn_cast<ClassDecl>(typeDecl)) {
13421342
// We emit a completely different pattern for foreign classes.
1343-
if (classDecl->isForeign()) {
1343+
if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
13441344
return emitForeignTypeMetadataRef(IGF, type);
13451345
}
13461346

lib/SILOptimizer/Mandatory/DIMemoryUseCollector.cpp

Lines changed: 40 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,15 +1107,30 @@ static bool isSelfInitUse(SILInstruction *I) {
11071107
}
11081108

11091109
// This is a self.init call if structured like this:
1110+
//
11101111
// (call_expr type='SomeClass'
11111112
// (dot_syntax_call_expr type='() -> SomeClass' self
11121113
// (other_constructor_ref_expr implicit decl=SomeClass.init)
11131114
// (decl_ref_expr type='SomeClass', "self"))
11141115
// (...some argument...)
1115-
if (auto AE = dyn_cast<ApplyExpr>(LocExpr)) {
1116-
if ((AE = dyn_cast<ApplyExpr>(AE->getFn())) &&
1117-
isa<OtherConstructorDeclRefExpr>(AE->getFn()))
1118-
return true;
1116+
//
1117+
// Or like this:
1118+
//
1119+
// (call_expr type='SomeClass'
1120+
// (dot_syntax_call_expr type='() -> SomeClass' self
1121+
// (decr_ref_expr implicit decl=SomeClass.init)
1122+
// (decl_ref_expr type='SomeClass', "self"))
1123+
// (...some argument...)
1124+
//
1125+
if (auto *AE = dyn_cast<ApplyExpr>(LocExpr)) {
1126+
if ((AE = dyn_cast<ApplyExpr>(AE->getFn()))) {
1127+
if (isa<OtherConstructorDeclRefExpr>(AE->getFn()))
1128+
return true;
1129+
if (auto *DRE = dyn_cast<DeclRefExpr>(AE->getFn()))
1130+
if (auto *CD = dyn_cast<ConstructorDecl>(DRE->getDecl()))
1131+
if (CD->isFactoryInit())
1132+
return true;
1133+
}
11191134
}
11201135
return false;
11211136
}
@@ -1128,59 +1143,19 @@ static bool isSelfInitUse(SILArgument *Arg) {
11281143
// of a throwing delegated init.
11291144
auto *BB = Arg->getParent();
11301145
auto *Pred = BB->getSinglePredecessor();
1131-
if (!Pred || !isa<TryApplyInst>(Pred->getTerminator()))
1132-
return false;
1133-
return isSelfInitUse(Pred->getTerminator());
1134-
}
1135-
1136-
1137-
/// Determine if this value_metatype instruction is part of a call to
1138-
/// self.init when delegating to a factory initializer.
1139-
///
1140-
/// FIXME: This is only necessary due to our broken model for factory
1141-
/// initializers.
1142-
static bool isSelfInitUse(ValueMetatypeInst *Inst) {
1143-
// "Inst" is a ValueMetatype instruction. Check to see if it is
1144-
// used by an apply that came from a call to self.init.
1145-
for (auto UI : Inst->getUses()) {
1146-
auto *User = UI->getUser();
1147-
1148-
// Check whether we're looking up a factory initializer with
1149-
// class_method.
1150-
if (auto *CMI = dyn_cast<ClassMethodInst>(User)) {
1151-
// Only works for allocating initializers...
1152-
auto Member = CMI->getMember();
1153-
if (Member.kind != SILDeclRef::Kind::Allocator)
1154-
return false;
1155-
1156-
// ... of factory initializers.
1157-
auto ctor = dyn_cast_or_null<ConstructorDecl>(Member.getDecl());
1158-
return ctor && ctor->isFactoryInit();
1159-
}
1160-
1161-
if (auto apply = ApplySite::isa(User)) {
1162-
auto *LocExpr = apply.getLoc().getAsASTNode<ApplyExpr>();
1163-
if (!LocExpr)
1164-
return false;
1165-
1166-
LocExpr = dyn_cast<ApplyExpr>(LocExpr->getFn());
1167-
if (!LocExpr || !isa<OtherConstructorDeclRefExpr>(LocExpr->getFn()))
1168-
return false;
1169-
1170-
return true;
1171-
}
1172-
1173-
// Ignore the thick_to_objc_metatype instruction.
1174-
if (isa<ThickToObjCMetatypeInst>(User)) {
1175-
continue;
1176-
}
11771146

1147+
// The two interesting cases are where self.init throws, in which case
1148+
// the argument came from a try_apply, or if self.init is failable,
1149+
// in which case we have a switch_enum.
1150+
if (!Pred ||
1151+
(!isa<TryApplyInst>(Pred->getTerminator()) &&
1152+
!isa<SwitchEnumInst>(Pred->getTerminator())))
11781153
return false;
1179-
}
11801154

1181-
return false;
1155+
return isSelfInitUse(Pred->getTerminator());
11821156
}
11831157

1158+
11841159
void ElementUseCollector::
11851160
collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType,
11861161
llvm::SmallDenseMap<VarDecl*, unsigned> &EltNumbering) {
@@ -1233,18 +1208,12 @@ collectClassSelfUses(SILValue ClassPointer, SILType MemorySILType,
12331208
recordFailableInitCall(User);
12341209
}
12351210

1236-
// If this is a ValueMetatypeInst, check to see if it's part of a
1237-
// self.init call to a factory initializer in a delegating
1238-
// initializer.
1239-
if (auto *VMI = dyn_cast<ValueMetatypeInst>(User)) {
1240-
if (isSelfInitUse(VMI))
1241-
Kind = DIUseKind::SelfInit;
1242-
else
1243-
// Otherwise, this is a simple reference to "type(of:)", which is
1244-
// always fine, even if self is uninitialized.
1245-
continue;
1246-
}
1247-
1211+
// If this is a ValueMetatypeInst, this is a simple reference
1212+
// to "type(of:)", which is always fine, even if self is
1213+
// uninitialized.
1214+
if (isa<ValueMetatypeInst>(User))
1215+
continue;
1216+
12481217
// If this is a partial application of self, then this is an escape point
12491218
// for it.
12501219
if (isa<PartialApplyInst>(User))
@@ -1289,27 +1258,26 @@ void ElementUseCollector::collectDelegatingClassInitSelfUses() {
12891258
if (auto apply = dyn_cast<ApplyInst>(cast<AssignInst>(User)->getSrc())) {
12901259
if (auto fn = apply->getCalleeFunction()) {
12911260
if (fn->getRepresentation()
1292-
== SILFunctionTypeRepresentation::CFunctionPointer)
1261+
== SILFunctionTypeRepresentation::CFunctionPointer) {
12931262
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
1263+
continue;
1264+
}
12941265
}
12951266
}
12961267

1297-
continue;
12981268
}
12991269

13001270
// Stores *to* the allocation are writes. If the value being stored is a
13011271
// call to self.init()... then we have a self.init call.
13021272
if (auto *AI = dyn_cast<AssignInst>(User)) {
13031273
if (auto *AssignSource = dyn_cast<SILInstruction>(AI->getOperand(0)))
13041274
if (isSelfInitUse(AssignSource)) {
1305-
assert(isa<ArchetypeType>(TheMemory.getType()));
13061275
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
13071276
continue;
13081277
}
13091278
if (auto *AssignSource = dyn_cast<SILArgument>(AI->getOperand(0)))
13101279
if (AssignSource->getParent() == AI->getParent())
13111280
if (isSelfInitUse(AssignSource)) {
1312-
assert(isa<ArchetypeType>(TheMemory.getType()));
13131281
Uses.push_back(DIMemoryUse(User, DIUseKind::SelfInit, 0, 1));
13141282
continue;
13151283
}
@@ -1379,16 +1347,10 @@ void ElementUseCollector::collectDelegatingClassInitSelfUses() {
13791347
recordFailableInitCall(User);
13801348
}
13811349

1382-
// If this is a ValueMetatypeInst, check to see if it's part of a
1383-
// self.init call to a factory initializer in a delegating
1384-
// initializer.
1385-
if (auto *VMI = dyn_cast<ValueMetatypeInst>(User)) {
1386-
if (isSelfInitUse(VMI))
1387-
Kind = DIUseKind::SelfInit;
1388-
else
1389-
// Otherwise, this is a simple reference to "type(of:)", which is
1390-
// always fine, even if self is uninitialized.
1391-
continue;
1350+
// A simple reference to "type(of:)" is always fine,
1351+
// even if self is uninitialized.
1352+
if (isa<ValueMetatypeInst>(User)) {
1353+
continue;
13921354
}
13931355

13941356
Uses.push_back(DIMemoryUse(User, Kind, 0, 1));

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,39 +35,70 @@ static void diagnose(SILModule &M, SILLocation loc, ArgTypes... args) {
3535
M.getASTContext().Diags.diagnose(loc.getSourceLoc(), Diagnostic(args...));
3636
}
3737

38+
enum class PartialInitializationKind {
39+
/// The box contains a fully-initialized value.
40+
IsNotInitialization,
41+
42+
/// The box contains a class instance that we own, but the instance has
43+
/// not been initialized and should be freed with a special SIL
44+
/// instruction made for this purpose.
45+
IsReinitialization,
46+
47+
/// The box contains an undefined value that should be ignored.
48+
IsInitialization,
49+
};
50+
3851
/// Emit the sequence that an assign instruction lowers to once we know
3952
/// if it is an initialization or an assignment. If it is an assignment,
4053
/// a live-in value can be provided to optimize out the reload.
4154
static void LowerAssignInstruction(SILBuilder &B, AssignInst *Inst,
42-
IsInitialization_t isInitialization) {
43-
DEBUG(llvm::dbgs() << " *** Lowering [isInit=" << (bool)isInitialization
55+
PartialInitializationKind isInitialization) {
56+
DEBUG(llvm::dbgs() << " *** Lowering [isInit=" << unsigned(isInitialization)
4457
<< "]: " << *Inst << "\n");
4558

4659
++NumAssignRewritten;
4760

4861
SILValue Src = Inst->getSrc();
62+
SILLocation Loc = Inst->getLoc();
4963

50-
// If this is an initialization, or the storage type is trivial, we
51-
// can just replace the assignment with a store.
52-
53-
// Otherwise, if it has trivial type, we can always just replace the
54-
// assignment with a store. If it has non-trivial type and is an
55-
// initialization, we can also replace it with a store.
56-
if (isInitialization == IsInitialization ||
64+
if (isInitialization == PartialInitializationKind::IsInitialization ||
5765
Inst->getDest()->getType().isTrivial(Inst->getModule())) {
58-
B.createStore(Inst->getLoc(), Src, Inst->getDest());
66+
67+
// If this is an initialization, or the storage type is trivial, we
68+
// can just replace the assignment with a store.
69+
assert(isInitialization != PartialInitializationKind::IsReinitialization);
70+
B.createStore(Loc, Src, Inst->getDest());
71+
} else if (isInitialization == PartialInitializationKind::IsReinitialization) {
72+
73+
// We have a case where a convenience initializer on a class
74+
// delegates to a factory initializer from a protocol extension.
75+
// Factory initializers give us a whole new instance, so the existing
76+
// instance, which has not been initialized and never will be, must be
77+
// freed using dealloc_partial_ref.
78+
SILValue Pointer = B.createLoad(Loc, Inst->getDest());
79+
B.createStore(Loc, Src, Inst->getDest());
80+
81+
auto MetatypeTy = CanMetatypeType::get(
82+
Inst->getDest()->getType().getSwiftRValueType(),
83+
MetatypeRepresentation::Thick);
84+
auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy);
85+
SILValue Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer);
86+
87+
B.createDeallocPartialRef(Loc, Pointer, Metatype);
5988
} else {
89+
assert(isInitialization == PartialInitializationKind::IsNotInitialization);
90+
6091
// Otherwise, we need to replace the assignment with the full
61-
// load/store/release dance. Note that the new value is already
92+
// load/store/release dance. Note that the new value is already
6293
// considered to be retained (by the semantics of the storage type),
6394
// and we're transferring that ownership count into the destination.
6495

6596
// This is basically TypeLowering::emitStoreOfCopy, except that if we have
6697
// a known incoming value, we can avoid the load.
67-
SILValue IncomingVal = B.createLoad(Inst->getLoc(), Inst->getDest());
98+
SILValue IncomingVal = B.createLoad(Loc, Inst->getDest());
6899
B.createStore(Inst->getLoc(), Src, Inst->getDest());
69100

70-
B.emitReleaseValueOperation(Inst->getLoc(), IncomingVal);
101+
B.emitReleaseValueOperation(Loc, IncomingVal);
71102
}
72103

73104
Inst->eraseFromParent();
@@ -1632,12 +1663,24 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &InstInfo) {
16321663
InstInfo.Inst = nullptr;
16331664
NonLoadUses.erase(Inst);
16341665

1666+
PartialInitializationKind PartialInitKind;
1667+
1668+
if (TheMemory.isClassInitSelf() &&
1669+
Kind == DIUseKind::SelfInit) {
1670+
assert(InitKind == IsInitialization);
1671+
PartialInitKind = PartialInitializationKind::IsReinitialization;
1672+
} else {
1673+
PartialInitKind = (InitKind == IsInitialization
1674+
? PartialInitializationKind::IsInitialization
1675+
: PartialInitializationKind::IsNotInitialization);
1676+
}
1677+
16351678
unsigned FirstElement = InstInfo.FirstElement;
16361679
unsigned NumElements = InstInfo.NumElements;
16371680

16381681
SmallVector<SILInstruction*, 4> InsertedInsts;
16391682
SILBuilderWithScope B(Inst, &InsertedInsts);
1640-
LowerAssignInstruction(B, AI, InitKind);
1683+
LowerAssignInstruction(B, AI, PartialInitKind);
16411684

16421685
// If lowering of the assign introduced any new loads or stores, keep track
16431686
// of them.
@@ -1646,7 +1689,11 @@ void LifetimeChecker::updateInstructionForInitState(DIMemoryUse &InstInfo) {
16461689
NonLoadUses[I] = Uses.size();
16471690
Uses.push_back(DIMemoryUse(I, Kind, FirstElement, NumElements));
16481691
} else if (isa<LoadInst>(I)) {
1649-
Uses.push_back(DIMemoryUse(I, Load, FirstElement, NumElements));
1692+
// If we have a re-initialization, the value must be a class,
1693+
// and the load is just there so we can free the uninitialized
1694+
// object husk; it's not an actual use of 'self'.
1695+
if (PartialInitKind != PartialInitializationKind::IsReinitialization)
1696+
Uses.push_back(DIMemoryUse(I, Load, FirstElement, NumElements));
16501697
}
16511698
}
16521699
return;
@@ -2541,7 +2588,8 @@ static bool lowerRawSILOperations(SILFunction &Fn) {
25412588
// Unprocessed assigns just lower into assignments, not initializations.
25422589
if (auto *AI = dyn_cast<AssignInst>(Inst)) {
25432590
SILBuilderWithScope B(AI);
2544-
LowerAssignInstruction(B, AI, IsNotInitialization);
2591+
LowerAssignInstruction(B, AI,
2592+
PartialInitializationKind::IsNotInitialization);
25452593
// Assign lowering may split the block. If it did,
25462594
// reset our iteration range to the block after the insertion.
25472595
if (B.getInsertionBB() != &BB)

test/1_stdlib/Dispatch.swift

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33

44
// REQUIRES: objc_interop
55

6-
// rdar://27226313
7-
// REQUIRES: optimized_stdlib
8-
96
import Dispatch
107
import Foundation
118
import StdlibUnittest

test/IRGen/objc_protocol_conversion.sil

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ entry:
2929
// CHECK-LABEL: define{{( protected)?}} %swift.type* @protocol_metatype()
3030
sil @protocol_metatype : $@convention(thin) () -> @thick Protocol.Type {
3131
entry:
32-
// CHECK: call %swift.type* @swift_getForeignTypeMetadata
32+
// CHECK: call %objc_class* @objc_lookUpClass
33+
// CHECK: call %swift.type* @swift_getObjCClassMetadata
3334
%t = metatype $@thick Protocol.Type
3435
return %t : $@thick Protocol.Type
3536
}

test/IRGen/objc_runtime_visible.sil

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,17 @@ bb0:
2020
// CHECK-NEXT: ret %objc_class*
2121
return %0 : $@objc_metatype A.Type
2222
}
23+
24+
// CHECK: define void @deallocA(%CSo1A*) #0 {
25+
sil @deallocA : $@convention(thin) (@owned A) -> () {
26+
bb0(%0 : $A):
27+
%1 = metatype $@thick A.Type
28+
29+
// CHECK: call %objc_object* @object_dispose
30+
dealloc_partial_ref %0 : $A, %1 : $@thick A.Type
31+
32+
%2 = tuple ()
33+
34+
// CHECK-NEXT: ret
35+
return %2 : $()
36+
}

0 commit comments

Comments
 (0)