Skip to content

Commit

Permalink
[NFC] Add HeapType::getFeatures() (WebAssembly#6707)
Browse files Browse the repository at this point in the history
  • Loading branch information
kripken authored Jun 27, 2024
1 parent 636d1b2 commit 53712b6
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 89 deletions.
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

0 comments on commit 53712b6

Please sign in to comment.