Skip to content
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 src/passes/StringLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ struct StringLowering : public StringGathering {
// explained we cannot do that - or before it, which is what we do here).
for (auto& func : module->functions) {
if (func->type.getRecGroup().size() != 1 ||
!Type(func->type, Nullable).getFeatures().hasStrings()) {
!func->type.getFeatures().hasStrings()) {
continue;
}

Expand Down
3 changes: 3 additions & 0 deletions src/wasm-type.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,9 @@ class HeapType {
// Return the LUB of two HeapTypes, which may or may not exist.
static std::optional<HeapType> getLeastUpperBound(HeapType a, HeapType b);

// Returns the feature set required to use this type.
FeatureSet getFeatures() const;

// Helper allowing the value of `print(...)` to be sent to an ostream. Stores
// a `TypeID` because `Type` is incomplete at this point and using a reference
// makes it less convenient to use.
Expand Down
176 changes: 90 additions & 86 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,92 +900,7 @@ Type Type::reinterpret() const {
FeatureSet Type::getFeatures() const {
auto getSingleFeatures = [](Type t) -> FeatureSet {
if (t.isRef()) {
// A reference type implies we need that feature. Some also require
// more, such as GC or exceptions, and may require us to look into child
// types.
struct ReferenceFeatureCollector
: HeapTypeChildWalker<ReferenceFeatureCollector> {
FeatureSet feats = FeatureSet::None;

void noteChild(HeapType* heapType) {
if (heapType->isBasic()) {
switch (heapType->getBasic(Unshared)) {
case HeapType::ext:
case HeapType::func:
feats |= FeatureSet::ReferenceTypes;
return;
case HeapType::any:
case HeapType::eq:
case HeapType::i31:
case HeapType::struct_:
case HeapType::array:
feats |= FeatureSet::ReferenceTypes | FeatureSet::GC;
return;
case HeapType::string:
feats |= FeatureSet::ReferenceTypes | FeatureSet::Strings;
return;
case HeapType::none:
case HeapType::noext:
case HeapType::nofunc:
// Technically introduced in GC, but used internally as part of
// ref.null with just reference types.
feats |= FeatureSet::ReferenceTypes;
return;
case HeapType::exn:
case HeapType::noexn:
feats |=
FeatureSet::ExceptionHandling | FeatureSet::ReferenceTypes;
return;
case HeapType::cont:
case HeapType::nocont:
feats |= FeatureSet::TypedContinuations;
return;
}
}

if (heapType->getRecGroup().size() > 1 ||
heapType->getDeclaredSuperType() || heapType->isOpen()) {
feats |= FeatureSet::ReferenceTypes | FeatureSet::GC;
}

if (heapType->isShared()) {
feats |= FeatureSet::SharedEverything;
}

if (heapType->isStruct() || heapType->isArray()) {
feats |= FeatureSet::ReferenceTypes | FeatureSet::GC;
} else if (heapType->isSignature()) {
// This is a function reference, which requires reference types and
// possibly also multivalue (if it has multiple returns). Note that
// technically typed function references also require GC, however,
// we use these types internally regardless of the presence of GC
// (in particular, since during load of the wasm we don't know the
// features yet, so we apply the more refined types), so we don't
// add that in any case here.
feats |= FeatureSet::ReferenceTypes;
auto sig = heapType->getSignature();
if (sig.results.isTuple()) {
feats |= FeatureSet::Multivalue;
}
} else if (heapType->isContinuation()) {
feats |= FeatureSet::TypedContinuations;
}

// In addition, scan their non-ref children, to add dependencies on
// things like SIMD.
for (auto child : heapType->getTypeChildren()) {
if (!child.isRef()) {
feats |= child.getFeatures();
}
}
}
};

ReferenceFeatureCollector collector;
auto heapType = t.getHeapType();
collector.walkRoot(&heapType);
collector.noteChild(&heapType);
return collector.feats;
return t.getHeapType().getFeatures();
}

switch (t.getBasic()) {
Expand Down Expand Up @@ -1604,6 +1519,95 @@ size_t HeapType::getRecGroupIndex() const {
return getHeapTypeInfo(*this)->recGroupIndex;
}

FeatureSet HeapType::getFeatures() const {
// Collects features from a type + children.
struct ReferenceFeatureCollector
: HeapTypeChildWalker<ReferenceFeatureCollector> {
FeatureSet feats = FeatureSet::None;

void noteChild(HeapType* heapType) {
if (heapType->isBasic()) {
switch (heapType->getBasic(Unshared)) {
case HeapType::ext:
case HeapType::func:
feats |= FeatureSet::ReferenceTypes;
return;
case HeapType::any:
case HeapType::eq:
case HeapType::i31:
case HeapType::struct_:
case HeapType::array:
feats |= FeatureSet::ReferenceTypes | FeatureSet::GC;
return;
case HeapType::string:
feats |= FeatureSet::ReferenceTypes | FeatureSet::Strings;
return;
case HeapType::none:
case HeapType::noext:
case HeapType::nofunc:
// Technically introduced in GC, but used internally as part of
// ref.null with just reference types.
feats |= FeatureSet::ReferenceTypes;
return;
case HeapType::exn:
case HeapType::noexn:
feats |= FeatureSet::ExceptionHandling | FeatureSet::ReferenceTypes;
return;
case HeapType::cont:
case HeapType::nocont:
feats |= FeatureSet::TypedContinuations;
return;
}
}

if (heapType->getRecGroup().size() > 1 ||
heapType->getDeclaredSuperType() || heapType->isOpen()) {
feats |= FeatureSet::ReferenceTypes | FeatureSet::GC;
}

if (heapType->isShared()) {
feats |= FeatureSet::SharedEverything;
}

if (heapType->isStruct() || heapType->isArray()) {
feats |= FeatureSet::ReferenceTypes | FeatureSet::GC;
} else if (heapType->isSignature()) {
// This is a function reference, which requires reference types and
// possibly also multivalue (if it has multiple returns). Note that
// technically typed function references also require GC, however,
// we use these types internally regardless of the presence of GC
// (in particular, since during load of the wasm we don't know the
// features yet, so we apply the more refined types), so we don't
// add that in any case here.
feats |= FeatureSet::ReferenceTypes;
auto sig = heapType->getSignature();
if (sig.results.isTuple()) {
feats |= FeatureSet::Multivalue;
}
} else if (heapType->isContinuation()) {
feats |= FeatureSet::TypedContinuations;
}

// In addition, scan their non-ref children, to add dependencies on
// things like SIMD.
for (auto child : heapType->getTypeChildren()) {
if (!child.isRef()) {
feats |= child.getFeatures();
}
}
}
};

ReferenceFeatureCollector collector;
// For internal reasons, the walkRoot/noteChild APIs all require non-const
// pointers. We only use them to scan the type, so it is safe for us to
// send |this| there from a |const| method.
auto* unconst = const_cast<HeapType*>(this);
collector.walkRoot(unconst);
collector.noteChild(unconst);
return collector.feats;
}

HeapType RecGroup::Iterator::operator*() const {
if (parent->id & 1) {
// This is a trivial recursion group. Mask off the low bit to recover the
Expand Down
3 changes: 1 addition & 2 deletions src/wasm/wasm-validator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3389,8 +3389,7 @@ void FunctionValidator::visitFunction(Function* curr) {
// Check for things like having a rec group with GC enabled. The type we're
// checking is a reference type even if this an MVP function type, so ignore
// the reference types feature here.
features |=
(Type(curr->type, Nullable).getFeatures() & ~FeatureSet::ReferenceTypes);
features |= (curr->type.getFeatures() & ~FeatureSet::ReferenceTypes);
for (const auto& param : curr->getParams()) {
features |= param.getFeatures();
shouldBeTrue(param.isConcrete(), curr, "params must be concretely typed");
Expand Down