Skip to content

Commit

Permalink
Runtime/IRGen: Preliminary plumbing for subclass existentials
Browse files Browse the repository at this point in the history
  • Loading branch information
slavapestov committed Apr 14, 2017
1 parent 5993b63 commit a5a40c7
Show file tree
Hide file tree
Showing 6 changed files with 174 additions and 51 deletions.
9 changes: 8 additions & 1 deletion include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -2441,6 +2441,11 @@ struct TargetExistentialTypeMetadata : public TargetMetadata<Runtime> {
return Flags.getClassConstraint() == ProtocolClassConstraint::Class;
}

const Metadata *getSuperclassConstraint() const {
// FIXME
return nullptr;
}

static bool classof(const TargetMetadata<Runtime> *metadata) {
return metadata->getKind() == MetadataKind::Existential;
}
Expand Down Expand Up @@ -3176,7 +3181,9 @@ swift_getExistentialMetatypeMetadata(const Metadata *instanceType);
/// referenced by \c protocols will be sorted in-place.
SWIFT_RT_ENTRY_VISIBILITY
const ExistentialTypeMetadata *
swift_getExistentialTypeMetadata(size_t numProtocols,
swift_getExistentialTypeMetadata(ProtocolClassConstraint classConstraint,
const Metadata *superclassConstraint,
size_t numProtocols,
const ProtocolDescriptor **protocols)
SWIFT_CC(RegisterPreservingCC);

Expand Down
9 changes: 7 additions & 2 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -791,12 +791,17 @@ FUNCTION(GetTupleMetadata3, swift_getTupleTypeMetadata3, DefaultCC,
Int8PtrTy, WitnessTablePtrTy),
ATTRS(NoUnwind, ReadOnly))

// Metadata *swift_getExistentialTypeMetadata(size_t numProtocols,
// Metadata *swift_getExistentialTypeMetadata(
// ProtocolClassConstraint classConstraint,
// const Metadata *superclassConstraint,
// size_t numProtocols,
// const protocol_descriptor_t * const *protocols);
//
// Note: ProtocolClassConstraint::Class is 0, ::Any is 1.
FUNCTION(GetExistentialMetadata,
swift_getExistentialTypeMetadata, RegisterPreservingCC,
RETURNS(TypeMetadataPtrTy),
ARGS(SizeTy,
ARGS(Int1Ty, TypeMetadataPtrTy, SizeTy,
ProtocolDescriptorPtrTy->getPointerTo()),
ATTRS(NoUnwind, ReadOnly))

Expand Down
33 changes: 26 additions & 7 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/ABI/MetadataValues.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/CanTypeVisitor.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Decl.h"
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/SubstitutionMap.h"
Expand Down Expand Up @@ -917,9 +918,13 @@ namespace {
}

llvm::Value *emitExistentialTypeMetadata(CanType type) {
SmallVector<ProtocolDecl*, 2> protocols;
type.getExistentialTypeProtocols(protocols);

if (auto metatype = tryGetLocal(type))
return metatype;

auto layout = type.getExistentialLayout();

auto protocols = layout.getProtocols();

// Collect references to the protocol descriptors.
auto descriptorArrayTy
= llvm::ArrayType::get(IGF.IGM.ProtocolDescriptorPtrTy,
Expand All @@ -933,16 +938,30 @@ namespace {
IGF.IGM.ProtocolDescriptorPtrTy->getPointerTo());

unsigned index = 0;
for (auto *p : protocols) {
llvm::Value *ref = emitProtocolDescriptorRef(IGF, p);
for (auto *protoTy : protocols) {
auto *protoDecl = protoTy->getDecl();
llvm::Value *ref = emitProtocolDescriptorRef(IGF, protoDecl);
Address slot = IGF.Builder.CreateConstArrayGEP(descriptorArray,
index, IGF.IGM.getPointerSize());
IGF.Builder.CreateStore(ref, slot);
++index;
}


// Note: ProtocolClassConstraint::Class is 0, ::Any is 1.
auto classConstraint =
llvm::ConstantInt::get(IGF.IGM.Int1Ty,
!layout.requiresClass);
llvm::Value *superclassConstraint =
llvm::ConstantPointerNull::get(IGF.IGM.TypeMetadataPtrTy);
if (layout.superclass) {
superclassConstraint = IGF.emitTypeMetadataRef(
CanType(layout.superclass));
}

auto call = IGF.Builder.CreateCall(IGF.IGM.getGetExistentialMetadataFn(),
{IGF.IGM.getSize(Size(protocols.size())),
{classConstraint,
superclassConstraint,
IGF.IGM.getSize(Size(protocols.size())),
descriptorArray.getAddress()});
call->setDoesNotThrow();
IGF.Builder.CreateLifetimeEnd(descriptorArray,
Expand Down
61 changes: 49 additions & 12 deletions stdlib/public/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2047,7 +2047,9 @@ class ExistentialCacheEntry {
FullMetadata<ExistentialTypeMetadata> Data;

struct Key {
size_t NumProtocols;
const Metadata *SuperclassConstraint;
ProtocolClassConstraint ClassConstraint : 1;
size_t NumProtocols : 31;
const ProtocolDescriptor * const *Protocols;
};

Expand All @@ -2058,6 +2060,14 @@ class ExistentialCacheEntry {
}

int compareWithKey(Key key) const {
if (auto result = compareIntegers(key.ClassConstraint,
Data.Flags.getClassConstraint()))
return result;

if (auto result = comparePointers(key.SuperclassConstraint,
Data.getSuperclassConstraint()))
return result;

if (auto result = compareIntegers(key.NumProtocols,
Data.Protocols.NumProtocols))
return result;
Expand Down Expand Up @@ -2195,7 +2205,9 @@ ClassExistentialValueWitnessTables;
/// Instantiate a value witness table for a class-constrained existential
/// container with the given number of witness table pointers.
static const ExtraInhabitantsValueWitnessTable *
getClassExistentialValueWitnesses(unsigned numWitnessTables) {
getClassExistentialValueWitnesses(const Metadata *superclass,
unsigned numWitnessTables) {
// FIXME: If the superclass is not @objc, use native reference counting.
if (numWitnessTables == 0) {
#if SWIFT_OBJC_INTEROP
return &VALUE_WITNESS_SYM(BO);
Expand Down Expand Up @@ -2245,6 +2257,7 @@ ClassExistentialValueWitnessTableCacheEntry(unsigned numWitnessTables) {
/// shared specialized table for common cases.
static const ValueWitnessTable *
getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,
const Metadata *superclassConstraint,
unsigned numWitnessTables,
SpecialProtocol special) {
// Use special representation for special protocols.
Expand All @@ -2266,8 +2279,10 @@ getExistentialValueWitnesses(ProtocolClassConstraint classConstraint,

switch (classConstraint) {
case ProtocolClassConstraint::Class:
return getClassExistentialValueWitnesses(numWitnessTables);
return getClassExistentialValueWitnesses(superclassConstraint,
numWitnessTables);
case ProtocolClassConstraint::Any:
assert(superclassConstraint == nullptr);
return getOpaqueExistentialValueWitnesses(numWitnessTables);
}

Expand Down Expand Up @@ -2472,44 +2487,66 @@ ExistentialTypeMetadata::getWitnessTable(const OpaqueValue *container,
/// \brief Fetch a uniqued metadata for an existential type. The array
/// referenced by \c protocols will be sorted in-place.
const ExistentialTypeMetadata *
swift::swift_getExistentialTypeMetadata(size_t numProtocols,
swift::swift_getExistentialTypeMetadata(ProtocolClassConstraint classConstraint,
const Metadata *superclassConstraint,
size_t numProtocols,
const ProtocolDescriptor **protocols)
SWIFT_CC(RegisterPreservingCC_IMPL) {

// Sort the protocol set.
std::sort(protocols, protocols + numProtocols);

ExistentialCacheEntry::Key key = { numProtocols, protocols };
ExistentialCacheEntry::Key key = {
superclassConstraint, classConstraint, numProtocols, protocols
};
return &ExistentialTypes.getOrInsert(key).first->Data;
}

ExistentialCacheEntry::ExistentialCacheEntry(Key key) {
// Calculate the class constraint and number of witness tables for the
// protocol set.
unsigned numWitnessTables = 0;
ProtocolClassConstraint classConstraint = ProtocolClassConstraint::Any;
for (auto p : make_range(key.Protocols, key.Protocols + key.NumProtocols)) {
if (p->Flags.needsWitnessTable()) {
if (p->Flags.needsWitnessTable())
++numWitnessTables;
}
if (p->Flags.getClassConstraint() == ProtocolClassConstraint::Class)
}

#ifndef NDEBUG
// Verify the class constraint.
{
auto classConstraint = ProtocolClassConstraint::Any;

if (key.SuperclassConstraint)
classConstraint = ProtocolClassConstraint::Class;
else {
for (auto p : make_range(key.Protocols, key.Protocols + key.NumProtocols)) {
if (p->Flags.getClassConstraint() == ProtocolClassConstraint::Class)
classConstraint = ProtocolClassConstraint::Class;
}
}

assert(classConstraint == key.ClassConstraint);
}
#endif

// Get the special protocol kind for an uncomposed protocol existential.
// Protocol compositions are currently never special.
auto special = SpecialProtocol::None;
if (key.NumProtocols == 1)
special = key.Protocols[0]->Flags.getSpecialProtocol();

Data.setKind(MetadataKind::Existential);
Data.ValueWitnesses = getExistentialValueWitnesses(classConstraint,
Data.ValueWitnesses = getExistentialValueWitnesses(key.ClassConstraint,
key.SuperclassConstraint,
numWitnessTables,
special);
Data.Flags = ExistentialTypeFlags()
.withNumWitnessTables(numWitnessTables)
.withClassConstraint(classConstraint)
.withClassConstraint(key.ClassConstraint)
.withSpecialProtocol(special);
// FIXME
//Data.Superclass = key.superclassConstraint;
assert(!key.SuperclassConstraint);
Data.Protocols.NumProtocols = key.NumProtocols;
for (size_t i = 0; i < key.NumProtocols; ++i)
Data.Protocols[i] = key.Protocols[i];
Expand Down
6 changes: 3 additions & 3 deletions test/IRGen/protocol_metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,20 @@ func protocol_types(_ a: A,
abc: A & B & C,
abco: A & B & C & O) {
// CHECK: store %swift.protocol* @_T017protocol_metadata1AMp
// CHECK: call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i64 1, %swift.protocol** {{%.*}})
// CHECK: call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i1 true, %swift.type* null, i64 1, %swift.protocol** {{%.*}})
reify_metadata(a)
// CHECK: store %swift.protocol* @_T017protocol_metadata1AMp
// CHECK: store %swift.protocol* @_T017protocol_metadata1BMp
// CHECK: store %swift.protocol* @_T017protocol_metadata1CMp
// CHECK: call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i64 3, %swift.protocol** {{%.*}})
// CHECK: call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i1 false, %swift.type* null, i64 3, %swift.protocol** {{%.*}})
reify_metadata(abc)
// CHECK: store %swift.protocol* @_T017protocol_metadata1AMp
// CHECK: store %swift.protocol* @_T017protocol_metadata1BMp
// CHECK: store %swift.protocol* @_T017protocol_metadata1CMp
// CHECK: [[O_REF:%.*]] = load i8*, i8** @"\01l_OBJC_PROTOCOL_REFERENCE_$__TtP17protocol_metadata1O_"
// CHECK: [[O_REF_BITCAST:%.*]] = bitcast i8* [[O_REF]] to %swift.protocol*
// CHECK: store %swift.protocol* [[O_REF_BITCAST]]
// CHECK: call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i64 4, %swift.protocol** {{%.*}})
// CHECK: call %swift.type* @swift_rt_swift_getExistentialTypeMetadata(i1 false, %swift.type* null, i64 4, %swift.protocol** {{%.*}})
reify_metadata(abco)
}

Loading

0 comments on commit a5a40c7

Please sign in to comment.