Skip to content

Finish the first stage of incomplete type metadata support #15671

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 5 commits into from
Apr 2, 2018
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
2 changes: 1 addition & 1 deletion include/swift/Runtime/Debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ struct RuntimeErrorDetails {
uintptr_t framesToSkip;

// Address of some associated object (if there's any).
void *memoryAddress;
const void *memoryAddress;

// A structure describing an extra thread (and its stack) that is related.
struct Thread {
Expand Down
75 changes: 51 additions & 24 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -195,25 +195,45 @@ using Metadata = TargetMetadata<InProcess>;
/// For performance, functions returning this type should use SWIFT_CC so
/// that the components are returned as separate values.
struct MetadataResponse {
/// For metadata access functions, this is the requested metadata.
///
/// For metadata initialization functions, this is either null,
/// indicating that initialization was successful, or a metadata on
/// which initialization depends for further progress.
/// The requested metadata.
const Metadata *Value;

/// For metadata access functions, this is the current state of the
/// metadata returned. Always use this instead of trying to inspect
/// the metadata directly; an incomplete metadata may be getting
/// initialized concurrently. This can generally be ignored if the
/// metadata request was for abstract metadata or if the request is
/// blocking.
///
/// For metadata initialization functions, this is the state that the
/// given metadata needs to be in before initialization can continue.
/// The current state of the metadata returned. Always use this
/// instead of trying to inspect the metadata directly to see if it
/// satisfies the request. An incomplete metadata may be getting
/// initialized concurrently. But this can generally be ignored if
/// the metadata request was for abstract metadata or if the request
/// is blocking.
MetadataState State;
};
using MetadataDependency = MetadataResponse;

/// A dependency on the metadata progress of other type, indicating that
/// initialization of a metadata cannot progress until another metadata
/// reaches a particular state.
///
/// For performance, functions returning this type should use SWIFT_CC so
/// that the components are returned as separate values.
struct MetadataDependency {
/// Either null, indicating that initialization was successful, or
/// a metadata on which initialization depends for further progress.
const Metadata *Value;

/// The state that Metadata needs to be in before initialization
/// can continue.
MetadataState Requirement;

MetadataDependency() : Value(nullptr) {}
MetadataDependency(const Metadata *metadata, MetadataState requirement)
: Value(metadata), Requirement(requirement) {}

explicit operator bool() const { return Value != nullptr; }

bool operator==(MetadataDependency other) const {
assert(Value && other.Value);
return Value == other.Value &&
Requirement == other.Requirement;
}
};

template <typename Runtime> struct TargetProtocolConformanceDescriptor;

Expand Down Expand Up @@ -1848,6 +1868,10 @@ struct TargetTupleTypeMetadata : public TargetMetadata<Runtime> {
OpaqueValue *findIn(OpaqueValue *tuple) const {
return (OpaqueValue*) (((char*) tuple) + Offset);
}

const TypeLayout *getTypeLayout() const {
return Type->getTypeLayout();
}
};

Element *getElements() {
Expand Down Expand Up @@ -4160,21 +4184,24 @@ swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique);
/// \param proposedWitnesses - an optional proposed set of value witnesses.
/// This is useful when working with a non-dependent tuple type
/// where the entrypoint is just being used to unique the metadata.
SWIFT_RUNTIME_EXPORT
const TupleTypeMetadata *
swift_getTupleTypeMetadata(TupleTypeFlags flags,
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
MetadataResponse
swift_getTupleTypeMetadata(MetadataRequest request,
TupleTypeFlags flags,
const Metadata * const *elements,
const char *labels,
const ValueWitnessTable *proposedWitnesses);

SWIFT_RUNTIME_EXPORT
const TupleTypeMetadata *
swift_getTupleTypeMetadata2(const Metadata *elt0, const Metadata *elt1,
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
MetadataResponse
swift_getTupleTypeMetadata2(MetadataRequest request,
const Metadata *elt0, const Metadata *elt1,
const char *labels,
const ValueWitnessTable *proposedWitnesses);
SWIFT_RUNTIME_EXPORT
const TupleTypeMetadata *
swift_getTupleTypeMetadata3(const Metadata *elt0, const Metadata *elt1,
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
MetadataResponse
swift_getTupleTypeMetadata3(MetadataRequest request,
const Metadata *elt0, const Metadata *elt1,
const Metadata *elt2, const char *labels,
const ValueWitnessTable *proposedWitnesses);

Expand Down
42 changes: 23 additions & 19 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -869,31 +869,35 @@ FUNCTION(GetObjCClassFromMetadata, swift_getObjCClassFromMetadata, C_CC,
ARGS(TypeMetadataPtrTy),
ATTRS(NoUnwind, ReadNone))

// Metadata *swift_getTupleTypeMetadata(TupleTypeFlags flags,
// Metadata * const *elts,
// const char *labels,
// value_witness_table_t *proposed);
FUNCTION(GetTupleMetadata, swift_getTupleTypeMetadata, C_CC,
RETURNS(TypeMetadataPtrTy),
ARGS(SizeTy, TypeMetadataPtrTy->getPointerTo(0),
// MetadataResponse swift_getTupleTypeMetadata(MetadataRequest request,
// TupleTypeFlags flags,
// Metadata * const *elts,
// const char *labels,
// value_witness_table_t *proposed);
FUNCTION(GetTupleMetadata, swift_getTupleTypeMetadata, SwiftCC,
RETURNS(TypeMetadataResponseTy),
ARGS(SizeTy, SizeTy, TypeMetadataPtrTy->getPointerTo(0),
Int8PtrTy, WitnessTablePtrTy),
ATTRS(NoUnwind, ReadOnly))

// Metadata *swift_getTupleTypeMetadata2(Metadata *elt0, Metadata *elt1,
// const char *labels,
// value_witness_table_t *proposed);
FUNCTION(GetTupleMetadata2, swift_getTupleTypeMetadata2, C_CC,
RETURNS(TypeMetadataPtrTy),
ARGS(TypeMetadataPtrTy, TypeMetadataPtrTy,
// MetadataResponse swift_getTupleTypeMetadata2(MetadataRequest request,
// Metadata *elt0, Metadata *elt1,
// const char *labels,
// value_witness_table_t *proposed);
FUNCTION(GetTupleMetadata2, swift_getTupleTypeMetadata2, SwiftCC,
RETURNS(TypeMetadataResponseTy),
ARGS(SizeTy, TypeMetadataPtrTy, TypeMetadataPtrTy,
Int8PtrTy, WitnessTablePtrTy),
ATTRS(NoUnwind, ReadOnly))

// Metadata *swift_getTupleTypeMetadata3(Metadata *elt0, Metadata *elt1,
// Metadata *elt2, const char *labels,
// value_witness_table_t *proposed);
FUNCTION(GetTupleMetadata3, swift_getTupleTypeMetadata3, C_CC,
RETURNS(TypeMetadataPtrTy),
ARGS(TypeMetadataPtrTy, TypeMetadataPtrTy, TypeMetadataPtrTy,
// MetadataResponse swift_getTupleTypeMetadata3(MetadataRequest request,
// Metadata *elt0, Metadata *elt1,
// Metadata *elt2,
// const char *labels,
// value_witness_table_t *proposed);
FUNCTION(GetTupleMetadata3, swift_getTupleTypeMetadata3, SwiftCC,
RETURNS(TypeMetadataResponseTy),
ARGS(SizeTy, TypeMetadataPtrTy, TypeMetadataPtrTy, TypeMetadataPtrTy,
Int8PtrTy, WitnessTablePtrTy),
ATTRS(NoUnwind, ReadOnly))

Expand Down
78 changes: 41 additions & 37 deletions lib/IRGen/MetadataRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,21 +688,22 @@ static llvm::Constant *emitEmptyTupleTypeMetadataRef(IRGenModule &IGM) {
/*Ty=*/nullptr, fullMetadata, indices);
}

/// Note that the element request will always be of a
/// canResponseStatusBeIgnored() kind.
using GetElementMetadataFn =
llvm::function_ref<llvm::Value *(CanType eltType,
DynamicMetadataRequest eltRequest)>;
llvm::function_ref<MetadataResponse(CanType eltType,
DynamicMetadataRequest eltRequest)>;

static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
CanTupleType type,
DynamicMetadataRequest request,
bool useLabels,
GetElementMetadataFn getElementMetadata) {

// FIXME: at least propagate dependency failure here.
// FIXME: allow abstract creation when the runtime supports that.
DynamicMetadataRequest eltRequest = MetadataState::Complete;
GetElementMetadataFn getMetadataRecursive) {
auto getElementMetadata = [&](CanType type) {
// Just request the elements to be abstract so that we can always build
// the metadata.
// TODO: if we have a collector, or if this is a blocking request, maybe
// we should build a stronger request?
return getMetadataRecursive(type, MetadataState::Abstract).getMetadata();
};

switch (type->getNumElements()) {
case 0:
Expand All @@ -712,42 +713,45 @@ static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
case 1:
// For metadata purposes, we consider a singleton tuple to be
// isomorphic to its element type. ???
return MetadataResponse::forComplete(
getElementMetadata(type.getElementType(0), eltRequest));
return getMetadataRecursive(type.getElementType(0), request);

case 2: {
auto elt0Metadata = getElementMetadata(type.getElementType(0), eltRequest);
auto elt1Metadata = getElementMetadata(type.getElementType(1), eltRequest);
auto elt0Metadata = getElementMetadata(type.getElementType(0));
auto elt1Metadata = getElementMetadata(type.getElementType(1));

llvm::Value *args[] = {
request.get(IGF),
elt0Metadata, elt1Metadata,
getTupleLabelsString(IGF.IGM, type, useLabels),
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
};

auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleMetadata2Fn(),
args);
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();

return MetadataResponse::forComplete(call);
return MetadataResponse::handle(IGF, request, call);
}

case 3: {
auto elt0Metadata = getElementMetadata(type.getElementType(0), eltRequest);
auto elt1Metadata = getElementMetadata(type.getElementType(1), eltRequest);
auto elt2Metadata = getElementMetadata(type.getElementType(2), eltRequest);
auto elt0Metadata = getElementMetadata(type.getElementType(0));
auto elt1Metadata = getElementMetadata(type.getElementType(1));
auto elt2Metadata = getElementMetadata(type.getElementType(2));

llvm::Value *args[] = {
request.get(IGF),
elt0Metadata, elt1Metadata, elt2Metadata,
getTupleLabelsString(IGF.IGM, type, useLabels),
llvm::ConstantPointerNull::get(IGF.IGM.WitnessTablePtrTy) // proposed
};

auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleMetadata3Fn(),
args);
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();

return MetadataResponse::forComplete(call);
return MetadataResponse::handle(IGF, request, call);
}
default:
// TODO: use a caching entrypoint (with all information
Expand All @@ -764,7 +768,7 @@ static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
IGF.IGM.getPointerSize() * elements.size());
for (auto i : indices(elements)) {
// Find the metadata pointer for this element.
llvm::Value *eltMetadata = getElementMetadata(elements[i], eltRequest);
llvm::Value *eltMetadata = getElementMetadata(elements[i]);

// GEP to the appropriate element and store.
Address eltPtr = IGF.Builder.CreateStructGEP(buffer, i,
Expand All @@ -778,6 +782,7 @@ static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,
TupleTypeFlags flags =
TupleTypeFlags().withNumElements(elements.size());
llvm::Value *args[] = {
request.get(IGF),
llvm::ConstantInt::get(IGF.IGM.SizeTy, flags.getIntValue()),
pointerToFirst,
getTupleLabelsString(IGF.IGM, type, useLabels),
Expand All @@ -786,12 +791,13 @@ static MetadataResponse emitTupleTypeMetadataRef(IRGenFunction &IGF,

auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleMetadataFn(),
args);
call->setCallingConv(IGF.IGM.SwiftCC);
call->setDoesNotThrow();

IGF.Builder.CreateLifetimeEnd(buffer,
IGF.IGM.getPointerSize() * elements.size());

return MetadataResponse::forComplete(call);
return MetadataResponse::handle(IGF, request, call);
}
}

Expand Down Expand Up @@ -882,8 +888,7 @@ namespace {
auto response = emitTupleTypeMetadataRef(IGF, type, request,
/*labels*/ true,
[&](CanType eltType, DynamicMetadataRequest eltRequest) {
assert(eltRequest.canResponseStatusBeIgnored());
return IGF.emitTypeMetadataRef(eltType, eltRequest).getMetadata();
return IGF.emitTypeMetadataRef(eltType, eltRequest);
});

return setLocal(type, response);
Expand Down Expand Up @@ -1651,24 +1656,23 @@ irgen::emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
/// construction of the metadata value just involves calling idempotent
/// metadata-construction functions. It is not used for the in-place
/// initialization of non-generic nominal type metadata.
static llvm::Value *
static MetadataResponse
emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
DynamicMetadataRequest request,
CanType type) {
assert(!type->hasArchetype() &&
"cannot emit metadata accessor for context-dependent type");

// We only take this path for non-generic nominal types.
auto typeDecl = type->getAnyNominal();
if (!typeDecl)
return emitDirectTypeMetadataRef(IGF, type, MetadataState::Complete)
.getMetadata();
return emitDirectTypeMetadataRef(IGF, type, request);

if (typeDecl->isGenericContext() &&
!(isa<ClassDecl>(typeDecl) &&
isa<ClangModuleUnit>(typeDecl->getModuleScopeContext()))) {
// This is a metadata accessor for a fully substituted generic type.
return emitDirectTypeMetadataRef(IGF, type, MetadataState::Complete)
.getMetadata();
return emitDirectTypeMetadataRef(IGF, type, request);
}

// We should never be emitting a metadata accessor for resilient nominal
Expand All @@ -1687,18 +1691,19 @@ emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
if (auto classDecl = dyn_cast<ClassDecl>(typeDecl)) {
// We emit a completely different pattern for foreign classes.
if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
return emitForeignTypeMetadataRef(IGF, type);
return MetadataResponse::forComplete(
emitForeignTypeMetadataRef(IGF, type));
}

// Classes that might not have Swift metadata use a different
// symbol name.
if (!hasKnownSwiftMetadata(IGF.IGM, classDecl)) {
return emitObjCMetadataRef(IGF, classDecl);
return MetadataResponse::forComplete(emitObjCMetadataRef(IGF, classDecl));
}

// Imported value types require foreign metadata uniquing.
} else if (isa<ClangModuleUnit>(typeDecl->getModuleScopeContext())) {
return emitForeignTypeMetadataRef(IGF, type);
return MetadataResponse::forComplete(emitForeignTypeMetadataRef(IGF, type));
}

// Okay, everything else is built from a Swift metadata object.
Expand All @@ -1707,7 +1712,7 @@ emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
// We should not be doing more serious work along this path.
assert(isTypeMetadataAccessTrivial(IGF.IGM, type));

return metadata;
return MetadataResponse::forComplete(metadata);
}

/// Get or create an accessor function to the given non-dependent type.
Expand Down Expand Up @@ -1762,10 +1767,7 @@ llvm::Function *irgen::getTypeMetadataAccessFunction(IRGenModule &IGM,
llvm::Constant *cacheVariable) {
// We should not be called with ForDefinition for nominal types
// that require in-place initialization.
// We should also not be called for types that require more interesting
// initialization that really requires the request/response machinery.
return MetadataResponse::forComplete(
emitTypeMetadataAccessFunctionBody(IGF, type));
return emitTypeMetadataAccessFunctionBody(IGF, request, type);
});
}

Expand Down Expand Up @@ -1959,8 +1961,10 @@ namespace {
auto response = emitTupleTypeMetadataRef(IGF, type, request,
/*labels*/ false,
[&](CanType eltType, DynamicMetadataRequest eltRequest) {
assert(eltRequest.canResponseStatusBeIgnored());
return visit(eltType, eltRequest);
// This use of 'forComplete' is technically questionable, but in
// this class we're always producing responses we can ignore, so
// it's okay.
return MetadataResponse::forComplete(visit(eltType, eltRequest));
});

return setLocal(type, response);
Expand Down
Loading