Skip to content

Implement Objective-C metadata update callback mechanism #19944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ Globals
global ::= nominal-type 'Mm' // class metaclass
global ::= nominal-type 'Mn' // nominal type descriptor
global ::= nominal-type 'Mu' // class method lookup function
global ::= nominal-type 'MU' // ObjC metadata update callback function
global ::= module 'MXM' // module descriptor
global ::= context 'MXE' // extension descriptor
global ::= context 'MXX' // anonymous context descriptor
Expand Down
4 changes: 4 additions & 0 deletions include/swift/ABI/Class.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ enum class ObjCClassFlags : uint32_t {
/// This class has the exception attribute.
Exception = 0x00020,

/// This class provides a metadata update callback trailing the ro-data.
/// Note that we're re-using the obsolete flag above.
HasMetadataUpdateCallback = 0x00040,

/// (Obsolete) ARC-specific: this class has a .release_ivars method.
HasIvarReleaser = 0x00040,

Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ NODE(Metatype)
NODE(MetatypeRepresentation)
NODE(Metaclass)
NODE(MethodLookupFunction)
NODE(ObjCMetadataUpdateFunction)
CONTEXT_NODE(ModifyAccessor)
CONTEXT_NODE(Module)
CONTEXT_NODE(NativeOwningAddressor)
Expand Down
11 changes: 11 additions & 0 deletions include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ class LinkEntity {
/// A swift metaclass-stub reference. The pointer is a ClassDecl*.
SwiftMetaclassStub,

/// A callback used by newer Objective-C runtimes to initialize class
/// metadata for classes where doesClassMetadataRequireUpdate() is true
/// but doesClassMetadataRequireInitialization() is false.
ObjCMetadataUpdateFunction,

/// A class metadata base offset global variable. This stores the offset
/// of the immediate members of a class (generic parameters, field offsets,
/// vtable offsets) in the class's metadata. The immediate members begin
Expand Down Expand Up @@ -565,6 +570,12 @@ class LinkEntity {
return entity;
}

static LinkEntity forObjCMetadataUpdateFunction(ClassDecl *decl) {
LinkEntity entity;
entity.setForDecl(Kind::ObjCMetadataUpdateFunction, decl);
return entity;
}

static LinkEntity forTypeMetadata(CanType concreteType,
TypeMetadataAddress addr) {
LinkEntity entity;
Expand Down
2 changes: 2 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,8 @@ NodePointer Demangler::demangleMetatype() {
return createWithChild(Node::Kind::ProtocolDescriptor, popProtocol());
case 'u':
return createWithPoppedType(Node::Kind::MethodLookupFunction);
case 'U':
return createWithPoppedType(Node::Kind::ObjCMetadataUpdateFunction);
case 'B':
return createWithChild(Node::Kind::ReflectionMetadataBuiltinDescriptor,
popNode(Node::Kind::Type));
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ class NodePrinter {
case Node::Kind::Number:
case Node::Kind::ObjCAttribute:
case Node::Kind::ObjCBlock:
case Node::Kind::ObjCMetadataUpdateFunction:
case Node::Kind::Owned:
case Node::Kind::OwningAddressor:
case Node::Kind::OwningMutableAddressor:
Expand Down Expand Up @@ -939,6 +940,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
Printer << "method lookup function for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::ObjCMetadataUpdateFunction:
Printer << "ObjC metadata update function for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::OutlinedBridgedMethod:
Printer << "outlined bridged method (" << Node->getText() << ") of ";
return nullptr;
Expand Down
4 changes: 4 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1973,6 +1973,10 @@ void Remangler::mangleMethodLookupFunction(Node *node) {
Out << "<method-lookup-function>";
}

void Remangler::mangleObjCMetadataUpdateFunction(Node *node) {
Out << "<objc-metadata-update-function>";
}

void Remangler::mangleEmptyList(Node *node) {
Out << "<empty>";
}
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1962,6 +1962,11 @@ void Remangler::mangleMethodLookupFunction(Node *node) {
Buffer << "Mu";
}

void Remangler::mangleObjCMetadataUpdateFunction(Node *node) {
mangleSingleChildNode(node);
Buffer << "MU";
}

void Remangler::mangleThrowsAnnotation(Node *node) {
Buffer << 'K';
}
Expand Down
80 changes: 71 additions & 9 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1233,7 +1233,7 @@ namespace {
IGM.getObjCRuntimeBaseForSwiftRootClass(getClass()));
}

auto dataPtr = emitROData(ForMetaClass);
auto dataPtr = emitROData(ForMetaClass, DoesNotHaveUpdateCallback);
dataPtr = llvm::ConstantExpr::getPtrToInt(dataPtr, IGM.IntPtrTy);

llvm::Constant *fields[] = {
Expand Down Expand Up @@ -1357,12 +1357,14 @@ namespace {
return buildGlobalVariable(fields, "_PROTOCOL_");
}

void emitRODataFields(ConstantStructBuilder &b, ForMetaClass_t forMeta) {
void emitRODataFields(ConstantStructBuilder &b,
ForMetaClass_t forMeta,
HasUpdateCallback_t hasUpdater) {
assert(FieldLayout && "can't emit rodata for a category");

// struct _class_ro_t {
// uint32_t flags;
b.addInt32(unsigned(buildFlags(forMeta)));
b.addInt32(unsigned(buildFlags(forMeta, hasUpdater)));

// uint32_t instanceStart;
// uint32_t instanceSize;
Expand All @@ -1374,6 +1376,8 @@ namespace {
Size instanceStart;
Size instanceSize;
if (forMeta) {
assert(!hasUpdater);

// sizeof(struct class_t)
instanceSize = Size(5 * IGM.getPointerSize().getValue());
// historical nonsense
Expand Down Expand Up @@ -1420,24 +1424,36 @@ namespace {
// const property_list_t *baseProperties;
b.add(buildPropertyList(forMeta));

// If hasUpdater is true, the metadata update callback goes here.
if (hasUpdater) {
// Class _Nullable (*metadataUpdateCallback)(Class _Nonnull cls,
// void * _Nullable arg);
b.add(IGM.getAddrOfObjCMetadataUpdateFunction(
TheEntity.get<ClassDecl *>(),
NotForDefinition));
}

// };
}

llvm::Constant *emitROData(ForMetaClass_t forMeta) {
llvm::Constant *emitROData(ForMetaClass_t forMeta,
HasUpdateCallback_t hasUpdater) {
ConstantInitBuilder builder(IGM);
auto fields = builder.beginStruct();
emitRODataFields(fields, forMeta);
emitRODataFields(fields, forMeta, hasUpdater);

auto dataSuffix = forMeta ? "_METACLASS_DATA_" : "_DATA_";
return buildGlobalVariable(fields, dataSuffix);
}

private:
ObjCClassFlags buildFlags(ForMetaClass_t forMeta) {
ObjCClassFlags buildFlags(ForMetaClass_t forMeta,
HasUpdateCallback_t hasUpdater) {
ObjCClassFlags flags = ObjCClassFlags::CompiledByARC;

// Mark metaclasses as appropriate.
if (forMeta) {
assert(!hasUpdater);
flags |= ObjCClassFlags::Meta;

// Non-metaclasses need us to record things whether primitive
Expand All @@ -1448,6 +1464,9 @@ namespace {
flags |= ObjCClassFlags::HasCXXDestructorOnly;
}

if (hasUpdater)
flags |= ObjCClassFlags::HasMetadataUpdateCallback;

// FIXME: set ObjCClassFlags::Hidden when appropriate
return flags;
}
Expand Down Expand Up @@ -2065,6 +2084,34 @@ namespace {
};
} // end anonymous namespace

static llvm::Function *emitObjCMetadataUpdateFunction(IRGenModule &IGM,
ClassDecl *cls) {
llvm::Function *f =
IGM.getAddrOfObjCMetadataUpdateFunction(cls, ForDefinition);
f->setAttributes(IGM.constructInitialAttributes());

IRGenFunction IGF(IGM, f);
if (IGM.DebugInfo)
IGM.DebugInfo->emitArtificialFunction(IGF, f);

// Our parameters are the metadata pointer, and an argument for
// future use. We just ignore them.
Explosion params = IGF.collectParameters();
(void) params.claimAll();

// Just directly call our metadata accessor. This should actually
// return the same metadata; the Objective-C runtime enforces this.
auto type = cls->getDeclaredType()->getCanonicalType();
auto *metadata = IGF.emitTypeMetadataRef(type,
MetadataState::Complete)
.getMetadata();
IGF.Builder.CreateRet(
IGF.Builder.CreateBitCast(metadata,
IGM.ObjCClassPtrTy));

return f;
}

/// Emit the private data (RO-data) associated with a class.
llvm::Constant *irgen::emitClassPrivateData(IRGenModule &IGM,
ClassDecl *cls) {
Expand All @@ -2081,8 +2128,15 @@ llvm::Constant *irgen::emitClassPrivateData(IRGenModule &IGM,
// First, build the metaclass object.
builder.buildMetaclassStub();

HasUpdateCallback_t hasUpdater = DoesNotHaveUpdateCallback;
if (doesClassMetadataRequireUpdate(IGM, cls) &&
!doesClassMetadataRequireInitialization(IGM, cls)) {
hasUpdater = HasUpdateCallback;
emitObjCMetadataUpdateFunction(IGM, cls);
}

// Then build the class RO-data.
return builder.emitROData(ForClass);
return builder.emitROData(ForClass, hasUpdater);
}

std::pair<Size, Size>
Expand All @@ -2092,6 +2146,9 @@ irgen::emitClassPrivateDataFields(IRGenModule &IGM,
assert(IGM.ObjCInterop && "emitting RO-data outside of interop mode");
PrettyStackTraceDecl stackTraceRAII("emitting ObjC metadata for", cls);

// This should only be used with generic classes.
assert(cls->isGenericContext());

SILType selfType = getSelfType(cls);
auto &classTI = IGM.getTypeInfo(selfType).as<ClassTypeInfo>();

Expand All @@ -2105,15 +2162,20 @@ irgen::emitClassPrivateDataFields(IRGenModule &IGM,
assert(startOfClassRO.isMultipleOf(IGM.getPointerSize()));
{
auto classRO = init.beginStruct();
builder.emitRODataFields(classRO, ForClass);

// Note: an update callback is only ever used with the in-place
// initialization pattern, which precludes generic classes.
builder.emitRODataFields(classRO,
ForClass,
DoesNotHaveUpdateCallback);
classRO.finishAndAddTo(init);
}

Size startOfMetaclassRO = init.getNextOffsetFromGlobal();
assert(startOfMetaclassRO.isMultipleOf(IGM.getPointerSize()));
{
auto classRO = init.beginStruct();
builder.emitRODataFields(classRO, ForMetaClass);
builder.emitRODataFields(classRO, ForMetaClass, DoesNotHaveUpdateCallback);
classRO.finishAndAddTo(init);
}

Expand Down
5 changes: 5 additions & 0 deletions lib/IRGen/GenClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ namespace irgen {
ForMetaClass = true
};

enum HasUpdateCallback_t : bool {
DoesNotHaveUpdateCallback = false,
HasUpdateCallback = true
};

std::pair<Size,Size>
emitClassPrivateDataFields(IRGenModule &IGM,
ConstantStructBuilder &builder,
Expand Down
22 changes: 22 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2725,6 +2725,28 @@ IRGenModule::getAddrOfMetaclassObject(ClassDecl *decl,
return addr;
}

/// Fetch the declaration of an Objective-C metadata update callback.
llvm::Function *
IRGenModule::getAddrOfObjCMetadataUpdateFunction(ClassDecl *classDecl,
ForDefinition_t forDefinition) {
IRGen.noteUseOfTypeMetadata(classDecl);

LinkEntity entity = LinkEntity::forObjCMetadataUpdateFunction(classDecl);
llvm::Function *&entry = GlobalFuncs[entity];
if (entry) {
if (forDefinition) updateLinkageForDefinition(*this, entry, entity);
return entry;
}

// Class _Nullable callback(Class _Nonnull cls, void * _Nullable arg);
llvm::Type *params[] = { ObjCClassPtrTy, Int8PtrTy };
auto fnType = llvm::FunctionType::get(ObjCClassPtrTy, params, false);
Signature signature(fnType, llvm::AttributeList(), DefaultCC);
LinkInfo link = LinkInfo::get(*this, entity, forDefinition);
entry = createFunction(*this, link, signature);
return entry;
}

/// Fetch the type metadata access function for a non-generic type.
llvm::Function *
IRGenModule::getAddrOfTypeMetadataAccessFunction(CanType type,
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ class IRGenMangler : public Mangle::ASTMangler {
return mangleNominalTypeSymbol(Decl, "Mm");
}

std::string mangleObjCMetadataUpdateFunction(const ClassDecl *Decl) {
return mangleNominalTypeSymbol(Decl, "MU");
}

std::string mangleClassMetadataBaseOffset(const ClassDecl *Decl) {
return mangleNominalTypeSymbol(Decl, "Mo");
}
Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,9 @@ private: \
Address getAddrOfObjCClassRef(ClassDecl *D);
llvm::Constant *getAddrOfMetaclassObject(ClassDecl *D,
ForDefinition_t forDefinition);
llvm::Function *getAddrOfObjCMetadataUpdateFunction(ClassDecl *D,
ForDefinition_t forDefinition);

llvm::Function *getAddrOfSILFunction(SILFunction *f,
ForDefinition_t forDefinition);
llvm::Function *getAddrOfContinuationPrototype(CanSILFunctionType fnType);
Expand Down
6 changes: 6 additions & 0 deletions lib/IRGen/Linking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ std::string LinkEntity::mangleAsString() const {
case Kind::SwiftMetaclassStub:
return mangler.mangleClassMetaClass(cast<ClassDecl>(getDecl()));

case Kind::ObjCMetadataUpdateFunction:
return mangler.mangleObjCMetadataUpdateFunction(cast<ClassDecl>(getDecl()));

case Kind::ClassMetadataBaseOffset: // class metadata base offset
return mangler.mangleClassMetadataBaseOffset(cast<ClassDecl>(getDecl()));

Expand Down Expand Up @@ -351,6 +354,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
return SILLinkage::Private;
}

case Kind::ObjCMetadataUpdateFunction:
case Kind::TypeMetadataInstantiationCache:
case Kind::TypeMetadataInstantiationFunction:
case Kind::TypeMetadataSingletonInitializationCache:
Expand Down Expand Up @@ -615,6 +619,7 @@ bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const {
case Kind::DefaultAssociatedConformanceAccessor:
return false;

case Kind::ObjCMetadataUpdateFunction:
case Kind::ValueWitness:
case Kind::TypeMetadataAccessFunction:
case Kind::TypeMetadataLazyCacheVariable:
Expand Down Expand Up @@ -785,6 +790,7 @@ const SourceFile *LinkEntity::getSourceFileForEmission() const {
case Kind::ObjCClass:
case Kind::ObjCMetaclass:
case Kind::SwiftMetaclassStub:
case Kind::ObjCMetadataUpdateFunction:
case Kind::ClassMetadataBaseOffset:
case Kind::PropertyDescriptor:
case Kind::NominalTypeDescriptor:
Expand Down
Loading