Skip to content

[SR-589] Plumbing for dynamic instantiation of generic types #959

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

Closed
wants to merge 1 commit into from
Closed
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
10 changes: 8 additions & 2 deletions docs/ABI.rst
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,14 @@ layout is as follows:
* For each type parameter **n**, the following fields are stored:

+ The **number of witnesses** for the type parameter is stored at
**offset 10+n**. This is the number of witness table pointers that are
stored for the type parameter in the generic parameter vector.
**offset 10+p**. This is the number of witness table pointers that are
stored for the type parameter in the generic parameter vector. The
inner offset **p** is advanced by **1+w** for each parameter, where
**w** is the number of witness tables for the type. This accounts for
the variable length protocol descriptor references below.
+ For each type parameter with protocol contraints that require a witness
table, **n** references to the **protocol descriptors**. References are
stored as an offset relative to the start of the nominal type descriptor.

Note that there is no nominal type descriptor for protocols or protocol types.
See the `protocol descriptor`_ description below.
Expand Down
32 changes: 25 additions & 7 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,8 @@ struct HeapMetadata : Metadata {
constexpr HeapMetadata(const Metadata &base) : Metadata(base) {}
};

struct ProtocolDescriptor;

/// Header for a generic parameter descriptor. This is a variable-sized
/// structure that describes how to find and parse a generic parameter vector
/// within the type metadata for an instance of a nominal type.
Expand All @@ -1199,15 +1201,33 @@ struct GenericParameterDescriptor {
struct Parameter {
/// The number of protocol witness tables required by this type parameter.
uint32_t NumWitnessTables;

// TODO: This is the bare minimum to be able to parse an opaque generic
// parameter vector. Should we include additional info, such as the
// required protocols?
/// The protocols required by this type parameter. If NumWitnessTables is
/// zero, this is absent.
RelativeIndirectablePointer<ProtocolDescriptor> Protocols[1];
};

/// The parameter descriptors are in a tail-emplaced array of NumParams
/// elements.
/// elements. Because Parameters are variable length, use getParameterAt()
/// to access them.
Parameter Parameters[1];

const struct Parameter *getParameterAt(uint32_t index) const {
const struct Parameter *param = Parameters;

for (uint32_t i = 0; i < NumParams; ++i) {
if (i == index)
return param;

auto offset = sizeof(struct Parameter)
- offsetof(struct Parameter, Protocols);
offset += param->NumWitnessTables *
sizeof(RelativeIndirectablePointer<ProtocolDescriptor>);
auto bytes = reinterpret_cast<const uint8_t *>(param) + offset;
param = reinterpret_cast<const Parameter *>(bytes);
}

return nullptr;
}
};

struct ClassTypeDescriptor;
Expand Down Expand Up @@ -1858,8 +1878,6 @@ struct TupleTypeMetadata : public Metadata {
/// The standard metadata for the empty tuple type.
extern "C" const FullMetadata<TupleTypeMetadata> _TMT_;

struct ProtocolDescriptor;

/// An array of protocol descriptors with a header and tail-allocated elements.
struct ProtocolDescriptorList {
uintptr_t NumProtocols;
Expand Down
14 changes: 6 additions & 8 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2270,8 +2270,6 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(CanType concreteType,
if (!section.empty())
var->setSection(section);

// Keep type metadata around for all types, although the runtime can currently
// only perform name lookup of non-generic types.
addRuntimeResolvableType(concreteType);

// For metadata patterns, we're done.
Expand Down Expand Up @@ -2477,15 +2475,15 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) {
return result;
}

/// Return the address of a nominal type descriptor. Right now, this
/// must always be for purposes of defining it.
/// Return the address of a nominal type descriptor.
llvm::Constant *IRGenModule::getAddrOfNominalTypeDescriptor(NominalTypeDecl *D,
llvm::Type *definitionType) {
assert(definitionType && "not defining nominal type descriptor?");
auto entity = LinkEntity::forNominalTypeDescriptor(D);
return getAddrOfLLVMVariable(entity, getPointerAlignment(),
definitionType, definitionType,
DebugTypeInfo());
DebugTypeInfo DbgTy(D->getDeclaredType(), NominalTypeDescriptorPtrTy,
getPointerSize(), getPointerAlignment(),
nullptr);
return getAddrOfLLVMVariable(entity, getPointerAlignment(), definitionType,
NominalTypeDescriptorPtrTy, DbgTy);
}

llvm::Constant *IRGenModule::getAddrOfProtocolDescriptor(ProtocolDecl *D,
Expand Down
36 changes: 36 additions & 0 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2025,6 +2025,31 @@ namespace {
class NominalTypeDescriptorBuilderBase : public ConstantBuilder<> {
Impl &asImpl() { return *static_cast<Impl*>(this); }

llvm::Constant *getBaseAddress() {
return IGM.getAddrOfNominalTypeDescriptor(asImpl().getTarget(), nullptr);
}

llvm::Constant *
emitRelativeReference(std::pair<llvm::Constant *,
IRGenModule::DirectOrGOT> target) {
auto targetAddr = llvm::ConstantExpr::getPtrToInt(target.first, IGM.SizeTy);
auto offset = llvm::ConstantInt::get(IGM.SizeTy, getNextOffset().getValue());
// This seems incredibly baroque, we should be creating an LLVM struct?
// not sure how to define one with nested tail-emplaced values
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would an anonymous LLVM struct type work?

auto baseAddr = llvm::ConstantExpr::getPtrToInt(getBaseAddress(), IGM.SizeTy);
auto currentAddr = llvm::ConstantExpr::getAdd(baseAddr, offset);
auto relativeAddr = llvm::ConstantExpr::getSub(targetAddr, currentAddr);

if (IGM.SizeTy != IGM.RelativeAddressTy)
relativeAddr = llvm::ConstantExpr::getTrunc(relativeAddr,
IGM.RelativeAddressTy);
if (target.second == IRGenModule::DirectOrGOT::GOT)
relativeAddr = llvm::ConstantExpr::getAdd(relativeAddr,
llvm::ConstantInt::get(IGM.RelativeAddressTy, 1));

return relativeAddr;
}

public:
NominalTypeDescriptorBuilderBase(IRGenModule &IGM) : ConstantBuilder(IGM) {}

Expand Down Expand Up @@ -2098,6 +2123,17 @@ namespace {
archetype->getConformsTo().end(),
Lowering::TypeConverter::protocolRequiresWitnessTable);
addConstantInt32(count);

// ProtocolDescriptor *Protocols[NumWitnessTables];
for (auto *protocol : archetype->getConformsTo()) {
if (!Lowering::TypeConverter::protocolRequiresWitnessTable(protocol))
continue;
assert(!protocol->isObjC());
auto descriptorRef = IGM.getAddrOfLLVMVariableOrGOTEquivalent(
LinkEntity::forProtocolDescriptor(protocol),
IGM.getPointerAlignment(), IGM.ProtocolDescriptorStructTy);
addInt32(emitRelativeReference(descriptorRef));
}
}
// };
}
Expand Down
8 changes: 4 additions & 4 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -839,6 +839,10 @@ private: \
/// the binary.
void setTrueConstGlobal(llvm::GlobalVariable *var);

std::pair<llvm::Constant *, DirectOrGOT>
getAddrOfLLVMVariableOrGOTEquivalent(LinkEntity entity, Alignment alignment,
llvm::Type *defaultType);

private:
llvm::Constant *getAddrOfLLVMVariable(LinkEntity entity,
Alignment alignment,
Expand All @@ -851,10 +855,6 @@ private: \
llvm::Type *defaultType,
DebugTypeInfo debugType);

std::pair<llvm::Constant *, DirectOrGOT>
getAddrOfLLVMVariableOrGOTEquivalent(LinkEntity entity, Alignment alignment,
llvm::Type *defaultType);

void emitLazyPrivateDefinitions();
void addRuntimeResolvableType(CanType type);

Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ if(SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_VARIANTS}")
set(swift_runtime_objc_sources
ErrorObject.mm
SwiftObject.mm
Remangle.cpp
Reflection.mm)
else()
endif()
Expand All @@ -46,6 +45,7 @@ add_swift_library(swiftRuntime IS_STDLIB IS_STDLIB_CORE
Once.cpp
ProtocolConformance.cpp
Reflection.cpp
Remangle.cpp
SwiftObject.cpp
${swift_runtime_objc_sources}
${swift_runtime_leaks_sources}
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,9 +1474,9 @@ static void _swift_initializeSuperclass(ClassMetadata *theClass,
if (genericParams.hasGenericParams()) {
unsigned numParamWords = 0;
for (unsigned i = 0; i < genericParams.NumParams; ++i) {
auto param = genericParams.getParameterAt(i);
// 1 word for the type metadata, and 1 for every protocol witness
numParamWords +=
1 + genericParams.Parameters[i].NumWitnessTables;
numParamWords += 1 + param->NumWitnessTables;
}
memcpy(classWords + genericParams.Offset,
superWords + genericParams.Offset,
Expand Down
Loading