diff --git a/src/ir/type-updating.cpp b/src/ir/type-updating.cpp index 069ccf4f9ca..fbf164929ca 100644 --- a/src/ir/type-updating.cpp +++ b/src/ir/type-updating.cpp @@ -423,7 +423,14 @@ void GlobalTypeRewriter::mapTypeNamesAndIndices(const TypeMap& oldToNewTypes) { } if (auto it = wasm.typeNames.find(old); it != wasm.typeNames.end()) { auto& names = it->second; - newTypeNames[new_] = names; + auto [newIt, inserted] = newTypeNames.insert({new_, names}); + if (!inserted) { + // Multiple old type names are being merged into the same new type. + // Deterministically keep the lesser name. + if (names.name.view() < newIt->second.name.view()) { + newIt->second = names; + } + } // Use the existing name in the new type, as usually it completely // replaces the old. Rename the old name in a unique way to avoid // confusion in the case that it remains used. @@ -437,9 +444,12 @@ void GlobalTypeRewriter::mapTypeNamesAndIndices(const TypeMap& oldToNewTypes) { } } if (auto it = wasm.typeIndices.find(old); it != wasm.typeIndices.end()) { - // It's ok if we end up with duplicate indices. Ties will be resolved in - // some arbitrary manner. - newTypeIndices[new_] = it->second; + auto [newIt, inserted] = newTypeIndices.insert({new_, it->second}); + if (!inserted) { + // Multiple old type indices are being merged into the same new type. + // Deterministically keep the lesser index. + newIt->second = std::min(newIt->second, it->second); + } } } newTypeNames.merge(wasm.typeNames); diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt index 684077171c8..41ccdef82bd 100644 --- a/src/passes/CMakeLists.txt +++ b/src/passes/CMakeLists.txt @@ -66,6 +66,7 @@ set(passes_SOURCES LocalSubtyping.cpp LogExecution.cpp LoopInvariantCodeMotion.cpp + MakeSharedObjects.cpp MarkJSCalled.cpp Memory64Lowering.cpp MemoryPacking.cpp diff --git a/src/passes/MakeSharedObjects.cpp b/src/passes/MakeSharedObjects.cpp new file mode 100644 index 00000000000..8c82ed92d2d --- /dev/null +++ b/src/passes/MakeSharedObjects.cpp @@ -0,0 +1,401 @@ +/* + * Copyright 2026 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. + */ + +// Make all structs and arrays shared and makes all functions unshared. This +// serves two purposes: 1) converts unshared Wasm GC programs to use the shared +// heap for testing, and 2) lowers shared Wasm GC programs that use shared +// functions so they can run on experimental implementations that do not support +// shared functions. +// +// Because shared structs and arrays cannot contain unshared function +// references, replace function references in structs and arrays with indices +// into a function table that will be duplicated on each thread. Because +// arbitrary unknown function references may be written into structs and arrays +// and there is no way to look up a table index given a function reference, +// function references cannot be replaced only inside structs and arrays. +// Replace all function references in the module with table indices and fix up +// all instructions that consume function references (e.g. call_ref, casts) +// accordingly. Use i31 references to represent the table indices to avoid +// further complications from mapping function references to non-reference +// values. + +#include "ir/drop.h" +#include "ir/module-utils.h" +#include "ir/names.h" +#include "ir/type-updating.h" +#include "ir/utils.h" +#include "literal.h" +#include "pass.h" +#include "support/name.h" +#include "wasm-builder.h" +#include "wasm-features.h" +#include "wasm-traversal.h" +#include "wasm-type.h" +#include "wasm.h" + +#include +#include + +namespace wasm { + +struct MakeSharedObjects + : WalkerPass>> { + Name tableName; + std::vector funcs; + std::unordered_map funcIndices; + + Type funcref = Type(HeapTypes::func, Nullable); + + Name getTable() { + if (!tableName) { + tableName = Names::getValidTableName(*getModule(), "funcs"); + } + return tableName; + } + + Index getIndex(Name func) { + auto [it, inserted] = funcIndices.insert({func, funcs.size()}); + if (inserted) { + funcs.push_back(func); + } + return it->second; + } + + HeapType updatedHeapType(HeapType type) { + if (type.isMaybeShared(HeapType::func) || type.isSignature()) { + return HeapTypes::i31.getBasic(Shared); + } + if (type.isMaybeShared(HeapType::nofunc)) { + return HeapTypes::none.getBasic(Shared); + } + if (type.isBasic()) { + return type.getBasic(Shared); + } + return type; + } + + Type updatedSingleType(Type type) { + if (type.isRef()) { + return type.with(updatedHeapType(type.getHeapType())); + } + return type; + } + + Type updatedType(Type type) { + if (type.isTuple()) { + std::vector elems; + elems.reserve(type.size()); + for (auto t : type) { + elems.push_back(updatedSingleType(t)); + } + return Type(elems); + } + return updatedSingleType(type); + } + + void updateType(Type& type) { type = updatedType(type); } + + void visitRefFunc(RefFunc* curr) { + Builder builder(*getModule()); + replaceCurrent(builder.makeRefI31( + builder.makeConst(Literal(getIndex(curr->func))), Shared)); + } + + std::unordered_map callRefTypes; + + void walkFunction(Function* curr) { + // When we replace call_ref target operands with i31s, we will lose + // information we need about the intended call target type. Collect those + // types up front before we update anything. + struct CallRefCollector : PostWalker { + std::unordered_map& types; + CallRefCollector(std::unordered_map& types) + : types(types) {} + void visitCallRef(CallRef* curr) { + if (curr->target->type.isSignature()) { + types[curr] = curr->target->type.getHeapType(); + } + } + }; + CallRefCollector collector(callRefTypes); + collector.walk(curr->body); + WalkerPass::walkFunction(curr); + ReFinalize().walkFunctionInModule(curr, getModule()); + } + + void visitCallRef(CallRef* curr) { + updateType(curr->type); + Builder builder(*getModule()); + auto it = callRefTypes.find(curr); + if (it == callRefTypes.end()) { + // The target type wasn't a signature, so it must have been null or + // unreachable. + replaceCurrent( + getDroppedChildrenAndAppend(curr, + *getModule(), + getPassOptions(), + builder.makeUnreachable(), + DropMode::IgnoreParentEffects)); + return; + } + auto type = it->second; + callRefTypes.erase(it); + replaceCurrent( + builder.makeCallIndirect(getTable(), + builder.makeI31Get(curr->target, false), + curr->operands, + type, + curr->isReturn)); + } + + void visitCallIndirect(CallIndirect* curr) { + updateType(curr->type); + Builder builder(*getModule()); + Name oldTable = curr->table; + Type oldTableType = updatedType(getModule()->getTable(oldTable)->type); + auto* index = builder.makeTableGet(oldTable, curr->target, oldTableType); + curr->target = builder.makeI31Get(index, false); + curr->table = getTable(); + } + + void visitRefTest(RefTest* curr) { + if (curr->type == Type::unreachable) { + return; + } + auto castHeapType = curr->castType.getHeapType(); + if (castHeapType.isMaybeShared(HeapType::func) || + castHeapType.isMaybeShared(HeapType::nofunc)) { + // This always passes or fails except possibly due to nulls. + updateType(curr->castType); + return; + } + if (!castHeapType.isSignature()) { + return; + } + Builder builder(*getModule()); + if (curr->ref->type.isNonNullable()) { + // (ref.test castType (table.get $t (i31.get_u ref))) + curr->ref = builder.makeTableGet( + getTable(), builder.makeI31Get(curr->ref, false), funcref); + return; + } + // (if (result i32) + // (ref.is_null (local.tee $scratch ref)) + // (then (i32.const 1) OR (i32.const 0)) + // (else (ref.test castType + // (table.get $t (i31.get_u (local.get $scratch)))) + // ) + // ) + auto scratchType = curr->ref->type; + Index scratch = Builder::addVar(getFunction(), scratchType); + auto* cond = builder.makeRefIsNull( + builder.makeLocalTee(scratch, curr->ref, scratchType)); + auto* ifNull = + builder.makeConst(Literal(int32_t(curr->castType.isNullable()))); + curr->ref = builder.makeTableGet( + getTable(), + builder.makeI31Get(builder.makeLocalGet(scratch, scratchType), false), + funcref); + replaceCurrent(builder.makeIf(cond, ifNull, curr, Type::i32)); + } + + void visitRefCast(RefCast* curr) { + if (curr->type == Type::unreachable) { + return; + } + auto castHeapType = curr->type.getHeapType(); + if (castHeapType.isMaybeShared(HeapType::func) || + castHeapType.isMaybeShared(HeapType::nofunc)) { + // This always passes or fails except possibly due to nulls. + updateType(curr->type); + return; + } + if (!castHeapType.isSignature()) { + return; + } + if (getPassOptions().trapsNeverHappen) { + // shared i31 in, shared i31 out, no matter what the cast source and + // target are. + replaceCurrent(curr->ref); + return; + } + // (if (result sharedi31ref) + // (ref.is_null (local.tee $scratch ref)), + // (then (local.get $scratch) OR (unreachable)) + // (else + // (if (result sharedi31ref) + // (ref.test castType + // (table.get $t (i31.get_u (local.get $scratch))) + // (then (ref.as_non_null? (local.get $scratch))) + // (else (unreachable)) + // ) + // ) + // ) + Builder builder(*getModule()); + auto newCastType = updatedType(curr->type); + auto scratchType = curr->ref->type; + Index scratch = Builder::addVar(getFunction(), scratchType); + auto* tee = builder.makeLocalTee(scratch, curr->ref, scratchType); + auto* isNull = builder.makeRefIsNull(tee); + Expression* ifNull = nullptr; + if (curr->type.isNullable()) { + ifNull = builder.makeLocalGet(scratch, scratchType); + } else { + ifNull = builder.makeUnreachable(); + } + auto* getScratch = builder.makeLocalGet(scratch, scratchType); + auto* i31get = builder.makeI31Get(getScratch, false); + auto* tableGet = builder.makeTableGet(getTable(), i31get, funcref); + auto* refTest = builder.makeRefTest(tableGet, curr->type); + Expression* ifPass = builder.makeLocalGet(scratch, scratchType); + if (curr->type.isNonNullable()) { + ifPass = builder.makeRefAs(RefAsNonNull, ifPass); + } + auto* ifFail = builder.makeUnreachable(); + auto* ifNonNull = builder.makeIf(refTest, ifPass, ifFail, newCastType); + replaceCurrent(builder.makeIf(isNull, ifNull, ifNonNull, newCastType)); + } + + void visitBrOn(BrOn* curr) { + if (curr->type == Type::unreachable) { + return; + } + if (curr->op != BrOnCast && curr->op != BrOnCastFail) { + return; + } + auto castHeapType = curr->castType.getHeapType(); + if (castHeapType.isMaybeShared(HeapType::func) || + castHeapType.isMaybeShared(HeapType::nofunc)) { + // This always passes or fails except possibly due to nulls. + updateType(curr->castType); + return; + } + WASM_UNREACHABLE("TODO: br_on"); + } + + void visitExpression(Expression* curr) { + updateType(curr->type); + +#define DELEGATE_ID curr->_id +#define DELEGATE_START(id) [[maybe_unused]] auto* cast = curr->cast(); +#define DELEGATE_GET_FIELD(id, field) cast->field +#define DELEGATE_FIELD_TYPE(id, field) updateType(cast->field); +#define DELEGATE_FIELD_CHILD(id, field) +#define DELEGATE_FIELD_INT(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) +#include "wasm-delegations-fields.def" + } + + void visitFunction(Function* curr) { + for (auto& type : curr->vars) { + updateType(type); + } + } + + void visitTable(Table* curr) { updateType(curr->type); } + + void visitElementSegment(ElementSegment* curr) { updateType(curr->type); } + + void visitGlobal(Global* curr) { updateType(curr->type); } + + void doWalkModule(Module* wasm) { + wasm->features.setSharedEverything(); + WalkerPass::doWalkModule(wasm); + } + + void visitModule(Module* wasm) { + rewriteTypes(); + addFunctionTable(); + } + + void rewriteTypes() { + auto info = ModuleUtils::getOptimizedIndexedHeapTypes(*getModule()); + TypeBuilder builder(info.types.size()); + + auto map = [&](HeapType type) -> HeapType { + if (auto newType = updatedHeapType(type); newType.isBasic()) { + return newType; + } + return builder[info.indices.at(type)]; + }; + + for (Index i = 0; i < info.types.size(); ++i) { + if (info.types[i].getRecGroupIndex() == 0) { + builder.createRecGroup(i, info.types[i].getRecGroup().size()); + } + builder[i].copy(info.types[i], map); + if (info.types[i].isSignature()) { + builder[i].setShared(Unshared); + // `map` will have mapped any supertype to i31, which means the copy + // will not have its supertype set. Fix it. + if (auto super = info.types[i].getDeclaredSuperType()) { + builder[i].subTypeOf(builder[info.indices.at(*super)]); + } + } + builder[i].setShared(info.types[i].isSignature() ? Unshared : Shared); + } + + auto built = builder.build(); + if (auto* err = built.getError()) { + Fatal() << "Failed to build types: " << err->index << ": " << err->reason; + } + + assert(info.types.size() == built->size()); + std::unordered_map oldToNew; + for (Index i = 0; i < info.types.size(); ++i) { + oldToNew[info.types[i]] = (*built)[i]; + } + + GlobalTypeRewriter rewriter(*getModule(), getPassOptions().worldMode); + rewriter.mapTypes(oldToNew); + rewriter.mapTypeNamesAndIndices(oldToNew); + } + + void addFunctionTable() { + if (!tableName) { + return; + } + Builder builder(*getModule()); + Table* table = getModule()->addTable(Builder::makeTable(tableName)); + table->type = funcref; + table->initial = table->max = funcs.size(); + Name segName = Names::getValidElementSegmentName(*getModule(), "funcs"); + auto* offset = builder.makeConst(Literal(int32_t(0))); + auto* segment = getModule()->addElementSegment( + Builder::makeElementSegment(segName, tableName, offset)); + segment->type = funcref; + + segment->data.reserve(funcs.size()); + for (auto func : funcs) { + segment->data.push_back( + builder.makeRefFunc(func, getModule()->getFunction(func)->type)); + } + } + + std::unique_ptr create() override { + return std::make_unique(); + } +}; + +Pass* createMakeSharedObjectsPass() { return new MakeSharedObjects(); } + +} // namespace wasm diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 178b9605cc8..34bc0c7dd40 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -280,6 +280,9 @@ void PassRegistry::registerPasses() { registerPass("limit-segments", "attempt to merge segments to fit within web limits", createLimitSegmentsPass); + registerPass("make-shared-objects", + "Make structs and arrays shared and functions unshared", + createMakeSharedObjectsPass); registerPass("mark-js-called", "mark js called functions (using configureAll) as doing so", createMarkJSCalledPass); diff --git a/src/passes/passes.h b/src/passes/passes.h index 3e289460eec..86e506b44b8 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -88,6 +88,7 @@ Pass* createInstrumentLocalsPass(); Pass* createInstrumentMemoryPass(); Pass* createLLVMMemoryCopyFillLoweringPass(); Pass* createLoopInvariantCodeMotionPass(); +Pass* createMakeSharedObjectsPass(); Pass* createMarkJSCalledPass(); Pass* createMemory64LoweringPass(); Pass* createMemoryPackingPass(); diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index ecb91ac9523..c984af766be 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -175,7 +175,7 @@ struct Walker : public VisitorType { if (segment->isActive()) { walk(segment->offset); } - for (auto* expr : segment->data) { + for (auto*& expr : segment->data) { walk(expr); } static_cast(this)->visitElementSegment(segment); diff --git a/src/wasm-type.h b/src/wasm-type.h index 68f753f00ce..e46088dee07 100644 --- a/src/wasm-type.h +++ b/src/wasm-type.h @@ -808,14 +808,22 @@ struct TypeBuilder { // function. template void copyHeapType(size_t i, HeapType type, F map) { assert(!type.isBasic()); + // Supertypes, descriptor types, and described types cannot be basic heap + // types. Only set them if the mapping takes them to defined types. if (auto super = type.getDeclaredSuperType()) { - setSubType(i, map(*super)); + if (auto mapped = map(*super); !mapped.isBasic()) { + setSubType(i, mapped); + } } if (auto desc = type.getDescriptorType()) { - setDescriptor(i, map(*desc)); + if (auto mapped = map(*desc); !mapped.isBasic()) { + setDescriptor(i, mapped); + } } if (auto desc = type.getDescribedType()) { - setDescribed(i, map(*desc)); + if (auto mapped = map(*desc); !mapped.isBasic()) { + setDescribed(i, mapped); + } } setOpen(i, type.isOpen()); setShared(i, type.getShared()); @@ -825,8 +833,10 @@ struct TypeBuilder { return t; } assert(t.isRef()); - return getTempRefType( - map(t.getHeapType()), t.getNullability(), t.getExactness()); + auto mapped = map(t.getHeapType()); + auto null = t.getNullability(); + auto exact = mapped.isBasic() ? Inexact : t.getExactness(); + return getTempRefType(mapped, null, exact); }; auto copyType = [&](Type t) -> Type { if (t.isTuple()) { diff --git a/test/lit/help/wasm-metadce.test b/test/lit/help/wasm-metadce.test index 43234cf67b4..6f86b33fea5 100644 --- a/test/lit/help/wasm-metadce.test +++ b/test/lit/help/wasm-metadce.test @@ -259,6 +259,9 @@ ;; CHECK-NEXT: --log-execution instrument the build with ;; CHECK-NEXT: logging of where execution goes ;; CHECK-NEXT: +;; CHECK-NEXT: --make-shared-objects Make structs and arrays shared +;; CHECK-NEXT: and functions unshared +;; CHECK-NEXT: ;; CHECK-NEXT: --mark-js-called mark js called functions (using ;; CHECK-NEXT: configureAll) as doing so ;; CHECK-NEXT: diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index 2d712c157c8..763bf958be0 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -295,6 +295,9 @@ ;; CHECK-NEXT: --log-execution instrument the build with ;; CHECK-NEXT: logging of where execution goes ;; CHECK-NEXT: +;; CHECK-NEXT: --make-shared-objects Make structs and arrays shared +;; CHECK-NEXT: and functions unshared +;; CHECK-NEXT: ;; CHECK-NEXT: --mark-js-called mark js called functions (using ;; CHECK-NEXT: configureAll) as doing so ;; CHECK-NEXT: diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test index 168347c4d9c..7ead11eea29 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -223,6 +223,9 @@ ;; CHECK-NEXT: --log-execution instrument the build with ;; CHECK-NEXT: logging of where execution goes ;; CHECK-NEXT: +;; CHECK-NEXT: --make-shared-objects Make structs and arrays shared +;; CHECK-NEXT: and functions unshared +;; CHECK-NEXT: ;; CHECK-NEXT: --mark-js-called mark js called functions (using ;; CHECK-NEXT: configureAll) as doing so ;; CHECK-NEXT: diff --git a/test/lit/passes/dae2.wast b/test/lit/passes/dae2.wast index a74e9709612..18506c88fee 100644 --- a/test/lit/passes/dae2.wast +++ b/test/lit/passes/dae2.wast @@ -1599,7 +1599,7 @@ ;; CHECK: (rec ;; CHECK-NEXT: (type $super (sub (func (param i32 i32)))) (type $super (sub (func (param i32 i32)))) - ;; CHECK: (type $sub2 (sub $super (func (param i32 i32)))) + ;; CHECK: (type $sub1_0 (sub $super (func (param i32 i32)))) ;; CHECK: (type $sub1 (sub $super (func (param i32 i32)))) (type $sub1 (sub $super (func (param i32 i32)))) @@ -1612,7 +1612,7 @@ ;; CHECK: (elem declare func $referenced1 $referenced2) ;; CHECK: (func $referenced1 (type $sub1) (param $0 i32) (param $1 i32) - ;; CHECK-NEXT: (call_ref $sub2 + ;; CHECK-NEXT: (call_ref $sub1_0 ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (ref.func $referenced2) @@ -1634,7 +1634,7 @@ ) ) - ;; CHECK: (func $referenced2 (type $sub2) (param $0 i32) (param $1 i32) + ;; CHECK: (func $referenced2 (type $sub1_0) (param $0 i32) (param $1 i32) ;; CHECK-NEXT: (call_ref $sub1 ;; CHECK-NEXT: (local.get $1) ;; CHECK-NEXT: (i32.const 1) diff --git a/test/lit/passes/make-shared-objects.wast b/test/lit/passes/make-shared-objects.wast new file mode 100644 index 00000000000..fe2ee5ebef5 --- /dev/null +++ b/test/lit/passes/make-shared-objects.wast @@ -0,0 +1,1422 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: foreach %s %t wasm-opt -all --make-shared-objects -S -o - | filecheck %s + +(module + ;; Check lowering of basic heap types. + ;; CHECK: (type $0 (func (param (ref null (shared none))) (result (ref null (shared none))))) + + ;; CHECK: (type $1 (func (param (ref (shared none))) (result (ref (shared none))))) + + ;; CHECK: (type $2 (func (param (ref null (shared i31))) (result (ref null (shared i31))))) + + ;; CHECK: (type $3 (func (param (ref (shared i31))) (result (ref (shared i31))))) + + ;; CHECK: (type $4 (func (param (ref null (shared any))) (result (ref null (shared any))))) + + ;; CHECK: (type $5 (func (param (ref (shared any))) (result (ref (shared any))))) + + ;; CHECK: (type $6 (func (param (ref null (shared eq))) (result (ref null (shared eq))))) + + ;; CHECK: (type $7 (func (param (ref (shared eq))) (result (ref (shared eq))))) + + ;; CHECK: (type $8 (func (param (ref null (shared struct))) (result (ref null (shared struct))))) + + ;; CHECK: (type $9 (func (param (ref (shared struct))) (result (ref (shared struct))))) + + ;; CHECK: (type $10 (func (param (ref null (shared array))) (result (ref null (shared array))))) + + ;; CHECK: (type $11 (func (param (ref (shared array))) (result (ref (shared array))))) + + ;; CHECK: (func $funcref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $funcref (param funcref) (result funcref) + (local funcref) + (local.get 0) + ) + + ;; CHECK: (func $shared-funcref (type $3) (param $0 (ref (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (local $1 (ref (shared i31))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-funcref (param (ref (shared func))) (result (ref (shared func))) + (local (ref (shared func))) + (local.get 0) + ) + + ;; CHECK: (func $nullfuncref (type $0) (param $0 (ref null (shared none))) (result (ref null (shared none))) + ;; CHECK-NEXT: (local $1 (ref null (shared none))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $nullfuncref (param nullfuncref) (result nullfuncref) + (local nullfuncref) + (local.get 0) + ) + + ;; CHECK: (func $shared-nullfuncref (type $1) (param $0 (ref (shared none))) (result (ref (shared none))) + ;; CHECK-NEXT: (local $1 (ref (shared none))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-nullfuncref (param (ref (shared nofunc))) (result (ref (shared nofunc))) + (local (ref (shared nofunc))) + (local.get 0) + ) + + ;; CHECK: (func $anyref (type $4) (param $0 (ref null (shared any))) (result (ref null (shared any))) + ;; CHECK-NEXT: (local $1 (ref null (shared any))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $anyref (param anyref) (result anyref) + (local anyref) + (local.get 0) + ) + + ;; CHECK: (func $shared-anyref (type $5) (param $0 (ref (shared any))) (result (ref (shared any))) + ;; CHECK-NEXT: (local $1 (ref (shared any))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-anyref (param (ref (shared any))) (result (ref (shared any))) + (local (ref (shared any))) + (local.get 0) + ) + + ;; CHECK: (func $eqref (type $6) (param $0 (ref null (shared eq))) (result (ref null (shared eq))) + ;; CHECK-NEXT: (local $1 (ref null (shared eq))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $eqref (param eqref) (result eqref) + (local eqref) + (local.get 0) + ) + + ;; CHECK: (func $shared-eqref (type $7) (param $0 (ref (shared eq))) (result (ref (shared eq))) + ;; CHECK-NEXT: (local $1 (ref (shared eq))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-eqref (param (ref (shared eq))) (result (ref (shared eq))) + (local (ref (shared eq))) + (local.get 0) + ) + + ;; CHECK: (func $structref (type $8) (param $0 (ref null (shared struct))) (result (ref null (shared struct))) + ;; CHECK-NEXT: (local $1 (ref null (shared struct))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $structref (param structref) (result structref) + (local structref) + (local.get 0) + ) + + ;; CHECK: (func $shared-structref (type $9) (param $0 (ref (shared struct))) (result (ref (shared struct))) + ;; CHECK-NEXT: (local $1 (ref (shared struct))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-structref (param (ref (shared struct))) (result (ref (shared struct))) + (local (ref (shared struct))) + (local.get 0) + ) + + ;; CHECK: (func $arrayref (type $10) (param $0 (ref null (shared array))) (result (ref null (shared array))) + ;; CHECK-NEXT: (local $1 (ref null (shared array))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $arrayref (param arrayref) (result arrayref) + (local arrayref) + (local.get 0) + ) + + ;; CHECK: (func $shared-arrayref (type $11) (param $0 (ref (shared array))) (result (ref (shared array))) + ;; CHECK-NEXT: (local $1 (ref (shared array))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-arrayref (param (ref (shared array))) (result (ref (shared array))) + (local (ref (shared array))) + (local.get 0) + ) + + ;; CHECK: (func $nullref (type $0) (param $0 (ref null (shared none))) (result (ref null (shared none))) + ;; CHECK-NEXT: (local $1 (ref null (shared none))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $nullref (param nullref) (result nullref) + (local nullref) + (local.get 0) + ) + + ;; CHECK: (func $shared-nullref (type $1) (param $0 (ref (shared none))) (result (ref (shared none))) + ;; CHECK-NEXT: (local $1 (ref (shared none))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $shared-nullref (param (ref (shared none))) (result (ref (shared none))) + (local (ref (shared none))) + (local.get 0) + ) + + ;; TODO: Handle exnref, contref. +) + +(module + ;; CHECK: (type $func (func (param (ref (shared i31))) (result (ref (shared i31))))) + (type $func (func (param (ref $func)) (result (ref $func)))) + ;; CHECK: (func $f (type $func) (param $0 (ref (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (local $1 (ref (shared i31))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + (func $f (type $func) (param (ref $func)) (result (ref $func)) + (local (ref $func)) + (local.get 0) + ) +) + +(module + ;; Struct and array types should become shared. Function references inside + ;; them should become shared i31refs. Shared struct and array types should + ;; remain shared. Function types should become unshared. Distinct shared and + ;; and unshared types can be merged, but that's ok because their values can + ;; never flow into the same locations. + (type $func (func)) + ;; CHECK: (type $array (shared (array (ref (shared i31))))) + + ;; CHECK: (type $shared-func (func)) + + ;; CHECK: (type $struct (shared (struct (field (ref (shared i31))) (field i32)))) + (type $struct (struct (field (ref $func)) (field i32))) + (type $array (array (ref (exact $func)))) + + (type $shared-func (shared (func))) + ;; CHECK: (type $shared-struct (shared (struct (field (ref null (shared i31)))))) + (type $shared-struct (shared (struct (field (ref null (exact $shared-func)))))) + (type $shared-array (shared (array (ref $shared-func)))) + + ;; CHECK: (type $4 (func (param (ref $struct) (ref $array) (ref $shared-struct) (ref $array)))) + + ;; CHECK: (func $use-types (type $4) (param $0 (ref $struct)) (param $1 (ref $array)) (param $2 (ref $shared-struct)) (param $3 (ref $array)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $use-types (param (ref $struct) (ref $array) (ref $shared-struct) (ref $shared-array)) + (nop) + ) + ;; CHECK: (func $f (type $shared-func) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $f (type $func) + (nop) + ) + ;; CHECK: (func $f-shared (type $shared-func) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $f-shared (type $shared-func) + (nop) + ) +) + +(module + ;; Rec groups containing mutually recursive struct, array, and function types + ;; should be preserved. + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $rec-func (func (param (ref $rec-struct)))) + (type $rec-func (func (param (ref $rec-struct)))) + ;; CHECK: (type $rec-struct (shared (struct (field (ref $rec-array)) (field (ref (shared i31)))))) + (type $rec-struct (struct (field (ref $rec-array)) (field (ref $rec-func)))) + ;; CHECK: (type $rec-array (shared (array (ref $rec-struct)))) + (type $rec-array (array (ref $rec-struct))) + ) + ;; CHECK: (type $3 (func (param (ref $rec-struct) (ref $rec-array) (ref (shared i31))))) + + ;; CHECK: (func $rec-group (type $3) (param $0 (ref $rec-struct)) (param $1 (ref $rec-array)) (param $2 (ref (shared i31))) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $rec-group (param (ref $rec-struct) (ref $rec-array) (ref $rec-func)) + (nop) + ) +) + +(module + ;; Function supertypes are preserved. + ;; CHECK: (type $parent (sub (func))) + (type $parent (sub (shared (func)))) + ;; CHECK: (type $child (sub $parent (func))) + (type $child (sub $parent (shared (func)))) + ;; CHECK: (func $f1 (type $parent) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $f1 (type $parent) + (nop) + ) + ;; CHECK: (func $f2 (type $child) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $f2 (type $child) + (nop) + ) +) + +(module + ;; Struct descriptor and described types become shared correctly. + (rec + ;; CHECK: (rec + ;; CHECK-NEXT: (type $struct (shared (descriptor $desc) (struct (field i32)))) + (type $struct (descriptor $desc) (struct (field i32))) + ;; CHECK: (type $desc (shared (describes $struct) (struct (field i32)))) + (type $desc (describes $struct) (struct (field i32))) + ) + ;; CHECK: (type $2 (func (param (ref $struct) (ref $desc)))) + + ;; CHECK: (func $descriptors (type $2) (param $0 (ref $struct)) (param $1 (ref $desc)) + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: ) + (func $descriptors (param (ref $struct) (ref $desc)) + (nop) + ) +) + +(module + ;; CHECK: (type $f (func)) + (type $f (func)) + ;; CHECK: (type $1 (func (result (ref null (shared none))))) + + ;; CHECK: (type $2 (func (result (ref null (shared i31))))) + + ;; CHECK: (type $3 (func (result (ref (shared i31))))) + + ;; CHECK: (func $target1 (type $f) + ;; CHECK-NEXT: ) + (func $target1 (type $f)) + ;; CHECK: (func $target2 (type $f) + ;; CHECK-NEXT: ) + (func $target2 (type $f)) + + ;; CHECK: (func $ref-null-nofunc (type $1) (result (ref null (shared none))) + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + (func $ref-null-nofunc (result (ref null nofunc)) + ;; (ref.null nofunc) becomes (ref.null (shared none)). + (ref.null nofunc) + ) + + ;; CHECK: (func $ref-null-shared-nofunc (type $1) (result (ref null (shared none))) + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + (func $ref-null-shared-nofunc (result (ref null (shared nofunc))) + ;; Same with a shared nofunc. + (ref.null (shared nofunc)) + ) + + ;; CHECK: (func $ref-func (type $2) (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-func (result funcref) + ;; ref.func becomes a shared i31. + (ref.func $target1) + ) + + ;; CHECK: (func $other-ref-func (type $3) (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $other-ref-func (result (ref (exact $f))) + ;; Different referenced function, different i31 value. + (ref.func $target2) + ) + + ;; CHECK: (func $multiple-ref-func (type $f) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $multiple-ref-func + ;; Mapping of functions to i31 values should be consistent. + (drop (ref.func $target1)) + (drop (ref.func $target1)) + (drop (ref.func $target2)) + ) +) + +(module + (type $sig (func (param i32) (result i32))) + ;; CHECK: (type $0 (func (result i32))) + + ;; CHECK: (type $shared-sig (func (param i32) (result i32))) + (type $shared-sig (shared (func (param i32) (result i32)))) + + ;; CHECK: (type $2 (func)) + + ;; CHECK: (type $struct (shared (struct (field (ref null (shared i31)))))) + (type $struct (struct (field (ref null func)))) + + (func $effect (import "" "") (result i32)) + + ;; CHECK: (type $4 (func (param (ref (shared i31)) i32) (result i32))) + + ;; CHECK: (type $5 (func (param (ref (shared i31))) (result i32))) + + ;; CHECK: (type $6 (func (param i32 i32) (result i32))) + + ;; CHECK: (import "" "" (func $effect (type $0) (result i32))) + + ;; CHECK: (global $g (ref null (shared i31)) (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: )) + (global $g (ref null func) (ref.func $a)) + + ;; CHECK: (global $g-struct (ref null $struct) (struct.new $struct + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: )) + (global $g-struct (ref null $struct) (struct.new $struct (ref.func $b))) + + ;; CHECK: (table $t-init 1 1 (ref null (shared i31)) (ref.i31_shared + ;; CHECK-NEXT: (i32.const 3) + ;; CHECK-NEXT: )) + (table $t-init 1 1 (ref null func) (ref.func $c)) + + ;; CHECK: (table $t 1 1 (ref null (shared i31))) + (table $t 1 1 (ref null $sig)) + ;; CHECK: (table $t-shared 1 1 (ref null (shared i31))) + (table $t-shared 1 1 (ref null (shared func))) + + ;; CHECK: (table $funcs 4 4 funcref) + + ;; CHECK: (elem $elem (ref null (shared i31)) (item (ref.i31_shared + ;; CHECK-NEXT: (i32.const 2) + ;; CHECK-NEXT: ))) + (elem $elem funcref (ref.func $d)) + + ;; CHECK: (elem $funcs (table $funcs) (i32.const 0) func $a $b $d $c) + + ;; CHECK: (func $a (type $2) + ;; CHECK-NEXT: ) + (func $a) + ;; CHECK: (func $b (type $2) + ;; CHECK-NEXT: ) + (func $b) + ;; CHECK: (func $c (type $2) + ;; CHECK-NEXT: ) + (func $c) + ;; CHECK: (func $d (type $2) + ;; CHECK-NEXT: ) + (func $d) + + ;; CHECK: (func $call-ref (type $4) (param $0 (ref (shared i31))) (param $1 i32) (result i32) + ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-ref (param (ref $sig) i32) (result i32) + (call_ref $sig (local.get 1) (local.get 0)) + ) + + ;; CHECK: (func $unreachable-call-ref (type $0) (result i32) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $unreachable-call-ref (result i32) + (call_ref $sig (i32.const 0) (unreachable)) + ) + + ;; CHECK: (func $unreachable-call-ref-effect (type $0) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $effect) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $unreachable-call-ref-effect (result i32) + (call_ref $sig (call $effect) (unreachable)) + ) + + ;; CHECK: (func $null-call-ref (type $0) (result i32) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $null-call-ref (result i32) + (call_ref $sig (i32.const 0) (ref.null nofunc)) + ) + + ;; CHECK: (func $null-call-ref-effect (type $0) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $effect) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $null-call-ref-effect (result i32) + (call_ref $sig (call $effect) (ref.null nofunc)) + ) + + ;; CHECK: (func $shared-call-ref (type $5) (param $0 (ref (shared i31))) (result i32) + ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $shared-call-ref (param (ref $shared-sig)) (result i32) + (call_ref $shared-sig (i32.const 42) (local.get 0)) + ) + + ;; CHECK: (func $shared-unreachable-call-ref (type $0) (result i32) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $shared-unreachable-call-ref (result i32) + (call_ref $shared-sig (i32.const 0) (unreachable)) + ) + + ;; CHECK: (func $shared-unreachable-call-ref-effect (type $0) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $effect) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $shared-unreachable-call-ref-effect (result i32) + (call_ref $shared-sig (call $effect) (unreachable)) + ) + + ;; CHECK: (func $shared-null-call-ref (type $0) (result i32) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $shared-null-call-ref (result i32) + (call_ref $shared-sig (i32.const 0) (ref.null (shared nofunc))) + ) + + ;; CHECK: (func $shared-null-call-ref-effect (type $0) (result i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $effect) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + (func $shared-null-call-ref-effect (result i32) + (call_ref $shared-sig (call $effect) (ref.null (shared nofunc))) + ) + + ;; CHECK: (func $return-call-ref (type $4) (param $0 (ref (shared i31))) (param $1 i32) (result i32) + ;; CHECK-NEXT: (return_call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $return-call-ref (param (ref $sig) i32) (result i32) + (return_call_ref $sig (local.get 1) (local.get 0)) + ) + + ;; call_indirect lowering + ;; CHECK: (func $call-indirect (type $6) (param $0 i32) (param $1 i32) (result i32) + ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (table.get $t + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $call-indirect (param i32 i32) (result i32) + (call_indirect $t (type $sig) (local.get 0) (local.get 1)) + ) + + ;; Unreachable call_indirect lowering. + ;; CHECK: (func $unreachable-call-indirect (type $0) (result i32) + ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (table.get $t + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $unreachable-call-indirect (result i32) + (call_indirect $t (type $sig) (i32.const 0) (unreachable)) + ) + + ;; call_indirect lowering of shared function. + ;; CHECK: (func $shared-call-indirect (type $shared-sig) (param $0 i32) (result i32) + ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (i32.const 42) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (table.get $t-shared + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $shared-call-indirect (param i32) (result i32) + (call_indirect $t-shared (type $shared-sig) (i32.const 42) (local.get 0)) + ) + + ;; unreachable call_indirect + ;; CHECK: (func $unreachable-param-call-indirect (type $shared-sig) (param $0 i32) (result i32) + ;; CHECK-NEXT: (call_indirect $funcs (type $shared-sig) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (table.get $t + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $unreachable-param-call-indirect (param i32) (result i32) + (call_indirect $t (type $sig) (unreachable) (local.get 0)) + ) +) + +(module + (type $sig (func (param i32) (result i32))) + ;; CHECK: (type $shared-sig (func (param i32) (result i32))) + (type $shared-sig (shared (func (param i32) (result i32)))) + ;; CHECK: (type $1 (func (param (ref null (shared i31))) (result i32))) + + ;; CHECK: (type $2 (func (param (ref null (shared i31))) (result (ref null (shared i31))))) + + ;; CHECK: (type $struct (shared (struct (field i32)))) + (type $struct (struct (field i32))) + + ;; ref.test of funcref + ;; CHECK: (type $4 (func (param (ref null (shared i31))) (result (ref (shared i31))))) + + ;; CHECK: (type $5 (func (param (ref (shared i31))) (result i32))) + + ;; CHECK: (type $6 (func (result i32))) + + ;; CHECK: (type $7 (func (param (ref null (shared i31))) (result (ref (shared none))))) + + ;; CHECK: (type $8 (func (param (ref null (shared i31))) (result (ref null (shared none))))) + + ;; CHECK: (type $9 (func (param (ref (shared i31))) (result (ref (shared i31))))) + + ;; CHECK: (type $10 (func (result (ref null (shared i31))))) + + ;; CHECK: (type $11 (func (param (ref null $struct)) (result i32))) + + ;; CHECK: (type $12 (func (param (ref null $struct)) (result (ref null $struct)))) + + ;; CHECK: (table $funcs 0 0 funcref) + + ;; CHECK: (elem $funcs (i32.const 0)) + + ;; CHECK: (func $ref-test-func (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-func (param funcref) (result i32) + (ref.test (ref func) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-funcref (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref null (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-funcref (param funcref) (result i32) + (ref.test (ref null func) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-nofunc (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-nofunc (param funcref) (result i32) + (ref.test (ref nofunc) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-null-nofunc (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref null (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-null-nofunc (param funcref) (result i32) + (ref.test (ref null nofunc) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-func (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-func (param (ref null (shared func))) (result i32) + (ref.test (ref (shared func)) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-funcref (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref null (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-funcref (param (ref null (shared func))) (result i32) + (ref.test (ref null (shared func)) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-nofunc (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-nofunc (param (ref null (shared func))) (result i32) + (ref.test (ref (shared nofunc)) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-null-shared-nofunc (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref null (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-null-shared-nofunc (param (ref null (shared func))) (result i32) + (ref.test (ref null (shared nofunc)) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-no-nulls (type $5) (param $0 (ref (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-no-nulls (param (ref func)) (result i32) + (ref.test (ref $sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-non-nullable (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-non-nullable (param funcref) (result i32) + (ref.test (ref $sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-nullable (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.test (ref null $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-nullable (param funcref) (result i32) + (ref.test (ref null $sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-nullable-exact (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.test (ref null (exact $shared-sig)) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-nullable-exact (param funcref) (result i32) + (ref.test (ref null (exact $sig)) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-no-nulls (type $5) (param $0 (ref (shared i31))) (result i32) + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-no-nulls (param (ref (shared func))) (result i32) + (ref.test (ref $shared-sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-non-nullable (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-non-nullable (param (ref null (shared func))) (result i32) + (ref.test (ref $shared-sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-nullable (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.test (ref null $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-nullable (param (ref null (shared func))) (result i32) + (ref.test (ref null $shared-sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-test-shared-nullable-exact (type $1) (param $0 (ref null (shared i31))) (result i32) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result i32) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.test (ref null (exact $shared-sig)) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-shared-nullable-exact (param (ref null (shared func))) (result i32) + (ref.test (ref null (exact $shared-sig)) (local.get 0)) + ) + + + ;; ref.test with null input + ;; CHECK: (func $ref-test-null-input (type $6) (result i32) + ;; CHECK-NEXT: (ref.test (ref null (shared none)) + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-null-input (result i32) + (ref.test (ref null $sig) (ref.null nofunc)) + ) + + ;; ref.test with unreachable input + ;; CHECK: (func $ref-test-unreachable-input (type $6) (result i32) + ;; CHECK-NEXT: (ref.test (ref null $shared-sig) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-unreachable-input (result i32) + (ref.test (ref null $sig) (unreachable)) + ) + + ;; ref.test of non-function type. + ;; CHECK: (func $ref-test-non-func (type $11) (param $0 (ref null $struct)) (result i32) + ;; CHECK-NEXT: (ref.test (ref null $struct) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-test-non-func (param (ref null $struct)) (result i32) + ;; This should not be changed. + (ref.test (ref null $struct) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-func (type $4) (param $0 (ref null (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.cast (ref (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-func (param funcref) (result (ref func)) + (ref.cast (ref func) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-funcref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.cast (ref null (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-funcref (param funcref) (result (ref null func)) + (ref.cast (ref null func) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-nofunc (type $7) (param $0 (ref null (shared i31))) (result (ref (shared none))) + ;; CHECK-NEXT: (ref.cast (ref (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-nofunc (param funcref) (result (ref nofunc)) + (ref.cast (ref nofunc) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-null-nofunc (type $8) (param $0 (ref null (shared i31))) (result (ref null (shared none))) + ;; CHECK-NEXT: (ref.cast (ref null (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-null-nofunc (param funcref) (result (ref null nofunc)) + (ref.cast (ref null nofunc) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-func (type $4) (param $0 (ref null (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.cast (ref (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-func (param (ref null (shared func))) (result (ref (shared func))) + (ref.cast (ref (shared func)) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-funcref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.cast (ref null (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-funcref (param (ref null (shared func))) (result (ref null (shared func))) + (ref.cast (ref null (shared func)) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-nofunc (type $7) (param $0 (ref null (shared i31))) (result (ref (shared none))) + ;; CHECK-NEXT: (ref.cast (ref (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-nofunc (param (ref null (shared func))) (result (ref (shared nofunc))) + (ref.cast (ref (shared nofunc)) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-null-shared-nofunc (type $8) (param $0 (ref null (shared i31))) (result (ref null (shared none))) + ;; CHECK-NEXT: (ref.cast (ref null (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-null-shared-nofunc (param (ref null (shared func))) (result (ref null (shared nofunc))) + (ref.cast (ref null (shared nofunc)) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-no-nulls (type $9) (param $0 (ref (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (local $1 (ref (shared i31))) + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-no-nulls (param (ref func)) (result (ref $sig)) + (ref.cast (ref $sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-non-nullable (type $4) (param $0 (ref null (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-non-nullable (param funcref) (result (ref $sig)) + (ref.cast (ref $sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-nullable (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.test (ref null $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-nullable (param funcref) (result (ref null $sig)) + (ref.cast (ref null $sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-nullable-exact (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.test (ref null (exact $shared-sig)) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-nullable-exact (param funcref) (result (ref null (exact $sig))) + (ref.cast (ref null (exact $sig)) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-no-nulls (type $9) (param $0 (ref (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (local $1 (ref (shared i31))) + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-no-nulls (param (ref (shared func))) (result (ref $shared-sig)) + (ref.cast (ref $shared-sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-non-nullable (type $4) (param $0 (ref null (shared i31))) (result (ref (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref (shared i31))) + ;; CHECK-NEXT: (ref.test (ref $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.as_non_null + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-non-nullable (param (ref null (shared func))) (result (ref $shared-sig)) + (ref.cast (ref $shared-sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-nullable (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.test (ref null $shared-sig) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-nullable (param (ref null (shared func))) (result (ref null $shared-sig)) + (ref.cast (ref null $shared-sig) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-shared-nullable-exact (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (local $1 (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (if (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.test (ref null (exact $shared-sig)) + ;; CHECK-NEXT: (table.get $funcs + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-shared-nullable-exact (param (ref null (shared func))) (result (ref null $shared-sig)) + (ref.cast (ref null (exact $shared-sig)) (local.get 0)) + ) + + ;; CHECK: (func $ref-cast-null-input (type $10) (result (ref null (shared i31))) + ;; CHECK-NEXT: (ref.cast (ref null (shared none)) + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-null-input (result (ref null $sig)) + (ref.cast (ref null $sig) (ref.null nofunc)) + ) + + ;; CHECK: (func $ref-cast-unreachable-input (type $10) (result (ref null (shared i31))) + ;; CHECK-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-unreachable-input (result (ref null $sig)) + (ref.cast (ref null $sig) (unreachable)) + ) + + ;; CHECK: (func $ref-cast-non-func (type $12) (param $0 (ref null $struct)) (result (ref null $struct)) + ;; CHECK-NEXT: (ref.cast (ref null $struct) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ref-cast-non-func (param (ref null $struct)) (result (ref null $struct)) + ;; This should not be changed. + (ref.cast (ref null $struct) (local.get 0)) + ) + + ;; CHECK: (func $br-on-cast-funcref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (block $label (result (ref null (shared i31))) + ;; CHECK-NEXT: (br_on_cast $label (ref null (shared i31)) (ref null (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-cast-funcref (param funcref) (result funcref) + (block $label (result funcref) + (br_on_cast $label funcref funcref (local.get 0)) + ) + ) + + ;; CHECK: (func $br-on-cast-fail-funcref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (block $label (result (ref null (shared i31))) + ;; CHECK-NEXT: (br_on_cast_fail $label (ref null (shared i31)) (ref null (shared i31)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-cast-fail-funcref (param funcref) (result funcref) + (block $label (result funcref) + (br_on_cast_fail $label funcref funcref (local.get 0)) + ) + ) + + ;; CHECK: (func $br-on-cast-nullfuncref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (block $label (result (ref null (shared i31))) + ;; CHECK-NEXT: (br_on_cast $label (ref null (shared i31)) (ref null (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-cast-nullfuncref (param funcref) (result funcref) + (block $label (result funcref) + (br_on_cast $label funcref nullfuncref (local.get 0)) + ) + ) + + ;; CHECK: (func $br-on-cast-fail-nullfuncref (type $2) (param $0 (ref null (shared i31))) (result (ref null (shared i31))) + ;; CHECK-NEXT: (block $label (result (ref null (shared i31))) + ;; CHECK-NEXT: (br_on_cast_fail $label (ref null (shared i31)) (ref null (shared none)) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $br-on-cast-fail-nullfuncref (param funcref) (result funcref) + (block $label (result funcref) + (br_on_cast_fail $label funcref nullfuncref (local.get 0)) + ) + ) + + ;; TODO: other br_on_cast{_fail} lowering +) + +(module + (type $sig (func (param i32) (result i32))) + + ;; Test that types in if, loop, result + + ;; Test that function types in globals, tables, element segments, and tags are updated. + ;; CHECK: (type $0 (func (param (ref null (shared i31))))) + + ;; CHECK: (type $1 (func (param i32) (result (ref null (shared i31))))) + + ;; CHECK: (global $g (ref null (shared i31)) (ref.null (shared none))) + (global $g (ref null $sig) (ref.null $sig)) + ;; CHECK: (table $t 1 1 (ref null (shared i31)) (ref.null (shared none))) + (table $t 1 1 (ref null $sig) (ref.null $sig)) + ;; CHECK: (elem $e (ref null (shared i31)) (item (ref.null (shared none)))) + (elem $e (ref null $sig) (ref.null $sig)) + ;; CHECK: (tag $tag (type $0) (param (ref null (shared i31)))) + (tag $tag (param (ref null $sig))) + + ;; CHECK: (func $control-types (type $1) (param $0 i32) (result (ref null (shared i31))) + ;; CHECK-NEXT: (if (result (ref null (shared none))) + ;; CHECK-NEXT: (local.get $0) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (ref.null (shared none)) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $control-types (param i32) (result (ref null $sig)) + (if (result (ref null $sig)) (local.get 0) + (then (ref.null $sig)) + (else (ref.null $sig)) + ) + ) +) + +(module + ;; Name collisions with the "funcs" table and "funcs" elements segment are handled correctly. + ;; CHECK: (type $f (func)) + (type $f (func)) + ;; CHECK: (table $funcs 1 1 (ref null (shared i31))) + (table $funcs 1 1 funcref) + ;; CHECK: (table $funcs_1 1 1 funcref) + + ;; CHECK: (elem $funcs (table $funcs) (i32.const 0) (ref null (shared i31)) (item (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ))) + (elem $funcs (table $funcs) (i32.const 0) func $target) + ;; CHECK: (elem $funcs_1 (table $funcs_1) (i32.const 0) func $target) + + ;; CHECK: (func $target (type $f) + ;; CHECK-NEXT: ) + (func $target (type $f)) + + ;; CHECK: (func $name-collision (type $f) + ;; CHECK-NEXT: (call_indirect $funcs_1 (type $f) + ;; CHECK-NEXT: (i31.get_u + ;; CHECK-NEXT: (ref.i31_shared + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $name-collision + (call_ref $f (ref.func $target)) + ) +) + +(module + ;; We want public types to be updated as well. + (type $sig (func)) + ;; CHECK: (import "" "" (global $g-funcref (ref null (shared i31)))) + (import "" "" (global $g-funcref funcref)) + ;; CHECK: (import "" "" (global $g-sig (ref null (shared i31)))) + (import "" "" (global $g-sig (ref null $sig))) +)