Skip to content

Support incomplete type metadata #15511

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 8 commits into from
Mar 26, 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
96 changes: 56 additions & 40 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ class TargetValueWitnessFlags {
private:
int_type Data;

constexpr TargetValueWitnessFlags(int_type data) : Data(data) {}
public:
explicit constexpr TargetValueWitnessFlags(int_type data) : Data(data) {}
constexpr TargetValueWitnessFlags() : Data(0) {}

/// The required alignment of the first byte of an object of this
Expand Down Expand Up @@ -1383,36 +1383,49 @@ class GenericMetadataPatternFlags : public FlagSet<uint32_t> {
value_setMetadataKind)
};

/// The public state of a metadata.
enum class MetadataState : size_t {
/// A request for fully-completed metadata. The metadata must be
/// prepared for all supported type operations. This is a superset
/// of the requirements of LayoutComplete.
///
/// For example, a class must be ready for subclassing and instantiation:
/// it must have a completed instance layout and (under ObjCInterop)
/// must have been realized by the Objective-C runtime.
Complete,

/// A request for metadata that can be used for type layout; that is,
/// the type's value witness table must be completely initialized.
LayoutComplete,

/// A request for a metadata pointer that fully identifies the type.
/// Basic type structure, such as the type context descriptor and the
/// list of generic arguments, should have been installed, but there is
/// no requirement for a valid value witness table.
Abstract,
};

/// Something that can be static_asserted in all the places where we depend
/// on metadata state ordering.
constexpr const bool MetadataStateIsReverseOrdered = true;

/// Return true if the first metadata state is at least as advanced as the
/// second.
inline bool isAtLeast(MetadataState lhs, MetadataState rhs) {
static_assert(MetadataStateIsReverseOrdered,
"relying on the ordering of MetadataState here");
return size_t(lhs) <= size_t(rhs);
}

/// Kinds of requests for metadata.
template <class IntType>
class TargetMetadataRequest : public FlagSet<IntType> {
class MetadataRequest : public FlagSet<size_t> {
using IntType = size_t;
using super = FlagSet<IntType>;
public:
enum BasicKind : IntType {
/// A request for fully-completed metadata. The metadata must be
/// prepared for all supported type operations. This is a superset
/// of the requirements of LayoutComplete.
///
/// For example, a class must be ready for subclassing and instantiation:
/// it must have a completed instance layout and (under ObjCInterop)
/// must have been realized by the Objective-C runtime.
Complete,

/// A request for metadata that can be used for type layout; that is,
/// the type's value witness table must be completely initialized.
LayoutComplete,

/// A request for a metadata pointer that fully identifies the type.
/// Basic type structure, such as the type context descriptor and the
/// list of generic arguments, should have been installed, but there is
/// no requirement for a valid value witness table.
Abstract,
};

private:
public:
enum : IntType {
BasicKind_bit = 0,
BasicKind_width = 8,
State_bit = 0,
State_width = 8,

/// A blocking request will not return until the runtime is able to produce
/// metadata with the given kind. A non-blocking request will return
Expand All @@ -1423,28 +1436,31 @@ class TargetMetadataRequest : public FlagSet<IntType> {
NonBlocking_bit = 8,
};

public:
TargetMetadataRequest(BasicKind kind, bool isNonBlocking = false) {
setBasicKind(kind);
MetadataRequest(MetadataState state, bool isNonBlocking = false) {
setState(state);
setIsNonBlocking(isNonBlocking);
}
explicit TargetMetadataRequest(IntType bits) : super(bits) {}
constexpr TargetMetadataRequest() {}
explicit MetadataRequest(IntType bits) : super(bits) {}
constexpr MetadataRequest() {}

FLAGSET_DEFINE_EQUALITY(TargetMetadataRequest)
FLAGSET_DEFINE_EQUALITY(MetadataRequest)

FLAGSET_DEFINE_FIELD_ACCESSORS(BasicKind_bit,
BasicKind_width,
BasicKind,
getBasicKind,
setBasicKind)
FLAGSET_DEFINE_FIELD_ACCESSORS(State_bit,
State_width,
MetadataState,
getState,
setState)

FLAGSET_DEFINE_FLAG_ACCESSORS(NonBlocking_bit,
isNonBlocking,
setIsNonBlocking)
bool isBlocking() const { return !isNonBlocking(); }

/// Is this request satisfied by a metadata that's in the given state?
bool isSatisfiedBy(MetadataState state) const {
return isAtLeast(state, getState());
}
};
using MetadataRequest =
TargetMetadataRequest<size_t>;

} // end namespace swift

Expand Down
3 changes: 1 addition & 2 deletions include/swift/AST/CanTypeVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ namespace swift {
template<typename ImplClass, typename RetTy = void, typename... Args>
class CanTypeVisitor {
public:

template <class... As> RetTy visit(CanType T, Args... args) {
RetTy visit(CanType T, Args... args) {
switch (T->getKind()) {
#define UNCHECKED_TYPE(CLASS, PARENT) \
case TypeKind::CLASS:
Expand Down
17 changes: 16 additions & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -682,10 +682,25 @@ class alignas(1 << TypeAlignInBits) TypeBase {
///
/// A type is SIL-illegal if it is:
/// - an l-value type,
/// - an AST function type (i.e. subclasses of AnyFunctionType), or
/// - a metatype without a representation,
/// - an AST function type (i.e. subclasses of AnyFunctionType),
/// - an optional whose object type is SIL-illegal, or
/// - a tuple type with a SIL-illegal element type.
bool isLegalSILType();

/// \brief Determine whether this type is a legal formal type.
///
/// A type is illegal as a formal type if it is:
/// - an l-value type,
/// - a reference storage type,
/// - a metatype with a representation,
/// - a lowered function type (i.e. SILFunctionType),
/// - an optional whose object type is not a formal type, or
/// - a tuple type with an element that is not a formal type.
///
/// These are the types of the Swift type system.
bool isLegalFormalType();

/// \brief Check if this type is equal to the empty tuple type.
bool isVoid();

Expand Down
48 changes: 30 additions & 18 deletions include/swift/Basic/PrefixMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,16 @@ class PrefixMap {
private:
template <typename T>
union UninitializedStorage {
T Storage;
UninitializedStorage() = default;
UninitializedStorage(const UninitializedStorage &other) = default;
UninitializedStorage &operator=(const UninitializedStorage &other)
= default;
~UninitializedStorage() = default;
LLVM_ALIGNAS(alignof(T))
char Storage[sizeof(T)];

template <typename... A>
void initializeFrom(A && ...value) {
void emplace(A && ...value) {
::new((void*) &Storage) T(std::forward<A>(value)...);
}

T &get() { return *reinterpret_cast<T*>(Storage); }
const T &get() const { return *reinterpret_cast<T*>(Storage); }
};

// We expect to see a lot of entries for keys like:
Expand Down Expand Up @@ -119,17 +118,35 @@ class PrefixMap {
KeyType getLocalKey() const { return { Key, KeyLength }; }
};
struct Node : NodeBase {
private:
UninitializedStorage<ValueType> Value;

public:
Node() { /* leave Value uninitialized */ }

// We split NodeBase out so that we can just delegate to something that
// copies all the other fields.
Node(const Node &other) : NodeBase(other) {
if (this->HasValue) {
Value.initializeFrom(other.Value.Storage);
Value.emplace(other.Value.get());
}
}

ValueType &get() {
assert(this->HasValue);
return Value.get();
}
const ValueType &get() const {
assert(this->HasValue);
return Value.get();
}

template <typename... A>
void emplace(A && ...value) {
assert(!this->HasValue);
Value.emplace(std::forward<A>(value)...);
this->HasValue = true;
}
};

/// Splits a node in two. The second part must always be non-empty.
Expand Down Expand Up @@ -421,8 +438,7 @@ class PrefixMap {
/// Return the value of the entry. The returned reference is valid
/// as long as the entry remains in the map.
const ValueType &getValue() const {
assert(Path.back().getPointer()->HasValue);
return Path.back().getPointer()->Value.Storage;
return Path.back().getPointer()->get();
}

/// Read the value's key into the given buffer.
Expand Down Expand Up @@ -553,8 +569,7 @@ class PrefixMap {

explicit operator bool() const { return Ptr != nullptr; }
ValueType &operator*() const {
assert(Ptr->HasValue);
return Ptr->Value.Storage;
return Ptr->get();
}
};

Expand Down Expand Up @@ -590,8 +605,7 @@ class PrefixMap {
if (node->HasValue) {
return { Handle(node), false };
} else {
node->Value.initializeFrom(create());
node->HasValue = true;
node->emplace(create());
return { Handle(node), true };
}
}
Expand All @@ -614,9 +628,7 @@ class PrefixMap {
Handle insertNewLazy(KeyType key, const Fn &create) {
auto node = getOrCreatePrefixNode(key, nullptr);
assert(node);
assert(!node->HasValue);
node->Value.initializeFrom(create());
node->HasValue = true;
node->emplace(create());
return Handle(node);
}

Expand All @@ -637,7 +649,7 @@ class PrefixMap {
Node *node = reinterpret_cast<Node*>(_node);
PrefixMapKeyPrinter<KeyElementType>::print(out, node->getLocalKey());
if (node->HasValue) {
out << " (" << node->Value.Storage << ')';
out << " (" << node->get() << ')';
}
});
}
Expand Down
52 changes: 47 additions & 5 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,9 @@ struct MetadataResponse {
///
/// For metadata initialization functions, this is the state that the
/// given metadata needs to be in before initialization can continue.
MetadataRequest::BasicKind State;
MetadataState State;
};
using MetadataDependency = MetadataResponse;

template <typename Runtime> struct TargetProtocolConformanceDescriptor;

Expand Down Expand Up @@ -398,6 +399,14 @@ struct ValueWitnessTable {
const TypeLayout *getTypeLayout() const {
return reinterpret_cast<const TypeLayout *>(&size);
}

/// Check whether this metadata is complete.
bool checkIsComplete() const;

/// "Publish" the layout of this type to other threads. All other stores
/// to the value witness table (including its extended header) should have
/// happened before this is called.
void publishLayout(const TypeLayout &layout);
};

/// A value-witness table with extra inhabitants entry points.
Expand Down Expand Up @@ -474,6 +483,15 @@ struct TypeLayout {

void _static_assert_layout();
public:
TypeLayout() = default;
constexpr TypeLayout(value_witness_types::size size,
value_witness_types::flags flags,
value_witness_types::stride stride,
value_witness_types::extraInhabitantFlags eiFlags =
value_witness_types::extraInhabitantFlags())
: size(size), flags(flags), stride(stride),
extraInhabitantFlags(eiFlags) {}

value_witness_types::extraInhabitantFlags getExtraInhabitantFlags() const {
assert(flags.hasExtraInhabitants());
return extraInhabitantFlags;
Expand Down Expand Up @@ -501,6 +519,25 @@ inline void TypeLayout::_static_assert_layout() {
#undef CHECK_TYPE_LAYOUT_OFFSET
}

inline void ValueWitnessTable::publishLayout(const TypeLayout &layout) {
size = layout.size;
stride = layout.stride;

// Currently there is nothing in the runtime or ABI which tries to
// asynchronously check completion, so we can just do a normal store here.
//
// If we decide to start allowing that (to speed up checkMetadataState,
// maybe), we'll have to:
// - turn this into an store-release,
// - turn the load in checkIsComplete() into a load-acquire, and
// - do something about getMutableVWTableForInit.
flags = layout.flags;
}

inline bool ValueWitnessTable::checkIsComplete() const {
return !flags.isIncomplete();
}

inline const ExtraInhabitantsValueWitnessTable *
ValueWitnessTable::_asXIVWT() const {
assert(ExtraInhabitantsValueWitnessTable::classof(this));
Expand Down Expand Up @@ -3060,9 +3097,9 @@ struct MetadataCompletionContext {
/// some other type
using MetadataCompleter =
SWIFT_CC(swift)
MetadataResponse(const Metadata *type,
MetadataCompletionContext *context,
const TargetGenericMetadataPattern<InProcess> *pattern);
MetadataDependency(const Metadata *type,
MetadataCompletionContext *context,
const TargetGenericMetadataPattern<InProcess> *pattern);

/// An instantiation pattern for type metadata.
template <typename Runtime>
Expand Down Expand Up @@ -3277,7 +3314,7 @@ struct TargetTypeGenericContextDescriptorHeader {
};
using TypeGenericContextDescriptorHeader =
TargetTypeGenericContextDescriptorHeader<InProcess>;

/// Wrapper class for the pointer to a metadata access function that provides
/// operator() overloads to call it with the right calling convention.
class MetadataAccessFunction {
Expand Down Expand Up @@ -3944,6 +3981,11 @@ swift_allocateGenericValueMetadata(const ValueTypeDescriptor *description,
const GenericValueMetadataPattern *pattern,
size_t extraDataSize);

/// \brief Check that the given metadata has the right state.
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
MetadataResponse swift_checkMetadataState(MetadataRequest request,
const Metadata *type);

/// Instantiate a resilient or generic protocol witness table.
///
/// \param genericTable - The witness table template for the
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,13 @@ FUNCTION(AllocateGenericValueMetadata, swift_allocateGenericValueMetadata,
ARGS(TypeContextDescriptorPtrTy, Int8PtrPtrTy, Int8PtrPtrTy, SizeTy),
ATTRS(NoUnwind))

// MetadataResponse swift_checkMetadataState(MetadataRequest request,
// cosnt Metadata *type);
FUNCTION(CheckMetadataState, swift_checkMetadataState, SwiftCC,
RETURNS(TypeMetadataResponseTy),
ARGS(SizeTy, TypeMetadataPtrTy),
ATTRS(NoUnwind, ReadOnly))

// const ProtocolWitnessTable *
// swift_getGenericWitnessTable(GenericProtocolWitnessTable *genericTable,
// const Metadata *type,
Expand Down
Loading