From 53712b6d6e93449a6faf18a55e0fb29022f158df Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 27 Jun 2024 10:25:05 -0700 Subject: [PATCH] [NFC] Add HeapType::getFeatures() (#6707) --- src/passes/StringLowering.cpp | 2 +- src/wasm-type.h | 3 + src/wasm/wasm-type.cpp | 176 +++++++++++++++++----------------- src/wasm/wasm-validator.cpp | 3 +- 4 files changed, 95 insertions(+), 89 deletions(-) diff --git a/src/passes/StringLowering.cpp b/src/passes/StringLowering.cpp index ca0ba773c76..f735b9ab222 100644 --- a/src/passes/StringLowering.cpp +++ b/src/passes/StringLowering.cpp @@ -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; } diff --git a/src/wasm-type.h b/src/wasm-type.h index 51d945b5373..69b50c07f26 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -451,6 +451,9 @@ class HeapType { // Return the LUB of two HeapTypes, which may or may not exist. static std::optional 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. diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 92c68d8bef0..6328180f81d 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -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 { - 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()) { @@ -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 { + 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(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 diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 281f3ec4599..a491fb951fd 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -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");