diff --git a/src/ir/subtype-exprs.h b/src/ir/subtype-exprs.h index dc9ea1432f0..0c0c41f9831 100644 --- a/src/ir/subtype-exprs.h +++ b/src/ir/subtype-exprs.h @@ -190,7 +190,10 @@ struct SubtypingDiscoverer : public OverriddenVisitor { void visitRefNull(RefNull* curr) {} void visitRefIsNull(RefIsNull* curr) {} void visitRefFunc(RefFunc* curr) {} - void visitRefEq(RefEq* curr) {} + void visitRefEq(RefEq* curr) { + self()->noteSubtype(curr->left, Type(HeapType::eq, Nullable)); + self()->noteSubtype(curr->right, Type(HeapType::eq, Nullable)); + } void visitTableGet(TableGet* curr) {} void visitTableSet(TableSet* curr) { self()->noteSubtype(curr->value, @@ -223,12 +226,27 @@ struct SubtypingDiscoverer : public OverriddenVisitor { void visitTupleMake(TupleMake* curr) {} void visitTupleExtract(TupleExtract* curr) {} void visitRefI31(RefI31* curr) {} - void visitI31Get(I31Get* curr) {} + void visitI31Get(I31Get* curr) { + self()->noteSubtype(curr->i31, Type(HeapType::i31, Nullable)); + } void visitCallRef(CallRef* curr) { - if (!curr->target->type.isSignature()) { - return; + // Even if we are unreachable, the target must be valid, and in particular + // it cannot be funcref - it must be a proper signature type. We could + // perhaps have |addStrictSubtype| to handle that, but for now just require + // that the target keep its type. + // + // Note that even if we are reachable, there is an interaction between the + // target and the the types of the parameters and results (the target's type + // must support the parameter and result types properly), and so it is not + // obvious how users would want to optimize here (if they are trying to + // generalize, should they generalize the target more or the parameters + // more? etc.), so we do the simple thing here for now of requiring the + // target type not generalize. + self()->noteSubtype(curr->target, curr->target->type); + + if (curr->target->type.isSignature()) { + handleCall(curr, curr->target->type.getHeapType().getSignature()); } - handleCall(curr, curr->target->type.getHeapType().getSignature()); } void visitRefTest(RefTest* curr) { self()->noteCast(curr->ref, curr->castType); @@ -284,13 +302,14 @@ struct SubtypingDiscoverer : public OverriddenVisitor { 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); + self()->noteSubtype(curr->value, array.element.type); } void visitArrayLen(ArrayLen* curr) {} void visitArrayCopy(ArrayCopy* curr) { @@ -306,7 +325,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor { return; } auto array = curr->ref->type.getHeapType().getArray(); - self()->noteSubtype(curr->value->type, array.element.type); + self()->noteSubtype(curr->value, array.element.type); } void visitArrayInitData(ArrayInitData* curr) {} void visitArrayInitElem(ArrayInitElem* curr) { @@ -317,7 +336,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor { auto* seg = self()->getModule()->getElementSegment(curr->segment); self()->noteSubtype(seg->type, array.element.type); } - void visitRefAs(RefAs* curr) {} + void visitRefAs(RefAs* curr) { self()->noteCast(curr->value, curr); } void visitStringNew(StringNew* curr) {} void visitStringConst(StringConst* curr) {} void visitStringMeasure(StringMeasure* curr) {} diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt index 53f52e23fe6..7d3a6c9d720 100644 --- a/src/passes/CMakeLists.txt +++ b/src/passes/CMakeLists.txt @@ -105,6 +105,7 @@ set(passes_SOURCES ReReloop.cpp TrapMode.cpp TypeGeneralizing.cpp + TypeGeneralizing2.cpp TypeRefining.cpp TypeMerging.cpp TypeSSA.cpp diff --git a/src/passes/TypeGeneralizing2.cpp b/src/passes/TypeGeneralizing2.cpp new file mode 100644 index 00000000000..0bdb2edcd1d --- /dev/null +++ b/src/passes/TypeGeneralizing2.cpp @@ -0,0 +1,575 @@ +/* + * 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. + */ + +// +// This is an alternative implementation of TypeGeneralizing, for comparison and +// exploration. +// +// Generalize the types of program locations as much as possible, both to +// eliminate unnecessarily refined types from the type section and (TODO) to +// weaken casts that cast to unnecessarily refined types. If the casts are +// weakened enough, they will be able to be removed by OptimizeInstructions. +// +// Perform an analysis on the types of program to discover how much the type of +// each location can be generalized without breaking validation or changing +// program behavior. The analysis is a basic flow operation: we find the +// "static", unavoidable constraints and consider them roots, and then flow from +// there to affect everything else. For example, when we update the type of a +// block then the block's last child must then be flowed to, as the child must +// be a subtype of the block, etc. +// + +#include "analysis/lattices/valtype.h" +#include "ir/possible-contents.h" +#include "ir/subtype-exprs.h" +#include "ir/utils.h" +#include "pass.h" +#include "support/unique_deferring_queue.h" +#include "wasm-traversal.h" +#include "wasm.h" + +#define TYPEGEN_DEBUG 0 + +#if TYPEGEN_DEBUG +#define DBG(statement) statement +#else +#define DBG(statement) +#endif + +namespace wasm { + +namespace { + +struct TypeGeneralizing + : WalkerPass>> { + using Super = WalkerPass< + ControlFlowWalker>>; + + bool isFunctionParallel() override { return true; } + + std::unique_ptr create() override { + return std::make_unique(); + } + + // Visitors during the walk. We purposefully do not visit the super of any of + // these. + + // We track local operations so that we can optimize them later. + void visitLocalGet(LocalGet* curr) { gets.push_back(curr); } + void visitLocalSet(LocalSet* curr) { + sets.push_back(curr); + + // If this is a parameter then we cannot modify it, and so we add a root + // here: the value written to the param must be a subtype of the never-to- + // change param type. + if (getFunction()->isParam(curr->index)) { + addRoot(curr->value, getFunction()->getLocalType(curr->index)); + } + } + + std::vector gets; + std::vector sets; + + void visitStructGet(StructGet* curr) { + // Connect the reference to us. As the reference becomes more refined, so do + // we. This is handled in the transfer function. + connectSrcToDest(curr->ref, curr); + } + void visitStructSet(StructSet* curr) { + auto refType = curr->ref->type; + if (!refType.isStruct()) { + // This is a bottom type or unreachable. Do not allow the ref to change. + addRoot(curr->ref, refType); + return; + } + + // Find the constraint on the ref and value and apply them. Note that these + // do not need any dynamic handling, because a struct.set implies + // mutability, which implies the field's type is identical in all supers and + // subs due to how the wasm type system works. + auto minimalRefType = + getLeastRefinedStruct(refType.getHeapType(), curr->index); + addRoot(curr->ref, Type(minimalRefType, Nullable)); + const auto& fields = minimalRefType.getStruct().fields; + addRoot(curr->value, fields[curr->index].type); + } + + void requireNonBasicArray(Expression* ref) { + if (!ref->type.isArray()) { + self()->noteSubtype(ref, ref->type); + return; + } + auto heapType = getLeastRefinedArray(ref->type.getHeapType()); + self()->noteSubtype(ref, Type(heapType, Nullable)); + } + void requireBasicArray(Expression* ref) { + if (!ref->type.isArray()) { + self()->noteSubtype(ref, ref->type); + return; + } + self()->noteSubtype(ref, Type(HeapType::array, Nullable)); + } + + void visitArrayGet(ArrayGet* curr) { + // As with StructGet, this is handled in the transfer function. + connectSrcToDest(curr->ref, curr); + } + void visitArraySet(ArraySet* curr) { + requireNonBasicArray(curr->ref); + Super::visitArraySet(curr); + } + void visitArrayLen(ArrayLen* curr) { requireBasicArray(curr->ref); } + void visitArrayCopy(ArrayCopy* curr) { + // Start minimal; see the next comment for more. + requireNonBasicArray(curr->destRef); + requireNonBasicArray(curr->srcRef); + // As the dest ref refines, we may need to refine the source ref. Connect + // those two through the copy, and we will handle that in the transfer + // function (the copy itself has no value, and we will calculate the change + // for the dest there and apply it; we do not connect the source to the dest + // directly because the copy mediates between them and defines in what + // manner the source affects the dest). + connectSrcToDest(curr, curr->destRef); + connectSrcToDest(curr->srcRef, curr); + } + void visitArrayFill(ArrayFill* curr) { + requireNonBasicArray(curr->ref); + Super::visitArrayFill(curr); + } + void visitArrayInitData(ArrayInitData* curr) { + requireNonBasicArray(curr->ref); + } + void visitArrayInitElem(ArrayInitElem* curr) { + requireNonBasicArray(curr->ref); + Super::visitArrayInitElem(curr); + } + + // Hooks that are called by SubtypingDiscoverer. + + void noteSubtype(Type sub, Type super) { + // Nothing to do; a totally static and fixed constraint. + } + void noteSubtype(HeapType sub, HeapType super) { + // As above. + } + void noteSubtype(Expression* sub, Type super) { + // This expression's type must be a subtype of a fixed type, so it is a + // root. + addRoot(sub, super); + } + void noteSubtype(Type sub, Expression* super) { + // A fixed type that must be a subtype of an expression's type. We do not + // need to do anything here, as we will just be generalizing expression + // types, so we will not break these constraints. + } + void noteSubtype(Expression* sub, Expression* super) { + // Two expressions with subtyping between them. Add a link in the graph so + // that we flow requirements along. + connectSrcToDest(sub, super); + } + + void noteCast(HeapType src, HeapType dest) { + // Same as in noteSubtype. + } + void noteCast(Expression* src, Type dest) { + // Same as in noteSubtype. + } + void noteCast(Expression* src, Expression* dest) { + if (dest->is()) { + // All a cast requires of its input is that it have the proper top type so + // it can be cast. TODO: Can we do better? Nullability? + addRoot( + src, + Type(dest->type.getHeapType().getTop(), dest->type.getNullability())); + } else if (dest->is()) { + // Handle this in an optimal manner in the transfer function. + connectSrcToDest(src, dest); + } + } + + // Internal graph for the flow. We track the predecessors so that we know who + // to update after updating a location. For example, when we update the type + // of a block then the block's last child must then be flowed to, so the child + // is a pred of the block. We also track successors so that we can tell where + // to read updates from (which the transfer function needs in some cases). + // TODO: SmallVector<1>s? + std::unordered_map> preds; + std::unordered_map> succs; + + // Connect a source location to where that value arrives. For example, a + // block's last element arrives in the block. + void connectSrcToDest(Expression* src, Expression* dest) { + connectSrcToDest(getLocation(src), getLocation(dest)); + } + + void connectSrcToDest(const Location& srcLoc, const Location& destLoc) { + DBG({ + std::cerr << "Connecting "; + dump(srcLoc); + std::cerr << " to "; + dump(destLoc); + }); + preds[destLoc].push_back(srcLoc); + succs[srcLoc].push_back(destLoc); + } + + Location getLocation(Expression* expr) { + // TODO: tuples + return ExpressionLocation{expr, 0}; + } + + // The analysis we do here is on types: each location will monotonically + // increase until all locations stabilize at the fixed point. + analysis::ValType typeLattice; + + using LatticeElement = analysis::ValType::Element; + + // The roots in the graph: constraints that we know from the start and from + // which the flow begins. Each root is a location and its type. + std::unordered_map roots; + + void addRoot(Expression* expr, LatticeElement type) { + if (!type.isRef()) { + return; + } + // There may already exist a type for a particular root, so merge them in, + // in the same manner as during the flow (a meet). + typeLattice.meet(roots[getLocation(expr)], type); + } + + // The types of locations as we discover them. When the flow is complete, + // these are the final types. + std::unordered_map locTypes; + + // A list of locations to process. When we process one, we compute the + // transfer function for it and set up any further flow. + UniqueDeferredQueue toFlow; + + // After we update a location's type, this function sets up the flow to + // reach all preds. + void flowFrom(Location loc) { + DBG({ + std::cerr << "Flowing from "; + dump(loc); + }); + for (auto dep : preds[loc]) { + DBG({ + std::cerr << " to "; + dump(dep); + }); + toFlow.push(dep); + } + } + + // Update a location, that is, apply the transfer function there. This reads + // the information that affects this location and computes the new type there. + // If the type changed, then apply it and flow onwards. + // + // We are given the values at successor locations, that is, the values that + // influence us and that we read from to compute our new value. We are also + // given the locations they arrive from, or an empty optional if this is a + // root (roots are the initial values where the flow begins). + void update(Location loc, + std::vector succValues, + std::optional> succLocs) { + DBG({ + std::cerr << "Updating \n"; + dump(loc); + }); + + // Some successors have special handling. Where such handling exists, it + // updates |succValues| for the processing below (which typically ends up + // being used in the generic meet operation, but there is customization in + // some cases there as well) and may also update |loc| (if the location to + // be updated is different). + if (succLocs && succLocs->size() == 1) { + auto succLoc = (*succLocs)[0]; + auto& succValue = succValues[0]; + if (auto* exprLoc = std::get_if(&succLoc)) { + if (auto* get = exprLoc->expr->dynCast()) { + // The situation is this: + // + // (struct.get K ;; succLoc + // (loc) ;; loc + // ) + // + // That is, our successor is a struct.get, so we are the reference of + // that struct.get. + succValue = transferStructGet(succValue, get->ref, get->index); + } else if (auto* get = exprLoc->expr->dynCast()) { + succValue = transferArrayGet(succValue, get->ref); + } else if (auto* copy = exprLoc->expr->dynCast()) { + succValue = transferArrayCopy(succValue, copy); + } + } + } + + DBG({ + for (auto value : succValues) { + std::cerr << " succ value: " << ModuleType(*getModule(), value) + << '\n'; + } + }); + + auto& locType = locTypes[loc]; + auto old = locType; + + // Some locations have special handling. + bool handled = false; + if (auto* exprLoc = std::get_if(&loc)) { + if (auto* refAs = exprLoc->expr->dynCast()) { + // There is a single successor (the place this RefAs sends a value + // to). + auto succType = succValues[0]; + switch (refAs->op) { + case RefAsNonNull: + // ref.as_non_null does not require its input to be non-nullable - + // all it does is enforce non-nullability - but it does pass along + // the heap type requirement. (We do not need to do a meet + // operation here because our monotonicity is proven by succ's.) + locType = Type(succType.getHeapType(), Nullable); + break; + case ExternInternalize: + // Internalize accepts any input of heap type extern. Pass along + // the nullability of the successor (whose monotonicity proves + // ours). + locType = Type(HeapType::ext, succType.getNullability()); + break; + case ExternExternalize: + locType = Type(HeapType::any, succType.getNullability()); + break; + } + handled = true; + } + } + if (!handled) { + // Perform a generic meet operation over all successors. + for (auto value : succValues) { + DBG({ + std::cerr << " old: " << ModuleType(*getModule(), locType) + << " new: " << ModuleType(*getModule(), value) << "\n"; + }); + if (typeLattice.meet(locType, value)) { + DBG({ + std::cerr << " result: " << ModuleType(*getModule(), locType) + << "\n"; + }); + } + } + } + if (locType != old) { + // We changed something here, so flow onwards. + flowFrom(loc); + +#ifndef NDEBUG + // Verify monotonicity. + assert(typeLattice.compare(locType, old) == analysis::LESS); +#endif + } + } + + // Given a struct.get operation, this receives the type the get must emit, the + // reference child of the struct.get, and the index it operates on. It then + // computes the type for the reference and returns that. This forms the core + // of the transfer logic for a struct.get. + LatticeElement + transferStructGet(Type outputType, Expression* ref, Index index) { + if (!ref->type.isStruct()) { + return ref->type; + } + + // Get the least-refined struct type that still has (at least) the necessary + // type for that field. This is monotonic because as the field type + // refines we will return something more refined (or equal) here. + auto heapType = + getLeastRefinedStruct(ref->type.getHeapType(), index, outputType); + return Type(heapType, Nullable); + } + + // Return the least refined struct type parent of a given type that still has + // a particular field, and if a field type is provided, that also has a field + // that is at least as refined as the given one. + HeapType getLeastRefinedStruct(HeapType curr, + Index index, + std::optional fieldType = {}) { + while (true) { + auto next = curr.getDeclaredSuperType(); + if (!next) { + // There is no super. + break; + } + const auto& fields = next->getStruct().fields; + if (index >= fields.size() || + (fieldType && !Type::isSubType(fields[index].type, *fieldType))) { + // There is no field at that index, or it is unsuitable. + break; + } + curr = *next; + } + return curr; + } + + LatticeElement transferArrayGet(LatticeElement outputType, Expression* ref) { + if (!ref->type.isArray()) { + return ref->type; + } + + auto heapType = getLeastRefinedArray(ref->type.getHeapType(), outputType); + return Type(heapType, Nullable); + } + + // Given a new type for the source of an ArrayCopy, compute the new type for + // the dest. + LatticeElement transferArrayCopy(LatticeElement destType, ArrayCopy* copy) { + if (!destType.isArray()) { + // No constraint here. + return Type(HeapType::array, Nullable); + } + // Similar to ArrayGet: We know the current output type, and can compute + // the input/dest. + auto destElementType = destType.getHeapType().getArray().element.type; + return transferArrayGet(destElementType, copy->srcRef); + } + + // Similar to getLeastRefinedStruct. + HeapType getLeastRefinedArray(HeapType curr, + std::optional elementType = {}) { + while (true) { + auto next = curr.getDeclaredSuperType(); + if (!next) { + // There is no super. + break; + } + if (elementType && + !Type::isSubType(next->getArray().element.type, *elementType)) { + // The element is not suitable. + break; + } + curr = *next; + } + return curr; + } + + void visitFunction(Function* func) { + Super::visitFunction(func); + + DBG({ std::cerr << "TypeGeneralizing: " << func->name << "\n"; }); + + // Finish setting up the graph: LocalLocals are "abstract" things that we + // did not walk, so set them up manually. Each LocalLocation is connected + // to the sets and gets for that index. + for (auto* get : gets) { + // This is not true subtyping here - really these have the same type - but + // this sets up the connections between them. This is important to prevent + // quadratic size of the graph: N gets for an index are connected to the + // single location for that index, which is connected to M sets for it, + // giving us N + M instead of N * M (which we'd get if we connected gets + // to sets directly). + connectSrcToDest(LocalLocation{func, get->index}, getLocation(get)); + } + for (auto* set : sets) { + connectSrcToDest(getLocation(set->value), + LocalLocation{func, set->index}); + if (set->type.isConcrete()) { + // This is a tee with a value, and that value shares the location of + // the local. + connectSrcToDest(LocalLocation{func, set->index}, getLocation(set)); + } + } + + // Set up locals. + auto numLocals = func->getNumLocals(); + for (Index i = 0; i < numLocals; i++) { + auto type = func->getLocalType(i); + if (type.isRef()) { + if (func->isParam(i)) { + // We cannot alter params. + locTypes[LocalLocation{func, i}] = type; + } else { + // Start each var with the top type. If we see nothing else, that is + // what will remain. + locTypes[LocalLocation{func, i}] = + Type(type.getHeapType().getTop(), Nullable); + } + } + } + + // First, apply the roots. Each is an update of a location with a single + // successor, the type we start that root from. + for (auto& [loc, value] : roots) { + DBG({ + std::cerr << "root: " << value << "\n"; + dump(loc); + }); + update(loc, {value}, {}); + } + + // Second, perform the flow: iteratively get an location that might change + // when we update it, as its successors changed, and if it does queue to + // flow from there. + while (!toFlow.empty()) { + auto loc = toFlow.pop(); + auto& succLocs = succs[loc]; + std::vector succValues; + for (auto& succ : succLocs) { + succValues.push_back(locTypes[succ]); + } + update(loc, succValues, succLocs); + } + + // Finally, apply the results of the flow: the types at LocalLocations are + // now the types of the locals. + auto numParams = func->getNumParams(); + for (Index i = numParams; i < numLocals; i++) { + auto& localType = func->vars[i - numParams]; + if (localType.isRef()) { + localType = locTypes[LocalLocation{func, i}]; + assert(localType.isRef()); + } + } + for (auto* get : gets) { + get->type = func->getLocalType(get->index); + } + for (auto* set : sets) { + if (set->type.isRef()) { + set->type = func->getLocalType(set->index); + } + } + + // TODO: avoid when not needed + ReFinalize().walkFunctionInModule(func, getModule()); + } + +#ifdef TYPEGEN_DEBUG + void dump(const Location& loc) { + if (auto* exprLoc = std::get_if(&loc)) { + std::cerr << "exprLoc " << ModuleExpression(*getModule(), exprLoc->expr) + << '\n'; + } else if (auto* localLoc = std::get_if(&loc)) { + std::cerr << "localloc " << localLoc->index << '\n'; + } else { + std::cerr << "unknown location\n"; + } + } +#endif +}; + +} // anonymous namespace + +Pass* createTypeGeneralizing2Pass() { return new TypeGeneralizing; } + +} // namespace wasm diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 38b6c73a6cb..e847d97f94f 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -520,6 +520,9 @@ void PassRegistry::registerPasses() { registerTestPass("experimental-type-generalizing", "generalize types (not yet sound)", createTypeGeneralizingPass); + registerTestPass("experimental-type-generalizing2", + "generalize types (not yet sound); alt version", + createTypeGeneralizing2Pass); } void PassRunner::addIfNoDWARFIssues(std::string passName) { diff --git a/src/passes/passes.h b/src/passes/passes.h index b0b42d5de28..dc1e0b80618 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -164,6 +164,7 @@ Pass* createTrapModeClamp(); Pass* createTrapModeJS(); Pass* createTupleOptimizationPass(); Pass* createTypeGeneralizingPass(); +Pass* createTypeGeneralizing2Pass(); Pass* createTypeRefiningPass(); Pass* createTypeFinalizingPass(); Pass* createTypeMergingPass(); diff --git a/src/wasm.h b/src/wasm.h index 47cc047bb95..a1f97755ca3 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -2354,6 +2354,9 @@ class Module { // Utility for printing an expression with named types. using ModuleExpression = std::pair; +// Utility for printing a type with named types; +using ModuleType = std::pair; + // Utility for printing only the top level of an expression. Named types will be // used if `module` is non-null. struct ShallowExpression { @@ -2374,6 +2377,7 @@ std::ostream& operator<<(std::ostream& o, wasm::Module& module); std::ostream& operator<<(std::ostream& o, wasm::Function& func); std::ostream& operator<<(std::ostream& o, wasm::Expression& expression); std::ostream& operator<<(std::ostream& o, wasm::ModuleExpression pair); +std::ostream& operator<<(std::ostream& o, wasm::ModuleType); std::ostream& operator<<(std::ostream& o, wasm::ShallowExpression expression); std::ostream& operator<<(std::ostream& o, wasm::StackInst& inst); std::ostream& operator<<(std::ostream& o, wasm::StackIR& ir); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index dce0eb64583..d15696c8df6 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -2844,4 +2844,12 @@ size_t hash::operator()(const wasm::TypeInfo& info) const { WASM_UNREACHABLE("unexpected kind"); } +std::ostream& operator<<(std::ostream& os, wasm::ModuleType moduleType) { + return wasm::TypePrinter(os, + [&](wasm::HeapType type) { + return moduleType.first.typeNames[type]; + }) + .print(moduleType.second); +} + } // namespace std diff --git a/test/lit/passes/type-generalizing.wast b/test/lit/passes/type-generalizing.wast index c874cc76cd3..4eada07e5e3 100644 --- a/test/lit/passes/type-generalizing.wast +++ b/test/lit/passes/type-generalizing.wast @@ -1,9 +1,11 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --experimental-type-generalizing -all -S -o - | filecheck %s +;; RzUN: foreach %s %t wasm-opt --experimental-type-generalizing -all -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt --experimental-type-generalizing2 -all -S -o - | filecheck %s --check-prefix=ALTER (module ;; CHECK: (type $void (func)) + ;; ALTER: (type $void (func)) (type $void (func)) ;; CHECK: (type $1 (func (result eqref))) @@ -37,15 +39,49 @@ ;; CHECK: (type $15 (func (param anyref anyref))) ;; CHECK: (global $global-eq (mut eqref) (ref.null none)) + ;; ALTER: (type $1 (func (result eqref))) + + ;; ALTER: (type $2 (func (param eqref))) + + ;; ALTER: (type $3 (func (param eqref anyref))) + + ;; ALTER: (type $4 (func (param anyref))) + + ;; ALTER: (type $5 (func (param i31ref))) + + ;; ALTER: (type $6 (func (param anyref eqref))) + + ;; ALTER: (type $7 (func (result i32))) + + ;; ALTER: (type $8 (func (result nullref))) + + ;; ALTER: (type $9 (func (result structref))) + + ;; ALTER: (type $10 (func (result (ref eq)))) + + ;; ALTER: (type $11 (func (param (ref noextern)) (result anyref))) + + ;; ALTER: (type $12 (func (param (ref noextern)) (result (ref any)))) + + ;; ALTER: (type $13 (func (result externref))) + + ;; ALTER: (type $14 (func (result (ref extern)))) + + ;; ALTER: (type $15 (func (param anyref anyref))) + + ;; ALTER: (global $global-eq (mut eqref) (ref.null none)) (global $global-eq (mut eqref) (ref.null none)) ;; CHECK: (global $global-i32 (mut i32) (i32.const 0)) + ;; ALTER: (global $global-i32 (mut i32) (i32.const 0)) (global $global-i32 (mut i32) (i32.const 0)) ;; CHECK: (table $func-table 0 0 funcref) + ;; ALTER: (table $func-table 0 0 funcref) (table $func-table 0 0 funcref) ;; CHECK: (table $eq-table 0 0 eqref) + ;; ALTER: (table $eq-table 0 0 eqref) (table $eq-table 0 0 eqref) ;; CHECK: (elem declare func $ref-func) @@ -56,6 +92,14 @@ ;; CHECK-NEXT: (local $z (anyref i32)) ;; CHECK-NEXT: (nop) ;; CHECK-NEXT: ) + ;; ALTER: (elem declare func $ref-func) + + ;; ALTER: (func $unconstrained (type $void) + ;; ALTER-NEXT: (local $x i32) + ;; ALTER-NEXT: (local $y anyref) + ;; ALTER-NEXT: (local $z (anyref i32)) + ;; ALTER-NEXT: (nop) + ;; ALTER-NEXT: ) (func $unconstrained ;; This non-ref local should be unmodified (local $x i32) @@ -69,6 +113,10 @@ ;; CHECK-NEXT: (local $var eqref) ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) + ;; ALTER: (func $implicit-return (type $1) (result eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) (func $implicit-return (result eqref) ;; This will be optimized, but only to eqref because of the constraint from the ;; implicit return. @@ -80,6 +128,11 @@ ;; CHECK-NEXT: (local $var anyref) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) + ;; ALTER: (func $implicit-return-unreachable (type $1) (result eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) (func $implicit-return-unreachable (result eqref) ;; We will optimize this all the way to anyref because we don't analyze ;; unreachable code. This would not validate if we didn't run DCE first. @@ -97,6 +150,15 @@ ;; CHECK-NEXT: (local.get $y) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $if (type $1) (result eqref) + ;; ALTER-NEXT: (local $x eqref) + ;; ALTER-NEXT: (local $y eqref) + ;; ALTER-NEXT: (if (result eqref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $x) + ;; ALTER-NEXT: (local.get $y) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $if (result eqref) (local $x i31ref) (local $y i31ref) @@ -115,6 +177,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $loop (type $1) (result eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (loop $loop-in (result eqref) + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $loop (result eqref) (local $var i31ref) ;; Require that typeof($var) <: eqref. @@ -137,6 +205,18 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $br-sent (type $1) (result eqref) + ;; ALTER-NEXT: (local $var1 anyref) + ;; ALTER-NEXT: (local $var2 eqref) + ;; ALTER-NEXT: (block $l (result eqref) + ;; ALTER-NEXT: (call $helper-any_any + ;; ALTER-NEXT: (local.get $var1) + ;; ALTER-NEXT: (br $l + ;; ALTER-NEXT: (local.get $var2) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $br-sent (result eqref) (local $var1 i31ref) (local $var2 i31ref) @@ -166,6 +246,15 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $br-no-sent (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (block $l + ;; ALTER-NEXT: (call $helper-any_any + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: (br $l) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $br-no-sent (local $var i31ref) (block $l @@ -194,6 +283,22 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $br_table-sent (type $3) (param $eq eqref) (param $any anyref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (local.set $eq + ;; ALTER-NEXT: (block $l1 (result eqref) + ;; ALTER-NEXT: (local.set $any + ;; ALTER-NEXT: (block $l2 (result eqref) + ;; ALTER-NEXT: (br_table $l1 $l2 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $br_table-sent (param $eq eqref) (param $any anyref) (local $var i31ref) ;; Require typeof($var) <: eqref. @@ -229,6 +334,22 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $br_table-sent-reversed (type $3) (param $eq eqref) (param $any anyref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (local.set $any + ;; ALTER-NEXT: (block $l1 (result eqref) + ;; ALTER-NEXT: (local.set $eq + ;; ALTER-NEXT: (block $l2 (result eqref) + ;; ALTER-NEXT: (br_table $l1 $l2 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $br_table-sent-reversed (param $eq eqref) (param $any anyref) ;; Same as above, but with the sources of requirements flipped. (local $var i31ref) @@ -257,6 +378,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-set (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (ref.i31 + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $local-set ;; This will be optimized to anyref. (local $var i31ref) @@ -274,6 +403,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-get-set (type $4) (param $dest anyref) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (local.set $dest + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $local-get-set (param $dest anyref) ;; This will be optimized to anyref. (local $var i31ref) @@ -287,6 +422,15 @@ ;; CHECK-NEXT: (local $var anyref) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-get-set-unreachable (type $5) (param $dest i31ref) + ;; ALTER-NEXT: (local $var i31ref) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: (local.set $dest + ;; ALTER-NEXT: (local.tee $var + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $local-get-set-unreachable (param $dest i31ref) ;; This is not constrained by reachable code, so we will optimize it. (local $var i31ref) @@ -309,6 +453,15 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-get-set-join (type $6) (param $dest1 anyref) (param $dest2 eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (local.set $dest1 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (local.set $dest2 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $local-get-set-join (param $dest1 anyref) (param $dest2 eqref) ;; This wll be optimized to eqref. (local $var i31ref) @@ -334,6 +487,18 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.get $c) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-get-set-chain (type $1) (result eqref) + ;; ALTER-NEXT: (local $a eqref) + ;; ALTER-NEXT: (local $b eqref) + ;; ALTER-NEXT: (local $c eqref) + ;; ALTER-NEXT: (local.set $b + ;; ALTER-NEXT: (local.get $a) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (local.set $c + ;; ALTER-NEXT: (local.get $b) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (local.get $c) + ;; ALTER-NEXT: ) (func $local-get-set-chain (result eqref) (local $a i31ref) (local $b i31ref) @@ -362,6 +527,18 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: (local.get $c) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-get-set-chain-out-of-order (type $1) (result eqref) + ;; ALTER-NEXT: (local $a eqref) + ;; ALTER-NEXT: (local $b eqref) + ;; ALTER-NEXT: (local $c eqref) + ;; ALTER-NEXT: (local.set $c + ;; ALTER-NEXT: (local.get $b) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (local.set $b + ;; ALTER-NEXT: (local.get $a) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (local.get $c) + ;; ALTER-NEXT: ) (func $local-get-set-chain-out-of-order (result eqref) (local $a i31ref) (local $b i31ref) @@ -391,6 +568,18 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $local-tee (type $2) (param $dest eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.tee $dest + ;; ALTER-NEXT: (local.tee $var + ;; ALTER-NEXT: (ref.i31 + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $local-tee (param $dest eqref) ;; This will be optimized to eqref. (local $var i31ref) @@ -424,6 +613,25 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $i31-get (type $void) + ;; ALTER-NEXT: (local $nullable i31ref) + ;; ALTER-NEXT: (local $nonnullable i31ref) + ;; ALTER-NEXT: (local.set $nonnullable + ;; ALTER-NEXT: (ref.i31 + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (i31.get_s + ;; ALTER-NEXT: (local.get $nullable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (i31.get_u + ;; ALTER-NEXT: (local.get $nonnullable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $i31-get ;; This must stay an i31ref. (local $nullable i31ref) @@ -455,6 +663,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $call (type $2) (param $x eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (call $call + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $call (param $x eqref) ;; This will be optimized to eqref. (local $var i31ref) @@ -471,6 +685,13 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $call_indirect (type $void) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (call_indirect $func-table (type $2) + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $call_indirect ;; This will be optimized to eqref. (local $var i31ref) @@ -491,6 +712,16 @@ ;; CHECK-NEXT: (global.get $global-i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $global-get (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (local $i32 i32) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (global.get $global-eq) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (local.set $i32 + ;; ALTER-NEXT: (global.get $global-i32) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $global-get (local $var eqref) (local $i32 i32) @@ -514,6 +745,16 @@ ;; CHECK-NEXT: (local.get $i32) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $global-set (type $void) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (local $i32 i32) + ;; ALTER-NEXT: (global.set $global-eq + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (global.set $global-i32 + ;; ALTER-NEXT: (local.get $i32) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $global-set (local $var i31ref) (local $i32 i32) @@ -536,6 +777,15 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $select (type $1) (result eqref) + ;; ALTER-NEXT: (local $var1 eqref) + ;; ALTER-NEXT: (local $var2 eqref) + ;; ALTER-NEXT: (select (result eqref) + ;; ALTER-NEXT: (local.get $var1) + ;; ALTER-NEXT: (local.get $var2) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $select (result eqref) ;; Both of these will be generalized to eqref. (local $var1 i31ref) @@ -554,6 +804,12 @@ ;; CHECK-NEXT: (ref.null none) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-null (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (ref.null none) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-null (local $var i31ref) ;; No constraints on $var. @@ -570,6 +826,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-is-null (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (ref.is_null + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-is-null (local $var i31ref) (drop @@ -586,6 +850,12 @@ ;; CHECK-NEXT: (ref.func $ref-func) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-func (type $void) + ;; ALTER-NEXT: (local $var funcref) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (ref.func $ref-func) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-func (local $var (ref null $void)) ;; No constraints on $var. @@ -604,6 +874,16 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-eq (type $void) + ;; ALTER-NEXT: (local $var1 eqref) + ;; ALTER-NEXT: (local $var2 eqref) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (ref.eq + ;; ALTER-NEXT: (local.get $var1) + ;; ALTER-NEXT: (local.get $var2) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-eq (local $var1 i31ref) (local $var2 i31ref) @@ -624,6 +904,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $table-get (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (table.get $eq-table + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $table-get (local $var eqref) ;; No constraints on $var. @@ -641,6 +929,13 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $table-set (type $void) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (table.set $eq-table + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $table-set (local $var i31ref) ;; Require typeof($var) <: eqref. @@ -658,6 +953,14 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $table-fill (type $void) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (table.fill $eq-table + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $table-fill (local $var i31ref) ;; Require typeof($var) <: eqref. @@ -674,6 +977,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-test (type $7) (result i32) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (ref.test nullref + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-test (result i32) (local $var i31ref) ;; No constraint on $var. @@ -690,6 +999,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-cast (type $void) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (ref.cast i31ref + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-cast (local $var i31ref) ;; No constraint on $var. @@ -706,6 +1023,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-cast-limited (type $1) (result eqref) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (ref.cast i31ref + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-cast-limited (result eqref) (local $var i31ref) ;; No constraint on $var. @@ -721,6 +1044,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-cast-more-limited (type $8) (result nullref) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (ref.cast nullref + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-cast-more-limited (result nullref) (local $var i31ref) ;; No constraint on $var. @@ -735,6 +1064,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-cast-lub (type $9) (result structref) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (ref.cast nullref + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-cast-lub (result structref) (local $var i31ref) ;; No constraint on $var. @@ -749,6 +1084,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $ref-as-non-null (type $10) (result (ref eq)) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (ref.as_non_null + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $ref-as-non-null (result (ref eq)) (local $var i31ref) ;; Require that typeof($var) <: eqref. @@ -757,6 +1098,22 @@ ) ) + ;; ALTER: (func $ref-as-non-null-unneeded (type $1) (result eqref) + ;; ALTER-NEXT: (local $var eqref) + ;; ALTER-NEXT: (ref.as_non_null + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + (func $ref-as-non-null-unneeded (result eqref) + ;; XXX waka new test + ;; As the above test, but now the output of the function is nullable, so the + ;; ref.as_non_null is not technically needed. + (local $var i31ref) + (ref.as_non_null + (local.get $var) + ) + ) + ;; CHECK: (func $any-convert-extern-nullable (type $11) (param $x (ref noextern)) (result anyref) ;; CHECK-NEXT: (local $var externref) ;; CHECK-NEXT: (local.set $var @@ -766,6 +1123,15 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $any-convert-extern-nullable (type $11) (param $x (ref noextern)) (result anyref) + ;; ALTER-NEXT: (local $var externref) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (local.get $x) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (extern.internalize + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $any-convert-extern-nullable (param $x (ref noextern)) (result anyref) (local $var (ref noextern)) (local.set $var @@ -786,6 +1152,15 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $any-convert-extern-non-nullable (type $12) (param $x (ref noextern)) (result (ref any)) + ;; ALTER-NEXT: (local $var (ref extern)) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (local.get $x) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (extern.internalize + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $any-convert-extern-non-nullable (param $x (ref noextern)) (result (ref any)) (local $var (ref noextern)) (local.set $var @@ -808,6 +1183,17 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $extern-convert-any-nullable (type $13) (result externref) + ;; ALTER-NEXT: (local $var anyref) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (ref.i31 + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (extern.externalize + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $extern-convert-any-nullable (result externref) (local $var (ref i31)) (local.set $var @@ -832,6 +1218,17 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $extern-convert-any-non-nullable (type $14) (result (ref extern)) + ;; ALTER-NEXT: (local $var (ref any)) + ;; ALTER-NEXT: (local.set $var + ;; ALTER-NEXT: (ref.i31 + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (extern.externalize + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $extern-convert-any-non-nullable (result (ref extern)) (local $var (ref i31)) (local.set $var @@ -848,6 +1245,9 @@ ;; CHECK: (func $helper-any_any (type $15) (param $0 anyref) (param $1 anyref) ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) + ;; ALTER: (func $helper-any_any (type $15) (param $0 anyref) (param $1 anyref) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) (func $helper-any_any (param anyref anyref) (unreachable) ) @@ -855,12 +1255,15 @@ (module ;; CHECK: (type $top (sub (func (param i31ref) (result anyref)))) + ;; ALTER: (type $top (sub (func (param i31ref) (result anyref)))) (type $top (sub (func (param i31ref) (result anyref)))) ;; CHECK: (type $mid (sub $top (func (param eqref) (result anyref)))) + ;; ALTER: (type $mid (sub $top (func (param eqref) (result anyref)))) (type $mid (sub $top (func (param eqref) (result anyref)))) ;; CHECK: (type $2 (func (result eqref))) ;; CHECK: (type $bot (sub $mid (func (param eqref) (result eqref)))) + ;; ALTER: (type $bot (sub $mid (func (param eqref) (result eqref)))) (type $bot (sub $mid (func (param eqref) (result eqref)))) ;; CHECK: (type $4 (func (result anyref))) @@ -873,7 +1276,20 @@ ;; CHECK-NEXT: (local.get $f) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (type $3 (func (result eqref))) + + ;; ALTER: (type $4 (func (result anyref))) + + ;; ALTER: (func $call-ref-params-limited (type $4) (result anyref) + ;; ALTER-NEXT: (local $f (ref null $bot)) + ;; ALTER-NEXT: (local $arg eqref) + ;; ALTER-NEXT: (call_ref $bot + ;; ALTER-NEXT: (local.get $arg) + ;; ALTER-NEXT: (local.get $f) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $call-ref-params-limited (result anyref) + ;; XXX waka different (local $f (ref null $bot)) (local $arg i31ref) ;; Require that typeof($f) <: $mid and that typeof($arg) <: eqref. In @@ -893,6 +1309,14 @@ ;; CHECK-NEXT: (local.get $f) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $call-ref-results-limited (type $3) (result eqref) + ;; ALTER-NEXT: (local $f (ref null $bot)) + ;; ALTER-NEXT: (local $arg eqref) + ;; ALTER-NEXT: (call_ref $bot + ;; ALTER-NEXT: (local.get $arg) + ;; ALTER-NEXT: (local.get $f) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $call-ref-results-limited (result eqref) (local $f (ref null $bot)) (local $arg i31ref) @@ -917,6 +1341,19 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $call-ref-impossible (type $3) (result eqref) + ;; ALTER-NEXT: (local $f nullfuncref) + ;; ALTER-NEXT: (local $arg anyref) + ;; ALTER-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $arg) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $f) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $call-ref-impossible (result eqref) (local $f nullfuncref) (local $arg i31ref) @@ -931,8 +1368,11 @@ (module ;; CHECK: (type $top (sub (func (result anyref)))) + ;; ALTER: (type $top (sub (func (result anyref)))) (type $top (sub (func (result anyref)))) + ;; ALTER: (type $mid (sub $top (func (result eqref)))) (type $mid (sub $top (func (result eqref)))) + ;; ALTER: (type $bot (sub $mid (func (result i31ref)))) (type $bot (sub $mid (func (result i31ref)))) ;; CHECK: (type $1 (func (result anyref))) @@ -943,7 +1383,16 @@ ;; CHECK-NEXT: (local.get $f) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (type $3 (func (result anyref))) + + ;; ALTER: (func $call-ref-no-limit (type $3) (result anyref) + ;; ALTER-NEXT: (local $f (ref null $bot)) + ;; ALTER-NEXT: (call_ref $bot + ;; ALTER-NEXT: (local.get $f) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $call-ref-no-limit (result anyref) + ;; XXX waka different (local $f (ref null $bot)) ;; Require that typeof($f) <: $top because that does not limit us in any way ;; and we cannot possibly do better. @@ -958,15 +1407,23 @@ ;; CHECK: (type $0 (func (result anyref))) ;; CHECK: (type $top (sub (struct (field (mut eqref)) (field eqref)))) + ;; ALTER: (type $0 (func (result anyref))) + + ;; ALTER: (type $top (sub (struct (field (mut eqref)) (field eqref)))) (type $top (sub (struct (field (mut eqref)) (field eqref)))) ;; CHECK: (type $mid (sub $top (struct (field (mut eqref)) (field eqref) (field (mut eqref))))) + ;; ALTER: (type $mid (sub $top (struct (field (mut eqref)) (field eqref) (field (mut eqref))))) (type $mid (sub $top (struct (field (mut eqref)) (field eqref) (field (mut eqref))))) ;; CHECK: (type $3 (func)) ;; CHECK: (type $bot (sub $mid (struct (field (mut eqref)) (field i31ref) (field (mut eqref))))) + ;; ALTER: (type $3 (func)) + + ;; ALTER: (type $bot (sub $mid (struct (field (mut eqref)) (field i31ref) (field (mut eqref))))) (type $bot (sub $mid (struct (field (mut eqref)) (field i31ref) (field (mut eqref))))) ;; CHECK: (type $struct (struct (field eqref) (field anyref))) + ;; ALTER: (type $struct (struct (field eqref) (field anyref))) (type $struct (struct (field eqref) (field anyref))) ;; CHECK: (type $6 (func (result i31ref))) @@ -979,6 +1436,16 @@ ;; CHECK-NEXT: (local.get $var2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (type $6 (func (result i31ref))) + + ;; ALTER: (func $struct-new (type $0) (result anyref) + ;; ALTER-NEXT: (local $var1 eqref) + ;; ALTER-NEXT: (local $var2 anyref) + ;; ALTER-NEXT: (struct.new $struct + ;; ALTER-NEXT: (local.get $var1) + ;; ALTER-NEXT: (local.get $var2) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-new (result anyref) (local $var1 i31ref) (local $var2 i31ref) @@ -995,6 +1462,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-get (type $0) (result anyref) + ;; ALTER-NEXT: (local $var (ref null $top)) + ;; ALTER-NEXT: (struct.get $top 1 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-get (result anyref) (local $var (ref null $bot)) ;; Require that typeof($var) <: (ref null $top) because it has a field of the @@ -1010,6 +1483,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-get-type (type $6) (result i31ref) + ;; ALTER-NEXT: (local $var (ref null $bot)) + ;; ALTER-NEXT: (struct.get $bot 1 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-get-type (result i31ref) (local $var (ref null $bot)) ;; Require that typeof($var) <: (ref null $bot) because further supertypes do @@ -1025,6 +1504,12 @@ ;; CHECK-NEXT: (local.get $var) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-get-index (type $0) (result anyref) + ;; ALTER-NEXT: (local $var (ref null $mid)) + ;; ALTER-NEXT: (struct.get $mid 2 + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-get-index (result anyref) (local $var (ref null $bot)) ;; Require that typeof($var) <: (ref null $mid) because further supertypes do @@ -1043,6 +1528,15 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-get-impossible (type $0) (result anyref) + ;; ALTER-NEXT: (local $var nullref) + ;; ALTER-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $var) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-get-impossible (result anyref) (local $var nullref) (struct.get $bot 0 @@ -1058,6 +1552,14 @@ ;; CHECK-NEXT: (local.get $val) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-set (type $3) + ;; ALTER-NEXT: (local $ref (ref null $top)) + ;; ALTER-NEXT: (local $val eqref) + ;; ALTER-NEXT: (struct.set $top 0 + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-set (local $ref (ref null $bot)) (local $val i31ref) @@ -1078,6 +1580,14 @@ ;; CHECK-NEXT: (local.get $val) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-set-index (type $3) + ;; ALTER-NEXT: (local $ref (ref null $mid)) + ;; ALTER-NEXT: (local $val eqref) + ;; ALTER-NEXT: (struct.set $mid 2 + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-set-index (local $ref (ref null $bot)) (local $val i31ref) @@ -1103,6 +1613,19 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $struct-set-impossible (type $3) + ;; ALTER-NEXT: (local $ref nullref) + ;; ALTER-NEXT: (local $val anyref) + ;; ALTER-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $struct-set-impossible (local $ref nullref) (local $val nullref) @@ -1120,6 +1643,11 @@ ;; CHECK: (type $super-mut (sub (array (mut eqref)))) ;; CHECK: (type $super (sub (array eqref))) + ;; ALTER: (type $0 (func)) + + ;; ALTER: (type $super-mut (sub (array (mut eqref)))) + + ;; ALTER: (type $super (sub (array eqref))) (type $super (sub (array (field eqref)))) ;; CHECK: (type $3 (func (result anyref))) @@ -1128,6 +1656,13 @@ ;; CHECK: (type $bytes (sub (array i8))) ;; CHECK: (type $sub (sub $super (array i31ref))) + ;; ALTER: (type $3 (func (result anyref))) + + ;; ALTER: (type $mut-bytes (sub (array (mut i8)))) + + ;; ALTER: (type $bytes (sub (array i8))) + + ;; ALTER: (type $sub (sub $super (array i31ref))) (type $sub (sub $super (array (field i31ref)))) (type $super-mut (sub (array (field (mut eqref))))) @@ -1142,6 +1677,9 @@ ;; CHECK: (data $data "") ;; CHECK: (elem $elem i31ref) + ;; ALTER: (data $data "") + + ;; ALTER: (elem $elem i31ref) (elem $elem i31ref) (data $data "") @@ -1153,6 +1691,13 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-new (type $3) (result anyref) + ;; ALTER-NEXT: (local $val eqref) + ;; ALTER-NEXT: (array.new $super + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-new (result anyref) (local $val i31ref) ;; Require that typeof($val) <: eqref. @@ -1171,6 +1716,15 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-new-data (type $0) + ;; ALTER-NEXT: (local $val anyref) + ;; ALTER-NEXT: (local.set $val + ;; ALTER-NEXT: (array.new_data $bytes $data + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-new-data (local $val arrayref) ;; No constraint on $val. @@ -1191,6 +1745,15 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-new-elem (type $0) + ;; ALTER-NEXT: (local $val anyref) + ;; ALTER-NEXT: (local.set $val + ;; ALTER-NEXT: (array.new_elem $sub $elem + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-new-elem (local $val arrayref) ;; No constraint on $val. @@ -1210,6 +1773,14 @@ ;; CHECK-NEXT: (local.get $val2) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-new-fixed (type $3) (result anyref) + ;; ALTER-NEXT: (local $val1 eqref) + ;; ALTER-NEXT: (local $val2 eqref) + ;; ALTER-NEXT: (array.new_fixed $super 2 + ;; ALTER-NEXT: (local.get $val1) + ;; ALTER-NEXT: (local.get $val2) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-new-fixed (result anyref) (local $val1 i31ref) (local $val2 i31ref) @@ -1227,6 +1798,13 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-get (type $3) (result anyref) + ;; ALTER-NEXT: (local $val (ref null $super)) + ;; ALTER-NEXT: (array.get $super + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-get (result anyref) (local $val (ref null $sub)) ;; Require that typeof($val) <: (ref null $super). @@ -1248,6 +1826,18 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-get-impossible (type $3) (result anyref) + ;; ALTER-NEXT: (local $val nullref) + ;; ALTER-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-get-impossible (result anyref) (local $val nullref) ;; Require that typeof($val) <: nullref. @@ -1266,6 +1856,15 @@ ;; CHECK-NEXT: (local.get $val) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-set (type $0) + ;; ALTER-NEXT: (local $ref (ref null $super-mut)) + ;; ALTER-NEXT: (local $val eqref) + ;; ALTER-NEXT: (array.set $super-mut + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-set (local $ref (ref null $sub-mut)) (local $val i31ref) @@ -1294,6 +1893,22 @@ ;; CHECK-NEXT: (unreachable) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-set-impossible (type $0) + ;; ALTER-NEXT: (local $ref nullref) + ;; ALTER-NEXT: (local $val anyref) + ;; ALTER-NEXT: (block ;; (replaces something unreachable we can't emit) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: (unreachable) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-set-impossible (local $ref nullref) (local $val i31ref) @@ -1313,6 +1928,14 @@ ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-len (type $0) + ;; ALTER-NEXT: (local $ref arrayref) + ;; ALTER-NEXT: (drop + ;; ALTER-NEXT: (array.len + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-len (local $ref (ref null $super)) (drop @@ -1334,6 +1957,17 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-copy-ref (type $0) + ;; ALTER-NEXT: (local $dest (ref null $super-mut)) + ;; ALTER-NEXT: (local $src (ref null $super)) + ;; ALTER-NEXT: (array.copy $super-mut $super + ;; ALTER-NEXT: (local.get $dest) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $src) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-copy-ref (local $dest (ref null $sub-mut)) (local $src (ref null $sub)) @@ -1358,6 +1992,17 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-copy-i8 (type $0) + ;; ALTER-NEXT: (local $dest (ref null $mut-bytes)) + ;; ALTER-NEXT: (local $src (ref null $bytes)) + ;; ALTER-NEXT: (array.copy $mut-bytes $bytes + ;; ALTER-NEXT: (local.get $dest) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $src) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-copy-i8 ;; Same as above, but now the copied element type is not a ref. (local $dest (ref null $sub-mut-bytes)) @@ -1383,7 +2028,19 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-copy-impossible-dest (type $0) + ;; ALTER-NEXT: (local $dest nullref) + ;; ALTER-NEXT: (local $src (ref null $super)) + ;; ALTER-NEXT: (block + ;; ALTER-NEXT: (local.get $dest) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $src) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-copy-impossible-dest + ;; XXX waka different ;; Same as above, but now the dest is bottom. (local $dest nullref) (local $src (ref null $sub)) @@ -1408,7 +2065,19 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-copy-impossible-src (type $0) + ;; ALTER-NEXT: (local $dest (ref null $super-mut)) + ;; ALTER-NEXT: (local $src nullref) + ;; ALTER-NEXT: (block + ;; ALTER-NEXT: (local.get $dest) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $src) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-copy-impossible-src + ;; XXX waka different ;; Same as above, but now the src is bottom instead. (local $dest (ref null $sub-mut)) (local $src nullref) @@ -1433,6 +2102,17 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-copy-impossible-both (type $0) + ;; ALTER-NEXT: (local $dest nullref) + ;; ALTER-NEXT: (local $src nullref) + ;; ALTER-NEXT: (block + ;; ALTER-NEXT: (local.get $dest) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $src) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-copy-impossible-both ;; Same as above, but now both src and dest are bottom. (local $dest nullref) @@ -1457,6 +2137,16 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-fill (type $0) + ;; ALTER-NEXT: (local $ref (ref null $super-mut)) + ;; ALTER-NEXT: (local $val eqref) + ;; ALTER-NEXT: (array.fill $super-mut + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-fill (local $ref (ref null $sub-mut)) (local $val i31ref) @@ -1480,6 +2170,16 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-fill-impossible (type $0) + ;; ALTER-NEXT: (local $ref nullref) + ;; ALTER-NEXT: (local $val anyref) + ;; ALTER-NEXT: (block + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (local.get $val) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-fill-impossible (local $ref nullref) (local $val i31ref) @@ -1501,6 +2201,15 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-init-data (type $0) + ;; ALTER-NEXT: (local $ref (ref null $mut-bytes)) + ;; ALTER-NEXT: (array.init_data $mut-bytes $data + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-init-data (local $ref (ref null $sub-mut-bytes)) ;; Require that typeof($ref) <: (ref null $mut-bytes). @@ -1521,6 +2230,15 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-init-data-impossible (type $0) + ;; ALTER-NEXT: (local $ref nullref) + ;; ALTER-NEXT: (block + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-init-data-impossible (local $ref nullref) ;; Require that typeof($ref) <: nullref. @@ -1541,6 +2259,15 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-init-elem (type $0) + ;; ALTER-NEXT: (local $ref (ref null $super-mut)) + ;; ALTER-NEXT: (array.init_elem $super-mut $elem + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-init-elem (local $ref (ref null $sub-mut)) ;; Require that typeof($ref) <: (ref null $super-mut). @@ -1561,6 +2288,15 @@ ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) + ;; ALTER: (func $array-init-elem-impossible (type $0) + ;; ALTER-NEXT: (local $ref nullref) + ;; ALTER-NEXT: (block + ;; ALTER-NEXT: (local.get $ref) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: (i32.const 0) + ;; ALTER-NEXT: ) + ;; ALTER-NEXT: ) (func $array-init-elem-impossible (local $ref nullref) ;; Require that typeof($ref) <: nullref.