diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index 9bb4f425907..51b77c3ab8a 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -204,82 +204,150 @@ template<> class hash { } }; +template<> class hash { +public: + size_t operator()(const wasm::TypeInfo& info) const; +}; + +template class hash> { +public: + size_t operator()(const reference_wrapper& ref) const { + return hash{}(ref.get()); + } +}; + +template class equal_to> { +public: + bool operator()(const reference_wrapper& a, + const reference_wrapper& b) const { + return equal_to{}(a.get(), b.get()); + } +}; + } // namespace std namespace wasm { namespace { +HeapTypeInfo* getHeapTypeInfo(HeapType ht) { + assert(!ht.isBasic()); + return (HeapTypeInfo*)ht.getID(); +} + +HeapType asHeapType(std::unique_ptr& info) { + return HeapType(uintptr_t(info.get())); +} + +Type markTemp(Type type) { + if (!type.isBasic()) { + Type::getTypeInfo(type)->isTemp = true; + } + return type; +} + +bool isTemp(Type type) { + return !type.isBasic() && Type::getTypeInfo(type)->isTemp; +} + +bool isTemp(HeapType type) { + return !type.isBasic() && getHeapTypeInfo(type)->isTemp; +} + // Generic utility for traversing type graphs. The inserted roots must live as // long as the Walker because they are referenced by address. This base class // only has logic for traversing type graphs; figuring out when to stop // traversing the graph and doing useful work during the traversal is left to -// subclasses. +// subclasses, which should override `scanType` and/or `scanHeapType`. Edges +// from reference types to the referenced heap types are not walked, so +// subclasses should handle referenced heap types when their reference types are +// visited. template struct TypeGraphWalkerBase { - void walkRoot(Type* type); - void walkRoot(HeapType* ht); + void walkRoot(Type* type) { + assert(taskList.empty()); + taskList.push_back(Task::scan(type)); + doWalk(); + } + + void walkRoot(HeapType* ht) { + assert(taskList.empty()); + taskList.push_back(Task::scan(ht)); + doWalk(); + } + +protected: + Self& self() { return *static_cast(this); } - // Override these in subclasses to do useful work. - void preVisitType(Type* type) {} - void preVisitHeapType(HeapType* ht) {} - void postVisitType(Type* type) {} - void postVisitHeapType(HeapType* ht) {} + void scanType(Type* type) { + if (type->isTuple()) { + auto& types = const_cast(type->getTuple()); + for (auto it = types.rbegin(); it != types.rend(); ++it) { + taskList.push_back(Task::scan(&*it)); + } + } + } - // This base walker does not know when to stop scanning, so at least one of - // these needs to be overridden with a method that calls the base scanning - // method only if some end condition isn't met. - void scanType(Type* type); - void scanHeapType(HeapType* ht); + void scanHeapType(HeapType* ht) { + if (ht->isBasic()) { + return; + } + auto* info = getHeapTypeInfo(*ht); + switch (info->kind) { + case HeapTypeKind::Func: + taskList.push_back(Task::scan(&info->signature.results)); + taskList.push_back(Task::scan(&info->signature.params)); + break; + case HeapTypeKind::Cont: + taskList.push_back(Task::scan(&info->continuation.type)); + break; + case HeapTypeKind::Struct: { + auto& fields = info->struct_.fields; + for (auto field = fields.rbegin(); field != fields.rend(); ++field) { + taskList.push_back(Task::scan(&field->type)); + } + break; + } + case HeapTypeKind::Array: + taskList.push_back(Task::scan(&info->array.element.type)); + break; + case HeapTypeKind::Basic: + WASM_UNREACHABLE("unexpected kind"); + } + } private: struct Task { enum Kind { - PreType, - PreHeapType, ScanType, ScanHeapType, - PostType, - PostHeapType, } kind; union { Type* type; HeapType* heapType; }; - static Task preVisit(Type* type) { return Task(type, PreType); } - static Task preVisit(HeapType* ht) { return Task(ht, PreHeapType); } static Task scan(Type* type) { return Task(type, ScanType); } static Task scan(HeapType* ht) { return Task(ht, ScanHeapType); } - static Task postVisit(Type* type) { return Task(type, PostType); } - static Task postVisit(HeapType* ht) { return Task(ht, PostHeapType); } private: Task(Type* type, Kind kind) : kind(kind), type(type) {} Task(HeapType* ht, Kind kind) : kind(kind), heapType(ht) {} }; - void doWalk(); - std::vector taskList; - void push(Type* type); - void push(HeapType* type); - Self& self() { return *static_cast(this); } -}; - -// A type graph walker base class that still does no useful work, but at least -// knows to scan each HeapType only once. -template struct HeapTypeGraphWalker : TypeGraphWalkerBase { - // Override this. - void noteHeapType(HeapType ht) {} - - void scanHeapType(HeapType* ht) { - if (scanned.insert(*ht).second) { - static_cast(this)->noteHeapType(*ht); - TypeGraphWalkerBase::scanHeapType(ht); + void doWalk() { + while (!taskList.empty()) { + auto curr = taskList.back(); + taskList.pop_back(); + switch (curr.kind) { + case Task::ScanType: + self().scanType(curr.type); + break; + case Task::ScanHeapType: + self().scanHeapType(curr.heapType); + break; + } } } - -private: - std::unordered_set scanned; }; // A type graph walker base class that still does no useful work, but at least @@ -307,24 +375,27 @@ template struct TypeGraphWalker : TypeGraphWalkerBase { std::unordered_set scannedTypes; }; -// A type graph walker that only traverses the direct HeapType children of the -// root, looking through child Types. What to do with each child is left to -// subclasses. -template struct HeapTypeChildWalker : HeapTypeGraphWalker { - // Override this. - void noteChild(HeapType* child) {} - +// A type graph walker base class that still does no useful work, but at least +// knows to scan each HeapType and Type only once. +// A type graph walker that calls `noteChild` on each each direct HeapType child +// of the root. +template struct HeapTypeChildWalker : TypeGraphWalkerBase { void scanType(Type* type) { isTopLevel = false; - HeapTypeGraphWalker::scanType(type); + if (type->isRef()) { + this->self().noteChild(type->getHeapType()); + } else { + TypeGraphWalkerBase::scanType(type); + } } - void scanHeapType(HeapType* ht) { + + void scanHeapType(HeapType* type) { if (isTopLevel) { - HeapTypeGraphWalker::scanHeapType(ht); + isTopLevel = false; + TypeGraphWalkerBase::scanHeapType(type); } else { - static_cast(this)->noteChild(ht); + this->self().noteChild(*type); } - isTopLevel = false; } private: @@ -333,63 +404,9 @@ template struct HeapTypeChildWalker : HeapTypeGraphWalker { struct HeapTypeChildCollector : HeapTypeChildWalker { std::vector children; - void noteChild(HeapType* child) { children.push_back(*child); } -}; - -} // anonymous namespace -} // namespace wasm - -namespace std { - -template<> class hash { -public: - size_t operator()(const wasm::TypeInfo& info) const; + void noteChild(HeapType type) { children.push_back(type); } }; -template class hash> { -public: - size_t operator()(const reference_wrapper& ref) const { - return hash{}(ref.get()); - } -}; - -template class equal_to> { -public: - bool operator()(const reference_wrapper& a, - const reference_wrapper& b) const { - return equal_to{}(a.get(), b.get()); - } -}; - -} // namespace std - -namespace wasm { -namespace { - -HeapTypeInfo* getHeapTypeInfo(HeapType ht) { - assert(!ht.isBasic()); - return (HeapTypeInfo*)ht.getID(); -} - -HeapType asHeapType(std::unique_ptr& info) { - return HeapType(uintptr_t(info.get())); -} - -Type markTemp(Type type) { - if (!type.isBasic()) { - Type::getTypeInfo(type)->isTemp = true; - } - return type; -} - -bool isTemp(Type type) { - return !type.isBasic() && Type::getTypeInfo(type)->isTemp; -} - -bool isTemp(HeapType type) { - return !type.isBasic() && getHeapTypeInfo(type)->isTemp; -} - HeapType::BasicHeapType getBasicHeapSupertype(HeapType type) { if (type.isBasic()) { return HeapType::BasicHeapType(type.getID()); @@ -1387,13 +1404,13 @@ FeatureSet HeapType::getFeatures() const { : HeapTypeChildWalker { FeatureSet feats = FeatureSet::None; - void noteChild(HeapType* heapType) { - if (heapType->isShared()) { + void noteChild(HeapType heapType) { + if (heapType.isShared()) { feats |= FeatureSet::SharedEverything; } - if (heapType->isBasic()) { - switch (heapType->getBasic(Unshared)) { + if (heapType.isBasic()) { + switch (heapType.getBasic(Unshared)) { case HeapType::ext: case HeapType::func: feats |= FeatureSet::ReferenceTypes; @@ -1426,14 +1443,14 @@ FeatureSet HeapType::getFeatures() const { } } - if (heapType->getRecGroup().size() > 1 || - heapType->getDeclaredSuperType() || heapType->isOpen()) { + if (heapType.getRecGroup().size() > 1 || + heapType.getDeclaredSuperType() || heapType.isOpen()) { feats |= FeatureSet::ReferenceTypes | FeatureSet::GC; } - if (heapType->isStruct() || heapType->isArray()) { + if (heapType.isStruct() || heapType.isArray()) { feats |= FeatureSet::ReferenceTypes | FeatureSet::GC; - } else if (heapType->isSignature()) { + } 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, @@ -1442,17 +1459,17 @@ FeatureSet HeapType::getFeatures() const { // 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(); + auto sig = heapType.getSignature(); if (sig.results.isTuple()) { feats |= FeatureSet::Multivalue; } - } else if (heapType->isContinuation()) { + } 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()) { + for (auto child : heapType.getTypeChildren()) { if (!child.isRef()) { feats |= child.getFeatures(); } @@ -1466,7 +1483,7 @@ FeatureSet HeapType::getFeatures() const { // send |this| there from a |const| method. auto* unconst = const_cast(this); collector.walkRoot(unconst); - collector.noteChild(unconst); + collector.noteChild(*unconst); return collector.feats; } @@ -2248,98 +2265,6 @@ bool RecGroupEquator::eq(const Array& a, const Array& b) const { return eq(a.element, b.element); } -template void TypeGraphWalkerBase::walkRoot(Type* type) { - assert(taskList.empty()); - taskList.push_back(Task::scan(type)); - doWalk(); -} - -template void TypeGraphWalkerBase::walkRoot(HeapType* ht) { - assert(taskList.empty()); - taskList.push_back(Task::scan(ht)); - doWalk(); -} - -template void TypeGraphWalkerBase::doWalk() { - while (!taskList.empty()) { - auto curr = taskList.back(); - taskList.pop_back(); - switch (curr.kind) { - case Task::PreType: - self().preVisitType(curr.type); - break; - case Task::PreHeapType: - self().preVisitHeapType(curr.heapType); - break; - case Task::ScanType: - taskList.push_back(Task::postVisit(curr.type)); - self().scanType(curr.type); - taskList.push_back(Task::preVisit(curr.type)); - break; - case Task::ScanHeapType: - taskList.push_back(Task::postVisit(curr.heapType)); - self().scanHeapType(curr.heapType); - taskList.push_back(Task::preVisit(curr.heapType)); - break; - case Task::PostType: - self().postVisitType(curr.type); - break; - case Task::PostHeapType: - self().postVisitHeapType(curr.heapType); - break; - } - } -} - -template void TypeGraphWalkerBase::scanType(Type* type) { - if (type->isBasic()) { - return; - } - auto* info = Type::getTypeInfo(*type); - switch (info->kind) { - case TypeInfo::TupleKind: { - auto& types = info->tuple; - for (auto it = types.rbegin(); it != types.rend(); ++it) { - taskList.push_back(Task::scan(&*it)); - } - break; - } - case TypeInfo::RefKind: { - taskList.push_back(Task::scan(&info->ref.heapType)); - break; - } - } -} - -template -void TypeGraphWalkerBase::scanHeapType(HeapType* ht) { - if (ht->isBasic()) { - return; - } - auto* info = getHeapTypeInfo(*ht); - switch (info->kind) { - case HeapTypeKind::Func: - taskList.push_back(Task::scan(&info->signature.results)); - taskList.push_back(Task::scan(&info->signature.params)); - break; - case HeapTypeKind::Cont: - taskList.push_back(Task::scan(&info->continuation.type)); - break; - case HeapTypeKind::Struct: { - auto& fields = info->struct_.fields; - for (auto field = fields.rbegin(); field != fields.rend(); ++field) { - taskList.push_back(Task::scan(&field->type)); - } - break; - } - case HeapTypeKind::Array: - taskList.push_back(Task::scan(&info->array.element.type)); - break; - case HeapTypeKind::Basic: - WASM_UNREACHABLE("unexpected kind"); - } -} - } // anonymous namespace struct TypeBuilder::Impl { @@ -2683,10 +2608,11 @@ buildRecGroup(std::unique_ptr&& groupInfo, // replace the old ones. TODO simplify this. struct Locations : TypeGraphWalker { std::unordered_map> types; - void preVisitType(Type* type) { + void scanType(Type* type) { if (isTemp(*type)) { types[*type].insert(type); } + TypeGraphWalker::scanType(type); } };