Skip to content

Commit 3524a79

Browse files
committed
Emit .cxx_destruct for destruction of ivars in Objective-C-derived classes.
The Objective-C runtime executes the .cxx_destruct method after the last -dealloc has executed when destroying an object, allowing the instance variables to remain live even after the subclass's destructor/-dealloc has executed, which is important for memory safety. This fixes the majority of <rdar://problem/15136592>. Note that IRGenModule::getAddrOfIVarDestroyer() contains an egregious hack to find the ivar destructor SIL function via a linear search. We need a better way to find SIL functions that we know exist, because LinkEntity does not suffice. Swift SVN r12206
1 parent 3339cad commit 3524a79

22 files changed

+211
-15
lines changed

docs/SIL.rst

+3
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,8 @@ Declaration References
665665
sil-decl-subref-part ::= 'initializer'
666666
sil-decl-subref-part ::= 'enumelt'
667667
sil-decl-subref-part ::= 'destroyer'
668+
sil-decl-subref-part ::= 'deallocator'
669+
sil-decl-subref-part ::= 'ivardestroyer'
668670
sil-decl-subref-part ::= 'globalaccessor'
669671
sil-decl-subref-part ::= 'defaultarg' '.' [0-9]+
670672
sil-decl-uncurry-level ::= [0-9]+
@@ -684,6 +686,7 @@ entity discriminators:
684686
- ``enumelt``: a member of a ``enum`` type.
685687
- ``destroyer``: a class's destroying destructor
686688
- ``deallocator``: a class's deallocating destructor
689+
- ``ivardestroyer``: a class's ivar destroyer
687690
- ``globalaccessor``: the addressor function for a global variable
688691
- ``defaultarg.``\ *n*: the default argument-generating function for
689692
the *n*\ -th argument of a Swift ``func``

include/swift/AST/Mangle.h

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class Mangler {
114114
void mangleConstructorEntity(ConstructorDecl *ctor, bool isAllocating,
115115
ExplosionKind kind, unsigned uncurryingLevel);
116116
void mangleDestructorEntity(DestructorDecl *decl, bool isDeallocating);
117+
void mangleIVarDestroyerEntity(ClassDecl *decl);
117118
void mangleGetterEntity(ValueDecl *decl, ExplosionKind explosionKind);
118119
void mangleSetterEntity(ValueDecl *decl, ExplosionKind explosionKind);
119120
void mangleAddressorEntity(ValueDecl *decl);

include/swift/Basic/DemangleNodes.def

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ NODE(GenericTypeMetadataPattern)
5252
NODE(Getter)
5353
NODE(Global)
5454
NODE(Identifier)
55+
NODE(IVarDestroyer)
5556
NODE(ImplConvention)
5657
NODE(ImplFunctionAttribute)
5758
NODE(ImplFunctionType)

include/swift/SIL/SILDeclRef.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,14 @@ struct SILDeclRef {
8989
GlobalAccessor,
9090

9191
/// References the generator for a default argument of a function.
92-
DefaultArgGenerator
92+
DefaultArgGenerator,
93+
94+
/// References the ivar destroyer for the ClassDecl in loc.
95+
///
96+
/// Only classes that are allocated using Objective-C's allocation
97+
/// routines have an ivar destroyer, which is emitted as
98+
/// .cxx_destruct.
99+
IVarDestroyer,
93100
};
94101

95102
/// The ValueDecl or AbstractClosureExpr represented by this SILDeclRef.

lib/AST/Mangle.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,12 @@ void Mangler::mangleDestructorEntity(DestructorDecl *dtor,
11011101
Buffer << (isDeallocating ? 'D' : 'd');
11021102
}
11031103

1104+
void Mangler::mangleIVarDestroyerEntity(ClassDecl *decl) {
1105+
Buffer << 'F';
1106+
mangleContext(decl, BindGenerics::Enclosing);
1107+
Buffer << 'E';
1108+
}
1109+
11041110
void Mangler::mangleGetterEntity(ValueDecl *decl, ExplosionKind explosion) {
11051111
Buffer << 'F';
11061112
mangleContextOf(decl, BindGenerics::All);

lib/Basic/Demangle.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,9 @@ class Demangler {
891891
} else if (Mangled.nextIf('d')) {
892892
entityKind = Node::Kind::Destructor;
893893
hasType = false;
894+
} else if (Mangled.nextIf('E')) {
895+
entityKind = Node::Kind::IVarDestroyer;
896+
hasType = false;
894897
} else if (Mangled.nextIf('C')) {
895898
if (context->getKind() == Node::Kind::Class)
896899
entityKind = Node::Kind::Allocator;
@@ -2127,6 +2130,9 @@ void NodePrinter::print(Node *pointer, bool asContext, bool suppressType) {
21272130
case Node::Kind::Deallocator:
21282131
printEntity(false, false, "__deallocating_destructor");
21292132
return;
2133+
case Node::Kind::IVarDestroyer:
2134+
printEntity(false, false, "__ivar_destroyer");
2135+
return;
21302136
case Node::Kind::ProtocolConformance: {
21312137
Node *child0 = pointer->getChild(0);
21322138
Node *child1 = pointer->getChild(1);

lib/IRGen/GenClass.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,9 @@ namespace {
715715
{
716716
visitConformances(theClass->getProtocols());
717717
visitMembers(theClass);
718+
719+
if (Lowering::usesObjCAllocator(theClass))
720+
addIVarDestroyer();
718721
}
719722

720723
ClassDataBuilder(IRGenModule &IGM, ClassDecl *theClass,
@@ -1059,6 +1062,11 @@ namespace {
10591062
}
10601063
}
10611064

1065+
void addIVarDestroyer() {
1066+
llvm::Constant *entry = emitObjCIVarDestroyerDescriptor(IGM, getClass());
1067+
InstanceMethods.push_back(entry);
1068+
}
1069+
10621070
private:
10631071
StringRef chooseNamePrefix(StringRef forClass,
10641072
StringRef forCategory,

lib/IRGen/GenDecl.cpp

+20-1
Original file line numberDiff line numberDiff line change
@@ -1250,7 +1250,7 @@ IRGenModule::getAddrOfBridgeToBlockConverter(SILType blockType,
12501250
return entry;
12511251
}
12521252

1253-
/// Fetch the declaration of the given known function.
1253+
/// Fetch the declaration of the given known destructor.
12541254
llvm::Function *IRGenModule::getAddrOfDestructor(ClassDecl *cd,
12551255
DestructorKind kind,
12561256
ForDefinition_t forDefinition,
@@ -1281,6 +1281,25 @@ llvm::Function *IRGenModule::getAddrOfDestructor(ClassDecl *cd,
12811281
return entry;
12821282
}
12831283

1284+
/// Fetch the declaration of the ivar destroyer for the given class.
1285+
llvm::Function *IRGenModule::getAddrOfIVarDestroyer(
1286+
ClassDecl *cd,
1287+
ForDefinition_t forDefinition) {
1288+
SILDeclRef silRef(cd, SILDeclRef::Kind::IVarDestroyer,
1289+
SILDeclRef::ConstructAtNaturalUncurryLevel,
1290+
/*isForeign=*/false);
1291+
llvm::SmallString<64> ivarDestroyerNameBuffer;
1292+
auto ivarDestroyerName = silRef.mangle(ivarDestroyerNameBuffer);
1293+
// Find the SILFunction for the ivar destroyer.
1294+
// FIXME: This linear scan is awful. Fortunately, it only happens
1295+
// once per definition of an Objective-C-derived class.
1296+
for (auto &silFn : SILMod->getFunctions()) {
1297+
if (silFn.getName() == ivarDestroyerName)
1298+
return getAddrOfSILFunction(&silFn, ExplosionKind::Minimal,
1299+
forDefinition);
1300+
}
1301+
llvm_unreachable("Unable to find ivar destroyer SIL function");
1302+
}
12841303

12851304
/// Returns the address of a value-witness function.
12861305
llvm::Function *IRGenModule::getAddrOfValueWitness(CanType abstractType,

lib/IRGen/GenObjC.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ namespace {
355355
cast<ConstructorDecl>(ref.getDecl())->getObjCSelector(Text);
356356
break;
357357

358+
case SILDeclRef::Kind::IVarDestroyer:
359+
Text = ".cxx_destruct";
360+
break;
361+
358362
case SILDeclRef::Kind::Setter:
359363
if (auto var = dyn_cast<VarDecl>(ref.getDecl()))
360364
var->getObjCSetterSelector(Text);
@@ -1094,6 +1098,29 @@ llvm::Constant *irgen::emitObjCMethodDescriptor(IRGenModule &IGM,
10941098
return llvm::ConstantStruct::getAnon(IGM.getLLVMContext(), fields);
10951099
}
10961100

1101+
llvm::Constant *irgen::emitObjCIVarDestroyerDescriptor(IRGenModule &IGM,
1102+
ClassDecl *cd) {
1103+
/// The first element is the selector.
1104+
SILDeclRef declRef = SILDeclRef(cd, SILDeclRef::Kind::IVarDestroyer,
1105+
1, /*foreign*/ true);
1106+
Selector selector(declRef);
1107+
llvm::Constant *selectorRef = IGM.getAddrOfObjCMethodName(selector.str());
1108+
1109+
/// The second element is the type @encoding.
1110+
llvm::Constant *atEncoding
1111+
= GetObjCEncodingForType(IGM, cd->getDestructor()->getType());
1112+
1113+
/// The third element is the method implementation pointer.
1114+
llvm::Function *swiftImpl = IGM.getAddrOfIVarDestroyer(cd, NotForDefinition);
1115+
llvm::Constant *impl
1116+
= getObjCMethodPointerForSwiftImpl(IGM, selector, declRef, swiftImpl,
1117+
ExplosionKind::Minimal);
1118+
1119+
// Form the method_t instance.
1120+
llvm::Constant *fields[] = { selectorRef, atEncoding, impl };
1121+
return llvm::ConstantStruct::getAnon(IGM.getLLVMContext(), fields);
1122+
}
1123+
10971124
/// Emit Objective-C method descriptors for the property accessors of the given
10981125
/// property. Returns a pair of Constants consisting of the getter and setter
10991126
/// function pointers, in that order. The setter llvm::Constant* will be null if

lib/IRGen/GenObjC.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -121,10 +121,15 @@ namespace irgen {
121121
llvm::Constant *&atEncoding,
122122
llvm::Constant *&impl);
123123

124-
/// Build an Objective-C method descriptor for the given method or constructor
125-
/// implementation.
124+
/// Build an Objective-C method descriptor for the given method,
125+
/// constructor, or destructor implementation.
126126
llvm::Constant *emitObjCMethodDescriptor(IRGenModule &IGM,
127127
AbstractFunctionDecl *method);
128+
129+
/// Build an Objective-C method descriptor for the ivar destroyer of
130+
/// a class (-.cxx_destruct).
131+
llvm::Constant *emitObjCIVarDestroyerDescriptor(IRGenModule &IGM,
132+
ClassDecl *cd);
128133

129134
/// Build an Objective-C method descriptor for the given property's
130135
/// getter and setter methods.

lib/IRGen/IRGenModule.h

+2
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,8 @@ private: \
393393
llvm::Function *getAddrOfDestructor(ClassDecl *D, DestructorKind kind,
394394
ForDefinition_t forDefinition,
395395
bool isForeign);
396+
llvm::Function *getAddrOfIVarDestroyer(ClassDecl *cd,
397+
ForDefinition_t forDefinition);
396398
llvm::Constant *getAddrOfTypeMetadata(CanType concreteType,
397399
bool isIndirect, bool isPattern,
398400
llvm::Type *definitionType = nullptr);

lib/Parse/ParseSIL.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -800,6 +800,9 @@ bool SILParser::parseSILDeclRef(SILDeclRef &Result) {
800800
} else if (!ParseState && Id.str() == "globalaccessor") {
801801
Kind = SILDeclRef::Kind::GlobalAccessor;
802802
ParseState = 1;
803+
} else if (!ParseState && Id.str() == "ivardestroyer") {
804+
Kind = SILDeclRef::Kind::IVarDestroyer;
805+
ParseState = 1;
803806
} else if (Id.str() == "foreign") {
804807
IsObjC = true;
805808
break;

lib/SIL/SIL.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ SILDeclRef::SILDeclRef(ValueDecl *vd, SILDeclRef::Kind kind,
9595
assert((kind == Kind::Destroyer || kind == Kind::Deallocator)
9696
&& "can only create destroyer/deallocator SILDeclRef for dtor");
9797
naturalUncurryLevel = 0;
98+
} else if (isa<ClassDecl>(vd)) {
99+
assert(kind == Kind::IVarDestroyer
100+
&& "can only create ivar destroyer SILDeclRef for class decl");
101+
naturalUncurryLevel = 1;
98102
} else if (auto *var = dyn_cast<VarDecl>(vd)) {
99103
assert((kind == Kind::Getter
100104
|| kind == Kind::Setter
@@ -359,6 +363,12 @@ static void mangleConstant(SILDeclRef c, llvm::raw_ostream &buffer) {
359363
c.uncurryLevel);
360364
return;
361365

366+
// entity ::= declaration 'E' // ivar destroyer
367+
case SILDeclRef::Kind::IVarDestroyer:
368+
buffer << introducer;
369+
mangler.mangleIVarDestroyerEntity(cast<ClassDecl>(c.getDecl()));
370+
return;
371+
362372
// entity ::= declaration 'g' // getter
363373
case SILDeclRef::Kind::Getter:
364374
buffer << introducer;

lib/SIL/SILFunctionType.cpp

+6-2
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,7 @@ static SelectorFamily getSelectorFamily(SILDeclRef c) {
632632
case SILDeclRef::Kind::Destroyer:
633633
case SILDeclRef::Kind::Deallocator:
634634
case SILDeclRef::Kind::GlobalAccessor:
635+
case SILDeclRef::Kind::IVarDestroyer:
635636
case SILDeclRef::Kind::DefaultArgGenerator:
636637
return SelectorFamily::None;
637638
}
@@ -754,7 +755,9 @@ AbstractCC TypeConverter::getAbstractCC(SILDeclRef c) {
754755

755756
// If this is a foreign thunk, it always has the foreign calling convention.
756757
if (c.isForeign)
757-
return c.hasDecl() && isClassOrProtocolMethod(c.getDecl())
758+
return c.hasDecl() &&
759+
(isClassOrProtocolMethod(c.getDecl()) ||
760+
c.kind == SILDeclRef::Kind::IVarDestroyer)
758761
? AbstractCC::ObjCMethod
759762
: AbstractCC::C;
760763

@@ -770,7 +773,8 @@ AbstractCC TypeConverter::getAbstractCC(SILDeclRef c) {
770773
return getProtocolWitnessCC(proto);
771774

772775
if (c.getDecl()->isInstanceMember() ||
773-
c.kind == SILDeclRef::Kind::Initializer)
776+
c.kind == SILDeclRef::Kind::Initializer ||
777+
c.kind == SILDeclRef::Kind::IVarDestroyer)
774778
return AbstractCC::Method;
775779
return AbstractCC::Freestanding;
776780
}

lib/SIL/SILPrinter.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ void SILDeclRef::print(raw_ostream &OS) const {
225225
case SILDeclRef::Kind::Deallocator:
226226
OS << "!deallocator";
227227
break;
228+
case SILDeclRef::Kind::IVarDestroyer:
229+
OS << "!ivardestroyer";
230+
break;
228231
case SILDeclRef::Kind::GlobalAccessor:
229232
OS << "!globalaccessor";
230233
break;

lib/SIL/TypeLowering.cpp

+26-2
Original file line numberDiff line numberDiff line change
@@ -1340,10 +1340,31 @@ static CanAnyFunctionType getDestructorType(DestructorDecl *dd,
13401340
CanType resultTy = isDeallocating? TupleType::getEmpty(C)->getCanonicalType()
13411341
: CanType(C.TheObjectPointerType);
13421342

1343+
auto selfType = classType;
13431344
if (auto params = dd->getDeclContext()->getGenericParamsOfContext())
1344-
return CanPolymorphicFunctionType::get(classType, resultTy, params, extInfo);
1345+
return CanPolymorphicFunctionType::get(selfType, resultTy, params, extInfo);
13451346

1346-
return CanFunctionType::get(classType, resultTy, extInfo);
1347+
return CanFunctionType::get(selfType, resultTy, extInfo);
1348+
}
1349+
1350+
/// Retrieve the type of the ivar initializer or destroyer method for
1351+
/// a class.
1352+
static CanAnyFunctionType getIVarInitDestroyerType(ClassDecl *cd,
1353+
bool isObjC,
1354+
ASTContext &ctx) {
1355+
auto classType = cd->getDeclaredTypeInContext()->getCanonicalType();
1356+
1357+
auto emptyTupleTy = TupleType::getEmpty(ctx)->getCanonicalType();
1358+
auto extInfo = AnyFunctionType::ExtInfo(isObjC? AbstractCC::ObjCMethod
1359+
: AbstractCC::Method,
1360+
/*thin*/ true,
1361+
/*noreturn*/ false);
1362+
1363+
auto resultType = CanFunctionType::get(emptyTupleTy, emptyTupleTy, extInfo);
1364+
if (auto params = cd->getGenericParams())
1365+
return CanPolymorphicFunctionType::get(classType, resultType, params,
1366+
extInfo);
1367+
return CanFunctionType::get(classType, resultType, extInfo);
13471368
}
13481369

13491370
GenericParamList *
@@ -1534,6 +1555,9 @@ CanAnyFunctionType TypeConverter::makeConstantType(SILDeclRef c,
15341555
return getDefaultArgGeneratorType(cast<AbstractFunctionDecl>(vd),
15351556
c.defaultArgIndex, Context);
15361557
}
1558+
case SILDeclRef::Kind::IVarDestroyer: {
1559+
return getIVarInitDestroyerType(cast<ClassDecl>(vd), c.isForeign, Context);
1560+
}
15371561
}
15381562
}
15391563

lib/SILGen/SILGen.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,19 @@ void SILGenModule::emitDestructor(ClassDecl *cd, DestructorDecl *dd) {
481481

482482
// Emit the Objective-C thunk for -dealloc.
483483
emitObjCDestructorThunk(dd);
484+
485+
// Emit the ivar destroyer.
486+
{
487+
SILDeclRef ivarDestroyer(cd, SILDeclRef::Kind::IVarDestroyer);
488+
SILFunction *f = preEmitFunction(ivarDestroyer, dd, dd);
489+
PrettyStackTraceSILFunction X("silgen emitDestructor ivar destroyer", f);
490+
SILGenFunction(*this, *f).emitIVarDestroyer(ivarDestroyer);
491+
postEmitFunction(ivarDestroyer, f);
492+
}
493+
494+
// Emit the Objective-C thunk for the ivar destroyer (.cxx_destruct).
495+
emitObjCIVarDestroyerThunk(cd);
496+
484497
return;
485498
}
486499

@@ -679,6 +692,22 @@ void SILGenModule::emitObjCDestructorThunk(DestructorDecl *destructor) {
679692
postEmitFunction(thunk, f);
680693
}
681694

695+
void SILGenModule::emitObjCIVarDestroyerThunk(ClassDecl *cd) {
696+
SILDeclRef thunk(cd,
697+
SILDeclRef::Kind::IVarDestroyer,
698+
SILDeclRef::ConstructAtNaturalUncurryLevel,
699+
/*isObjC*/ true);
700+
701+
// Don't emit the thunk if it already exists.
702+
if (hasFunction(thunk))
703+
return;
704+
SILFunction *f = preEmitFunction(thunk, cd, cd);
705+
PrettyStackTraceSILFunction X("silgen objc ivar destroyer thunk", f);
706+
f->setBare(IsBare);
707+
SILGenFunction(*this, *f).emitObjCMethodThunk(thunk);
708+
postEmitFunction(thunk, f);
709+
}
710+
682711
void SILGenModule::visitPatternBindingDecl(PatternBindingDecl *pd) {
683712
// If we're in a script mode context, emit the pattern binding as top-level
684713
// code.

lib/SILGen/SILGen.h

+4
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
203203
/// Emit the ObjC-compatible entry point for a destructor (i.e., -dealloc).
204204
void emitObjCDestructorThunk(DestructorDecl *destructor);
205205

206+
/// Emit the ObjC-compatible entry point for an ivar destroyer
207+
/// (i.e., -.cxx_destruct).
208+
void emitObjCIVarDestroyerThunk(ClassDecl *cd);
209+
206210
/// Emit the ObjC-compatible getter and setter for a subscript
207211
/// declaration.
208212
void emitObjCSubscriptMethodThunks(SubscriptDecl *subscript);

lib/SILGen/SILGenDecl.cpp

+3-7
Original file line numberDiff line numberDiff line change
@@ -1772,13 +1772,9 @@ void SILGenFunction::emitObjCDestructor(SILDeclRef dtor) {
17721772
return;
17731773

17741774
auto cleanupLoc = CleanupLocation::getCleanupLocation(loc);
1775-
1776-
// Release our members early.
1777-
// FIXME: Because Objective-C's -dealloc is a deallocating destructor,
1778-
// rather than a destroying destructor, we don't get a change to release
1779-
// the members after our superclass dealloc runs. This will eventually be
1780-
// handled by .cxx_destruct.
1781-
emitClassMemberDestruction(selfValue, cd, loc, cleanupLoc);
1775+
1776+
// Note: the ivar destroyer is responsible for destroying the
1777+
// instance variables before the object is actually deallocated.
17821778

17831779
// Form a reference to the superclass -dealloc.
17841780
Type superclassTy = cd->getSuperclass();

0 commit comments

Comments
 (0)