diff options
author | Alon Zakai <azakai@google.com> | 2023-11-15 16:04:13 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-15 16:04:13 -0800 |
commit | 1fcb57e5c424cfb42f9c0e61e9b7b5485cb03896 (patch) | |
tree | 8859083fd6e222937adaebd54563b1b96e95d059 | |
parent | bf7635728e80ddda845e0b893b775e75a154e48e (diff) | |
download | binaryen-1fcb57e5c424cfb42f9c0e61e9b7b5485cb03896.tar.gz binaryen-1fcb57e5c424cfb42f9c0e61e9b7b5485cb03896.tar.bz2 binaryen-1fcb57e5c424cfb42f9c0e61e9b7b5485cb03896.zip |
[NFC] Refactor out subtyping discovery code (#6106)
This implements an idea I mentioned in the past, to extract the subtyping discovery
code out of Unsubtyping so it could be reused elsewhere. Example possible uses:
the validator could use to remove a lot of code, and also a future PR of mine will
need it. Separately from those, I think this is a nice refactoring as it makes Unsubtyping
much smaller.
This just moves the code out and adds some C++ template elbow grease as needed.
-rw-r--r-- | src/ir/subtype-exprs.h | 338 | ||||
-rw-r--r-- | src/passes/Unsubtyping.cpp | 279 |
2 files changed, 358 insertions, 259 deletions
diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h new file mode 100644 index 000000000..dc9ea1432 --- /dev/null +++ b/src/ir/subtype-exprs.h @@ -0,0 +1,338 @@ +/* + * Copyright 2023 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef wasm_ir_subtype_exprs_h +#define wasm_ir_subtype_exprs_h + +#include "ir/branch-utils.h" +#include "wasm-traversal.h" +#include "wasm.h" + +namespace wasm { + +// +// Analyze subtyping relationships between expressions. This must CRTP with a +// class that implements: +// +// * noteSubType(A, B) indicating A must be a subtype of B +// * noteCast(A, B) indicating A is cast to B +// +// There must be multiple versions of each of those, supporting A and B being +// either a Type, which indicates a fixed type requirement, or an Expression*, +// indicating a flexible requirement that depends on the type of that +// expression. Specifically: +// +// * noteSubType(Type, Type) - A constraint not involving expressions at all, +// for example, an element segment's type must be +// a subtype of the corresponding table's. +// * noteSubType(HeapType, HeapType) - Ditto, with heap types, for example in a +// CallIndirect. +// * noteSubType(Type, Expression) - A fixed type must be a subtype of an +// expression's type, for example, in BrOn +// (the declared sent type must be a subtype +// of the block we branch to). +// * noteSubType(Expression, Type) - An expression's type must be a subtype of +// a fixed type, for example, a Call operand +// must be a subtype of the signature's +// param. +// * noteSubType(Expression, Expression) - An expression's type must be a +// subtype of anothers, for example, +// a block and its last child. +// +// * noteCast(HeapType, HeapType) - A fixed type is cast to another, for +// example, in a CallIndirect. +// * noteCast(Expression, Type) - An expression's type is cast to a fixed type, +// for example, in RefTest. +// * noteCast(Expression, Expression) - An expression's type is cast to +// another, for example, in RefCast. +// +// Note that noteCast(Type, Type) and noteCast(Type, Expression) never occur and +// do not need to be implemented. +// +// The class must also inherit from ControlFlowWalker (for findBreakTarget). +// + +template<typename SubType> +struct SubtypingDiscoverer : public OverriddenVisitor<SubType> { + SubType* self() { return static_cast<SubType*>(this); } + + void visitFunction(Function* func) { + if (func->body) { + self()->noteSubtype(func->body, func->getResults()); + } + } + void visitGlobal(Global* global) { + if (global->init) { + self()->noteSubtype(global->init, global->type); + } + } + void visitElementSegment(ElementSegment* seg) { + if (seg->offset) { + self()->noteSubtype(seg->type, + self()->getModule()->getTable(seg->table)->type); + } + for (auto init : seg->data) { + self()->noteSubtype(init->type, seg->type); + } + } + void visitNop(Nop* curr) {} + void visitBlock(Block* curr) { + if (!curr->list.empty()) { + self()->noteSubtype(curr->list.back(), curr); + } + } + void visitIf(If* curr) { + if (curr->ifFalse) { + self()->noteSubtype(curr->ifTrue, curr); + self()->noteSubtype(curr->ifFalse, curr); + } + } + void visitLoop(Loop* curr) { self()->noteSubtype(curr->body, curr); } + void visitBreak(Break* curr) { + if (curr->value) { + self()->noteSubtype(curr->value, self()->findBreakTarget(curr->name)); + } + } + void visitSwitch(Switch* curr) { + if (curr->value) { + for (auto name : BranchUtils::getUniqueTargets(curr)) { + self()->noteSubtype(curr->value, self()->findBreakTarget(name)); + } + } + } + template<typename T> void handleCall(T* curr, Signature sig) { + assert(curr->operands.size() == sig.params.size()); + for (size_t i = 0, size = sig.params.size(); i < size; ++i) { + self()->noteSubtype(curr->operands[i], sig.params[i]); + } + if (curr->isReturn) { + self()->noteSubtype(sig.results, self()->getFunction()->getResults()); + } + } + void visitCall(Call* curr) { + handleCall(curr, self()->getModule()->getFunction(curr->target)->getSig()); + } + void visitCallIndirect(CallIndirect* curr) { + handleCall(curr, curr->heapType.getSignature()); + auto* table = self()->getModule()->getTable(curr->table); + auto tableType = table->type.getHeapType(); + if (HeapType::isSubType(tableType, curr->heapType)) { + // Unlike other casts, where cast targets are always subtypes of cast + // sources, call_indirect target types may be supertypes of their source + // table types. In this case, the cast will always succeed, but only if we + // keep the types related. + self()->noteSubtype(tableType, curr->heapType); + } else if (HeapType::isSubType(curr->heapType, tableType)) { + self()->noteCast(tableType, curr->heapType); + } else { + // The types are unrelated and the cast will fail. We can keep the types + // unrelated. + } + } + void visitLocalGet(LocalGet* curr) {} + void visitLocalSet(LocalSet* curr) { + self()->noteSubtype(curr->value, + self()->getFunction()->getLocalType(curr->index)); + } + void visitGlobalGet(GlobalGet* curr) {} + void visitGlobalSet(GlobalSet* curr) { + self()->noteSubtype(curr->value, + self()->getModule()->getGlobal(curr->name)->type); + } + void visitLoad(Load* curr) {} + void visitStore(Store* curr) {} + void visitAtomicRMW(AtomicRMW* curr) {} + void visitAtomicCmpxchg(AtomicCmpxchg* curr) {} + void visitAtomicWait(AtomicWait* curr) {} + void visitAtomicNotify(AtomicNotify* curr) {} + void visitAtomicFence(AtomicFence* curr) {} + void visitSIMDExtract(SIMDExtract* curr) {} + void visitSIMDReplace(SIMDReplace* curr) {} + void visitSIMDShuffle(SIMDShuffle* curr) {} + void visitSIMDTernary(SIMDTernary* curr) {} + void visitSIMDShift(SIMDShift* curr) {} + void visitSIMDLoad(SIMDLoad* curr) {} + void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {} + void visitMemoryInit(MemoryInit* curr) {} + void visitDataDrop(DataDrop* curr) {} + void visitMemoryCopy(MemoryCopy* curr) {} + void visitMemoryFill(MemoryFill* curr) {} + void visitConst(Const* curr) {} + void visitUnary(Unary* curr) {} + void visitBinary(Binary* curr) {} + void visitSelect(Select* curr) { + self()->noteSubtype(curr->ifTrue, curr); + self()->noteSubtype(curr->ifFalse, curr); + } + void visitDrop(Drop* curr) {} + void visitReturn(Return* curr) { + if (curr->value) { + self()->noteSubtype(curr->value, self()->getFunction()->getResults()); + } + } + void visitMemorySize(MemorySize* curr) {} + void visitMemoryGrow(MemoryGrow* curr) {} + void visitUnreachable(Unreachable* curr) {} + void visitPop(Pop* curr) {} + void visitRefNull(RefNull* curr) {} + void visitRefIsNull(RefIsNull* curr) {} + void visitRefFunc(RefFunc* curr) {} + void visitRefEq(RefEq* curr) {} + void visitTableGet(TableGet* curr) {} + void visitTableSet(TableSet* curr) { + self()->noteSubtype(curr->value, + self()->getModule()->getTable(curr->table)->type); + } + void visitTableSize(TableSize* curr) {} + void visitTableGrow(TableGrow* curr) {} + void visitTableFill(TableFill* curr) { + self()->noteSubtype(curr->value, + self()->getModule()->getTable(curr->table)->type); + } + void visitTableCopy(TableCopy* curr) { + self()->noteSubtype(self()->getModule()->getTable(curr->sourceTable)->type, + self()->getModule()->getTable(curr->destTable)->type); + } + void visitTry(Try* curr) { + self()->noteSubtype(curr->body, curr); + for (auto* body : curr->catchBodies) { + self()->noteSubtype(body, curr); + } + } + void visitThrow(Throw* curr) { + Type params = self()->getModule()->getTag(curr->tag)->sig.params; + assert(params.size() == curr->operands.size()); + for (size_t i = 0, size = curr->operands.size(); i < size; ++i) { + self()->noteSubtype(curr->operands[i], params[i]); + } + } + void visitRethrow(Rethrow* curr) {} + void visitTupleMake(TupleMake* curr) {} + void visitTupleExtract(TupleExtract* curr) {} + void visitRefI31(RefI31* curr) {} + void visitI31Get(I31Get* curr) {} + void visitCallRef(CallRef* curr) { + if (!curr->target->type.isSignature()) { + return; + } + handleCall(curr, curr->target->type.getHeapType().getSignature()); + } + void visitRefTest(RefTest* curr) { + self()->noteCast(curr->ref, curr->castType); + } + void visitRefCast(RefCast* curr) { self()->noteCast(curr->ref, curr); } + void visitBrOn(BrOn* curr) { + if (curr->op == BrOnCast || curr->op == BrOnCastFail) { + self()->noteCast(curr->ref, curr->castType); + } + self()->noteSubtype(curr->getSentType(), + self()->findBreakTarget(curr->name)); + } + void visitStructNew(StructNew* curr) { + if (!curr->type.isStruct() || curr->isWithDefault()) { + return; + } + const auto& fields = curr->type.getHeapType().getStruct().fields; + assert(fields.size() == curr->operands.size()); + for (size_t i = 0, size = fields.size(); i < size; ++i) { + self()->noteSubtype(curr->operands[i], fields[i].type); + } + } + void visitStructGet(StructGet* curr) {} + void visitStructSet(StructSet* curr) { + if (!curr->ref->type.isStruct()) { + return; + } + const auto& fields = curr->ref->type.getHeapType().getStruct().fields; + self()->noteSubtype(curr->value, fields[curr->index].type); + } + void visitArrayNew(ArrayNew* curr) { + if (!curr->type.isArray() || curr->isWithDefault()) { + return; + } + auto array = curr->type.getHeapType().getArray(); + self()->noteSubtype(curr->init, array.element.type); + } + void visitArrayNewData(ArrayNewData* curr) {} + void visitArrayNewElem(ArrayNewElem* curr) { + if (!curr->type.isArray()) { + return; + } + auto array = curr->type.getHeapType().getArray(); + auto* seg = self()->getModule()->getElementSegment(curr->segment); + self()->noteSubtype(seg->type, array.element.type); + } + void visitArrayNewFixed(ArrayNewFixed* curr) { + if (!curr->type.isArray()) { + return; + } + auto array = curr->type.getHeapType().getArray(); + for (auto* value : curr->values) { + self()->noteSubtype(value, array.element.type); + } + } + void visitArrayGet(ArrayGet* curr) {} + void visitArraySet(ArraySet* curr) { + if (!curr->ref->type.isArray()) { + return; + } + auto array = curr->ref->type.getHeapType().getArray(); + self()->noteSubtype(curr->value->type, array.element.type); + } + void visitArrayLen(ArrayLen* curr) {} + void visitArrayCopy(ArrayCopy* curr) { + if (!curr->srcRef->type.isArray() || !curr->destRef->type.isArray()) { + return; + } + auto src = curr->srcRef->type.getHeapType().getArray(); + auto dest = curr->destRef->type.getHeapType().getArray(); + self()->noteSubtype(src.element.type, dest.element.type); + } + void visitArrayFill(ArrayFill* curr) { + if (!curr->ref->type.isArray()) { + return; + } + auto array = curr->ref->type.getHeapType().getArray(); + self()->noteSubtype(curr->value->type, array.element.type); + } + void visitArrayInitData(ArrayInitData* curr) {} + void visitArrayInitElem(ArrayInitElem* curr) { + if (!curr->ref->type.isArray()) { + return; + } + auto array = curr->ref->type.getHeapType().getArray(); + auto* seg = self()->getModule()->getElementSegment(curr->segment); + self()->noteSubtype(seg->type, array.element.type); + } + void visitRefAs(RefAs* curr) {} + void visitStringNew(StringNew* curr) {} + void visitStringConst(StringConst* curr) {} + void visitStringMeasure(StringMeasure* curr) {} + void visitStringEncode(StringEncode* curr) {} + void visitStringConcat(StringConcat* curr) {} + void visitStringEq(StringEq* curr) {} + void visitStringAs(StringAs* curr) {} + void visitStringWTF8Advance(StringWTF8Advance* curr) {} + void visitStringWTF16Get(StringWTF16Get* curr) {} + void visitStringIterNext(StringIterNext* curr) {} + void visitStringIterMove(StringIterMove* curr) {} + void visitStringSliceWTF(StringSliceWTF* curr) {} + void visitStringSliceIter(StringSliceIter* curr) {} +}; + +} // namespace wasm + +#endif // #define wasm_ir_subtype_exprs_h diff --git a/src/passes/Unsubtyping.cpp b/src/passes/Unsubtyping.cpp index e5d9453a8..67a3c4e85 100644 --- a/src/passes/Unsubtyping.cpp +++ b/src/passes/Unsubtyping.cpp @@ -16,7 +16,7 @@ #include <unordered_map> -#include "ir/branch-utils.h" +#include "ir/subtype-exprs.h" #include "ir/subtypes.h" #include "ir/type-updating.h" #include "ir/utils.h" @@ -105,7 +105,8 @@ namespace wasm { namespace { struct Unsubtyping - : WalkerPass<ControlFlowWalker<Unsubtyping, OverriddenVisitor<Unsubtyping>>> { + : WalkerPass< + ControlFlowWalker<Unsubtyping, SubtypingDiscoverer<Unsubtyping>>> { // The new set of supertype relations. std::unordered_map<HeapType, HeapType> supertypes; @@ -181,6 +182,17 @@ struct Unsubtyping noteSubtype(sub.getHeapType(), super.getHeapType()); } + // Note a subtyping where one or both sides are expressions. + void noteSubtype(Expression* sub, Type super) { + noteSubtype(sub->type, super); + } + void noteSubtype(Type sub, Expression* super) { + noteSubtype(sub, super->type); + } + void noteSubtype(Expression* sub, Expression* super) { + noteSubtype(sub->type, super->type); + } + void noteCast(HeapType src, HeapType dest) { if (src == dest || dest.isBottom()) { return; @@ -198,6 +210,12 @@ struct Unsubtyping noteCast(src.getHeapType(), dest.getHeapType()); } + // Note a cast where one or both sides are expressions. + void noteCast(Expression* src, Type dest) { noteCast(src->type, dest); } + void noteCast(Expression* src, Expression* dest) { + noteCast(src->type, dest->type); + } + void analyzePublicTypes(Module& wasm) { // We cannot change supertypes for anything public. for (auto type : ModuleUtils::getPublicHeapTypes(wasm)) { @@ -317,263 +335,6 @@ struct Unsubtyping // Visit the rest of the code that is not in functions. walkModuleCode(wasm); } - - void visitFunction(Function* func) { - if (func->body) { - noteSubtype(func->body->type, func->getResults()); - } - } - void visitGlobal(Global* global) { - if (global->init) { - noteSubtype(global->init->type, global->type); - } - } - void visitElementSegment(ElementSegment* seg) { - if (seg->offset) { - noteSubtype(seg->type, getModule()->getTable(seg->table)->type); - } - for (auto init : seg->data) { - noteSubtype(init->type, seg->type); - } - } - void visitNop(Nop* curr) {} - void visitBlock(Block* curr) { - if (!curr->list.empty()) { - noteSubtype(curr->list.back()->type, curr->type); - } - } - void visitIf(If* curr) { - if (curr->ifFalse) { - noteSubtype(curr->ifTrue->type, curr->type); - noteSubtype(curr->ifFalse->type, curr->type); - } - } - void visitLoop(Loop* curr) { noteSubtype(curr->body->type, curr->type); } - void visitBreak(Break* curr) { - if (curr->value) { - noteSubtype(curr->value->type, findBreakTarget(curr->name)->type); - } - } - void visitSwitch(Switch* curr) { - if (curr->value) { - for (auto name : BranchUtils::getUniqueTargets(curr)) { - noteSubtype(curr->value->type, findBreakTarget(name)->type); - } - } - } - template<typename T> void handleCall(T* curr, Signature sig) { - assert(curr->operands.size() == sig.params.size()); - for (size_t i = 0, size = sig.params.size(); i < size; ++i) { - noteSubtype(curr->operands[i]->type, sig.params[i]); - } - if (curr->isReturn) { - noteSubtype(sig.results, getFunction()->getResults()); - } - } - void visitCall(Call* curr) { - handleCall(curr, getModule()->getFunction(curr->target)->getSig()); - } - void visitCallIndirect(CallIndirect* curr) { - handleCall(curr, curr->heapType.getSignature()); - auto* table = getModule()->getTable(curr->table); - auto tableType = table->type.getHeapType(); - if (HeapType::isSubType(tableType, curr->heapType)) { - // Unlike other casts, where cast targets are always subtypes of cast - // sources, call_indirect target types may be supertypes of their source - // table types. In this case, the cast will always succeed, but only if we - // keep the types related. - noteSubtype(tableType, curr->heapType); - } else if (HeapType::isSubType(curr->heapType, tableType)) { - noteCast(tableType, curr->heapType); - } else { - // The types are unrelated and the cast will fail. We can keep the types - // unrelated. - } - } - void visitLocalGet(LocalGet* curr) {} - void visitLocalSet(LocalSet* curr) { - noteSubtype(curr->value->type, getFunction()->getLocalType(curr->index)); - } - void visitGlobalGet(GlobalGet* curr) {} - void visitGlobalSet(GlobalSet* curr) { - noteSubtype(curr->value->type, getModule()->getGlobal(curr->name)->type); - } - void visitLoad(Load* curr) {} - void visitStore(Store* curr) {} - void visitAtomicRMW(AtomicRMW* curr) {} - void visitAtomicCmpxchg(AtomicCmpxchg* curr) {} - void visitAtomicWait(AtomicWait* curr) {} - void visitAtomicNotify(AtomicNotify* curr) {} - void visitAtomicFence(AtomicFence* curr) {} - void visitSIMDExtract(SIMDExtract* curr) {} - void visitSIMDReplace(SIMDReplace* curr) {} - void visitSIMDShuffle(SIMDShuffle* curr) {} - void visitSIMDTernary(SIMDTernary* curr) {} - void visitSIMDShift(SIMDShift* curr) {} - void visitSIMDLoad(SIMDLoad* curr) {} - void visitSIMDLoadStoreLane(SIMDLoadStoreLane* curr) {} - void visitMemoryInit(MemoryInit* curr) {} - void visitDataDrop(DataDrop* curr) {} - void visitMemoryCopy(MemoryCopy* curr) {} - void visitMemoryFill(MemoryFill* curr) {} - void visitConst(Const* curr) {} - void visitUnary(Unary* curr) {} - void visitBinary(Binary* curr) {} - void visitSelect(Select* curr) { - noteSubtype(curr->ifTrue->type, curr->type); - noteSubtype(curr->ifFalse->type, curr->type); - } - void visitDrop(Drop* curr) {} - void visitReturn(Return* curr) { - if (curr->value) { - noteSubtype(curr->value->type, getFunction()->getResults()); - } - } - void visitMemorySize(MemorySize* curr) {} - void visitMemoryGrow(MemoryGrow* curr) {} - void visitUnreachable(Unreachable* curr) {} - void visitPop(Pop* curr) {} - void visitRefNull(RefNull* curr) {} - void visitRefIsNull(RefIsNull* curr) {} - void visitRefFunc(RefFunc* curr) {} - void visitRefEq(RefEq* curr) {} - void visitTableGet(TableGet* curr) {} - void visitTableSet(TableSet* curr) { - noteSubtype(curr->value->type, getModule()->getTable(curr->table)->type); - } - void visitTableSize(TableSize* curr) {} - void visitTableGrow(TableGrow* curr) {} - void visitTableFill(TableFill* curr) { - noteSubtype(curr->value->type, getModule()->getTable(curr->table)->type); - } - void visitTableCopy(TableCopy* curr) { - noteSubtype(getModule()->getTable(curr->sourceTable)->type, - getModule()->getTable(curr->destTable)->type); - } - void visitTry(Try* curr) { - noteSubtype(curr->body->type, curr->type); - for (auto* body : curr->catchBodies) { - noteSubtype(body->type, curr->type); - } - } - void visitThrow(Throw* curr) { - Type params = getModule()->getTag(curr->tag)->sig.params; - assert(params.size() == curr->operands.size()); - for (size_t i = 0, size = curr->operands.size(); i < size; ++i) { - noteSubtype(curr->operands[i]->type, params[i]); - } - } - void visitRethrow(Rethrow* curr) {} - void visitTupleMake(TupleMake* curr) {} - void visitTupleExtract(TupleExtract* curr) {} - void visitRefI31(RefI31* curr) {} - void visitI31Get(I31Get* curr) {} - void visitCallRef(CallRef* curr) { - if (!curr->target->type.isSignature()) { - return; - } - handleCall(curr, curr->target->type.getHeapType().getSignature()); - } - void visitRefTest(RefTest* curr) { - noteCast(curr->ref->type, curr->castType); - } - void visitRefCast(RefCast* curr) { noteCast(curr->ref->type, curr->type); } - void visitBrOn(BrOn* curr) { - if (curr->op == BrOnCast || curr->op == BrOnCastFail) { - noteCast(curr->ref->type, curr->castType); - } - noteSubtype(curr->getSentType(), findBreakTarget(curr->name)->type); - } - void visitStructNew(StructNew* curr) { - if (!curr->type.isStruct() || curr->isWithDefault()) { - return; - } - const auto& fields = curr->type.getHeapType().getStruct().fields; - assert(fields.size() == curr->operands.size()); - for (size_t i = 0, size = fields.size(); i < size; ++i) { - noteSubtype(curr->operands[i]->type, fields[i].type); - } - } - void visitStructGet(StructGet* curr) {} - void visitStructSet(StructSet* curr) { - if (!curr->ref->type.isStruct()) { - return; - } - const auto& fields = curr->ref->type.getHeapType().getStruct().fields; - noteSubtype(curr->value->type, fields[curr->index].type); - } - void visitArrayNew(ArrayNew* curr) { - if (!curr->type.isArray() || curr->isWithDefault()) { - return; - } - auto array = curr->type.getHeapType().getArray(); - noteSubtype(curr->init->type, array.element.type); - } - void visitArrayNewData(ArrayNewData* curr) {} - void visitArrayNewElem(ArrayNewElem* curr) { - if (!curr->type.isArray()) { - return; - } - auto array = curr->type.getHeapType().getArray(); - auto* seg = getModule()->getElementSegment(curr->segment); - noteSubtype(seg->type, array.element.type); - } - void visitArrayNewFixed(ArrayNewFixed* curr) { - if (!curr->type.isArray()) { - return; - } - auto array = curr->type.getHeapType().getArray(); - for (auto* value : curr->values) { - noteSubtype(value->type, array.element.type); - } - } - void visitArrayGet(ArrayGet* curr) {} - void visitArraySet(ArraySet* curr) { - if (!curr->ref->type.isArray()) { - return; - } - auto array = curr->ref->type.getHeapType().getArray(); - noteSubtype(curr->value->type, array.element.type); - } - void visitArrayLen(ArrayLen* curr) {} - void visitArrayCopy(ArrayCopy* curr) { - if (!curr->srcRef->type.isArray() || !curr->destRef->type.isArray()) { - return; - } - auto src = curr->srcRef->type.getHeapType().getArray(); - auto dest = curr->destRef->type.getHeapType().getArray(); - noteSubtype(src.element.type, dest.element.type); - } - void visitArrayFill(ArrayFill* curr) { - if (!curr->ref->type.isArray()) { - return; - } - auto array = curr->ref->type.getHeapType().getArray(); - noteSubtype(curr->value->type, array.element.type); - } - void visitArrayInitData(ArrayInitData* curr) {} - void visitArrayInitElem(ArrayInitElem* curr) { - if (!curr->ref->type.isArray()) { - return; - } - auto array = curr->ref->type.getHeapType().getArray(); - auto* seg = getModule()->getElementSegment(curr->segment); - noteSubtype(seg->type, array.element.type); - } - void visitRefAs(RefAs* curr) {} - void visitStringNew(StringNew* curr) {} - void visitStringConst(StringConst* curr) {} - void visitStringMeasure(StringMeasure* curr) {} - void visitStringEncode(StringEncode* curr) {} - void visitStringConcat(StringConcat* curr) {} - void visitStringEq(StringEq* curr) {} - void visitStringAs(StringAs* curr) {} - void visitStringWTF8Advance(StringWTF8Advance* curr) {} - void visitStringWTF16Get(StringWTF16Get* curr) {} - void visitStringIterNext(StringIterNext* curr) {} - void visitStringIterMove(StringIterMove* curr) {} - void visitStringSliceWTF(StringSliceWTF* curr) {} - void visitStringSliceIter(StringSliceIter* curr) {} }; } // anonymous namespace |