Skip to content

[Runtime] Improvements to foreign class metadata #13704

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
13 changes: 13 additions & 0 deletions docs/ABI/TypeMetadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,19 @@ parameter vector contains witness tables for those protocols, as if laid out::
AnsibleWitnessTable *U_Ansible;
};

Foreign Class Metadata
~~~~~~~~~~~~~~~~~~~~~~

Foreign class metadata describes "foreign" class types, which support Swift
reference counting but are otherwise opaque to the Swift runtime.

- The `nominal type descriptor`_ for the most-derived class type is stored at
**offset 0**.
- The **super pointer** pointing to the metadata record for the superclass is
stored at **offset 1**. If the class is a root class, it is null.
- Three **pointer-sized fields**, starting at **offset 2**, are reserved for
future use.

Nominal Type Descriptor
~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
8 changes: 7 additions & 1 deletion include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,7 @@ template <typename Runtime> struct TargetClassMetadata;
template <typename Runtime> struct TargetStructMetadata;
template <typename Runtime> struct TargetOpaqueMetadata;
template <typename Runtime> struct TargetValueMetadata;
template <typename Runtime> struct TargetForeignClassMetadata;

// FIXME: https://bugs.swift.org/browse/SR-1155
#pragma clang diagnostic push
Expand Down Expand Up @@ -907,6 +908,8 @@ struct TargetMetadata {
return static_cast<const TargetValueMetadata<Runtime> *>(this)
->Description;
case MetadataKind::ForeignClass:
return static_cast<const TargetForeignClassMetadata<Runtime> *>(this)
->Description;
case MetadataKind::Opaque:
case MetadataKind::Tuple:
case MetadataKind::Function:
Expand Down Expand Up @@ -1812,9 +1815,12 @@ struct TargetForeignClassMetadata
: public TargetForeignTypeMetadata<Runtime> {
using StoredPointer = typename Runtime::StoredPointer;

/// An out-of-line description of the type.
const TargetNominalTypeDescriptor<Runtime> *Description;

/// The superclass of the foreign class, if any.
ConstTargetMetadataPointer<Runtime, swift::TargetForeignClassMetadata>
SuperClass;
SuperClass;

/// Reserved space. For now, these should be zero-initialized.
StoredPointer Reserved[3];
Expand Down
84 changes: 84 additions & 0 deletions lib/IRGen/ForeignClassMetadataVisitor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//===-- ForeignClassMetadataVisitor.h - foreign class metadata -*- C++ --*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// A CRTP class useful for laying out foreign class metadata.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_IRGEN_FOREIGNCLASSMETADATAVISITOR_H
#define SWIFT_IRGEN_FOREIGNCLASSMETADATAVISITOR_H

#include "NominalMetadataVisitor.h"

namespace swift {
namespace irgen {

/// A CRTP layout class for foreign class metadata.
template <class Impl>
class ForeignClassMetadataVisitor
: public NominalMetadataVisitor<Impl> {
using super = NominalMetadataVisitor<Impl>;
protected:
ClassDecl *Target;
using super::asImpl;
public:
ForeignClassMetadataVisitor(IRGenModule &IGM, ClassDecl *target)
: super(IGM), Target(target) {}

void layout() {
super::layout();
asImpl().addNominalTypeDescriptor();
asImpl().noteStartOfSuperClass();
asImpl().addSuperClass();
asImpl().addReservedWord();
asImpl().addReservedWord();
asImpl().addReservedWord();
}

bool requiresInitializationFunction() {
return Target->getSuperclassDecl() &&
Target->getSuperclassDecl()->isForeign();
}

CanType getTargetType() const {
return Target->getDeclaredType()->getCanonicalType();
}
};

/// An "implementation" of ForeignClassMetadataVisitor that just scans through
/// the metadata layout, maintaining the offset of the next field.
template <class Impl>
class ForeignClassMetadataScanner : public ForeignClassMetadataVisitor<Impl> {
typedef ForeignClassMetadataVisitor<Impl> super;
protected:
Size NextOffset = Size(0);

ForeignClassMetadataScanner(IRGenModule &IGM, ClassDecl *target)
: super(IGM, target) {}

public:
void addMetadataFlags() { addPointer(); }
void addValueWitnessTable() { addPointer(); }
void addNominalTypeDescriptor() { addPointer(); }
void addSuperClass() { addPointer(); }
void addReservedWord() { addPointer(); }

private:
void addPointer() {
NextOffset += super::IGM.getPointerSize();
}
};

} // end namespace irgen
} // end namespace swift

#endif // SWIFT_IRGEN_FOREIGNCLASSMETADATAVISITOR_H
4 changes: 2 additions & 2 deletions lib/IRGen/GenKeyPath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -903,11 +903,11 @@ IRGenModule::getAddrOfKeyPathPattern(KeyPathPattern *pattern,
} else {
idKind = KeyPathComponentHeader::VTableOffset;
auto dc = declRef.getDecl()->getDeclContext();
if (isa<ClassDecl>(dc)) {
if (isa<ClassDecl>(dc) && !cast<ClassDecl>(dc)->isForeign()) {
auto overridden = declRef.getOverriddenVTableEntry();
auto declaringClass =
cast<ClassDecl>(overridden.getDecl()->getDeclContext());
auto &metadataLayout = getMetadataLayout(declaringClass);
auto &metadataLayout = getClassMetadataLayout(declaringClass);
// FIXME: Resilience. We don't want vtable layout to be ABI, so this
// should be encoded as a reference to the method dispatch thunk
// instead.
Expand Down
119 changes: 68 additions & 51 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "ConstantBuilder.h"
#include "EnumMetadataVisitor.h"
#include "FixedTypeInfo.h"
#include "ForeignClassMetadataVisitor.h"
#include "GenArchetype.h"
#include "GenClass.h"
#include "GenDecl.h"
Expand Down Expand Up @@ -296,18 +297,24 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF,
}

/// Emit a reference to the type metadata for a foreign type.
static llvm::Value *emitForeignTypeMetadataRef(IRGenFunction &IGF,
CanType type) {
llvm::Value *candidate = IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type);
static llvm::Value *uniqueForeignTypeMetadataRef(IRGenFunction &IGF,
llvm::Value *candidate) {
auto call = IGF.Builder.CreateCall(IGF.IGM.getGetForeignTypeMetadataFn(),
candidate);
candidate);
call->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::NoUnwind);
call->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadNone);
return call;
}

/// Emit a reference to the type metadata for a foreign type.
static llvm::Value *emitForeignTypeMetadataRef(IRGenFunction &IGF,
CanType type) {
llvm::Value *candidate = IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type);
return uniqueForeignTypeMetadataRef(IGF, candidate);
}

/// Returns a metadata reference for a nominal type.
///
/// This is only valid in a couple of special cases:
Expand Down Expand Up @@ -2189,11 +2196,13 @@ namespace {
// GenericParameterDescriptorFlags Flags;
GenericParameterDescriptorFlags flags;
if (auto *cd = dyn_cast<ClassDecl>(ntd)) {
auto &layout = IGM.getMetadataLayout(cd);
if (layout.getVTableSize() > 0)
flags = flags.withHasVTable(true);
if (layout.hasResilientSuperclass())
flags = flags.withHasResilientSuperclass(true);
if (!cd->isForeign()) {
auto &layout = IGM.getClassMetadataLayout(cd);
if (layout.getVTableSize() > 0)
flags = flags.withHasVTable(true);
if (layout.hasResilientSuperclass())
flags = flags.withHasResilientSuperclass(true);
}
}

// Calculate the number of generic parameters at each nesting depth.
Expand Down Expand Up @@ -2428,7 +2437,13 @@ namespace {
ClassDecl *c)
: super(IGM), Target(c)
{
auto &layout = IGM.getMetadataLayout(Target);
if (Target->isForeign()) {
VTable = nullptr;
VTableSize = 0;
return;
}

auto &layout = IGM.getClassMetadataLayout(Target);

VTable = IGM.getSILModule().lookUpVTable(Target);
VTableSize = layout.getVTableSize();
Expand Down Expand Up @@ -2834,7 +2849,7 @@ namespace {
// fill indexes are word-indexed.
auto *metadataWords = IGF.Builder.CreateBitCast(metadataValue, IGM.Int8PtrPtrTy);

auto genericReqtOffset = IGM.getMetadataLayout(Target)
auto genericReqtOffset = IGM.getNominalMetadataLayout(Target)
.getGenericRequirementsOffset(IGF);

for (auto &fillOp : FillOps) {
Expand Down Expand Up @@ -3145,7 +3160,7 @@ namespace {
auto fn = entry.Method;

auto *classDecl = cast<ClassDecl>(fn.getDecl()->getDeclContext());
auto &layout = IGM.getMetadataLayout(classDecl);
auto &layout = IGM.getClassMetadataLayout(classDecl);

auto offset = layout.getMethodInfo(IGF, fn).getOffset();

Expand Down Expand Up @@ -3493,7 +3508,7 @@ namespace {
if (!HasResilientSuperclass)
return;

auto &layout = IGM.getMetadataLayout(Target);
auto &layout = IGM.getClassMetadataLayout(Target);

// Load the size of the superclass metadata.
Address metadataAsBytes(
Expand Down Expand Up @@ -3747,7 +3762,7 @@ namespace {
if (doesClassMetadataRequireDynamicInitialization(IGM, Target)) {
auto templateSize = IGM.getSize(Size(B.getNextOffsetFromGlobal()));
auto numImmediateMembers = IGM.getSize(
Size(IGM.getMetadataLayout(Target).getNumImmediateMembers()));
Size(IGM.getClassMetadataLayout(Target).getNumImmediateMembers()));
metadata = IGF.Builder.CreateCall(IGF.IGM.getRelocateClassMetadataFn(),
{metadata, templateSize,
numImmediateMembers});
Expand Down Expand Up @@ -3838,7 +3853,7 @@ namespace {
}

auto numImmediateMembers =
IGM.getSize(Size(IGM.getMetadataLayout(Target).getNumImmediateMembers()));
IGM.getSize(Size(IGM.getClassMetadataLayout(Target).getNumImmediateMembers()));

return IGF.Builder.CreateCall(IGM.getAllocateGenericClassMetadataFn(),
{metadataPattern, arguments, superMetadata,
Expand Down Expand Up @@ -4230,7 +4245,7 @@ std::pair<llvm::Value *, llvm::Value *>
irgen::emitClassResilientInstanceSizeAndAlignMask(IRGenFunction &IGF,
ClassDecl *theClass,
llvm::Value *metadata) {
auto &layout = IGF.IGM.getMetadataLayout(theClass);
auto &layout = IGF.IGM.getClassMetadataLayout(theClass);

Address metadataAsBytes(IGF.Builder.CreateBitCast(metadata, IGF.IGM.Int8PtrTy),
IGF.IGM.getPointerAlignment());
Expand Down Expand Up @@ -4487,7 +4502,7 @@ FunctionPointer irgen::emitVirtualMethodValue(IRGenFunction &IGF,
auto declaringClass = cast<ClassDecl>(overridden.getDecl()->getDeclContext());

auto methodInfo =
IGF.IGM.getMetadataLayout(declaringClass).getMethodInfo(IGF, overridden);
IGF.IGM.getClassMetadataLayout(declaringClass).getMethodInfo(IGF, overridden);
auto offset = methodInfo.getOffset();

auto slot = IGF.emitAddressAtOffset(metadata, offset,
Expand Down Expand Up @@ -4887,36 +4902,6 @@ llvm::Value *IRGenFunction::emitObjCSelectorRefLoad(StringRef selector) {
//===----------------------------------------------------------------------===//

namespace {
/// A CRTP layout class for foreign class metadata.
template <class Impl>
class ForeignClassMetadataVisitor
: public NominalMetadataVisitor<Impl> {
using super = NominalMetadataVisitor<Impl>;
protected:
ClassDecl *Target;
using super::asImpl;
public:
ForeignClassMetadataVisitor(IRGenModule &IGM, ClassDecl *target)
: super(IGM), Target(target) {}

void layout() {
super::layout();
asImpl().addSuperClass();
asImpl().addReservedWord();
asImpl().addReservedWord();
asImpl().addReservedWord();
}

bool requiresInitializationFunction() {
// TODO: superclasses?
return false;
}

CanType getTargetType() const {
return Target->getDeclaredType()->getCanonicalType();
}
};

/// An adapter that turns a metadata layout class into a foreign metadata
/// layout class.
///
Expand Down Expand Up @@ -5033,8 +5018,23 @@ namespace {
: ForeignMetadataBuilderBase(IGM, target, B) {}

void emitInitialization(IRGenFunction &IGF, llvm::Value *metadata) {
// TODO: superclasses?
llvm_unreachable("no supported forms of initialization");
// Dig out the address of the superclass field.
auto &layout = IGF.IGM.getForeignMetadataLayout(Target);
Address metadataWords(IGF.Builder.CreateBitCast(metadata,
IGM.Int8PtrPtrTy),
IGM.getPointerAlignment());
auto superclassField =
createPointerSizedGEP(IGF, metadataWords,
layout.getSuperClassOffset().getStaticOffset());
superclassField =
IGF.Builder.CreateBitCast(
superclassField,
llvm::PointerType::get(IGM.TypeMetadataPtrTy, 0));

// Unique the superclass field and write it back.
auto superclass = IGF.Builder.CreateLoad(superclassField);
auto uniquedSuperclass = uniqueForeignTypeMetadataRef(IGF, superclass);
IGF.Builder.CreateStore(uniquedSuperclass, superclassField);
}

// Visitor methods.
Expand All @@ -5053,9 +5053,26 @@ namespace {
B.addInt(IGM.MetadataKindTy, (unsigned) MetadataKind::ForeignClass);
}

void addNominalTypeDescriptor() {
auto descriptor = ClassNominalTypeDescriptorBuilder(this->IGM, Target).emit();
B.add(descriptor);
}

void noteStartOfSuperClass() { }

void addSuperClass() {
// TODO: superclasses
B.addNullPointer(IGM.TypeMetadataPtrTy);
auto superclassDecl = Target->getSuperclassDecl();
if (!superclassDecl || !superclassDecl->isForeign()) {
B.addNullPointer(IGM.TypeMetadataPtrTy);
return;
}

auto superclassType =
superclassDecl->swift::TypeDecl::getDeclaredInterfaceType()
->getCanonicalType();
auto superclass =
IGM.getAddrOfForeignTypeMetadataCandidate(superclassType);
B.add(superclass);
}

void addReservedWord() {
Expand Down
7 changes: 5 additions & 2 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ namespace irgen {
class EnumMetadataLayout;
class ExplosionSchema;
class FixedTypeInfo;
class ForeignClassMetadataLayout;
class ForeignFunctionInfo;
class FormalType;
class HeapLayout;
Expand Down Expand Up @@ -667,10 +668,12 @@ class IRGenModule {

SpareBitVector getSpareBitsForType(llvm::Type *scalarTy, Size size);

NominalMetadataLayout &getMetadataLayout(NominalTypeDecl *decl);
MetadataLayout &getMetadataLayout(NominalTypeDecl *decl);
NominalMetadataLayout &getNominalMetadataLayout(NominalTypeDecl *decl);
StructMetadataLayout &getMetadataLayout(StructDecl *decl);
ClassMetadataLayout &getMetadataLayout(ClassDecl *decl);
ClassMetadataLayout &getClassMetadataLayout(ClassDecl *decl);
EnumMetadataLayout &getMetadataLayout(EnumDecl *decl);
ForeignClassMetadataLayout &getForeignMetadataLayout(ClassDecl *decl);

private:
TypeConverter &Types;
Expand Down
Loading