diff options
43 files changed, 1542 insertions, 896 deletions
diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index fc3a5b522..e94c58086 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -518,6 +518,9 @@ instructions = [ ("tuple.make", "makeTupleMake(s)"), ("tuple.extract", "makeTupleExtract(s)"), ("pop", "makePop(s)"), + # Typed function references instructions + ("call_ref", "makeCallRef(s, /*isReturn=*/false)"), + ("return_call_ref", "makeCallRef(s, /*isReturn=*/true)"), # GC ("ref.eq", "makeRefEq(s)"), ("i31.new", "makeI31New(s)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index a62d9fdc6..8afcea917 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -99,9 +99,17 @@ switch (op[0]) { case '\0': if (strcmp(op, "call") == 0) { return makeCall(s, /*isReturn=*/false); } goto parse_error; - case '_': - if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/false); } - goto parse_error; + case '_': { + switch (op[5]) { + case 'i': + if (strcmp(op, "call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/false); } + goto parse_error; + case 'r': + if (strcmp(op, "call_ref") == 0) { return makeCallRef(s, /*isReturn=*/false); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } @@ -2747,9 +2755,17 @@ switch (op[0]) { case '\0': if (strcmp(op, "return_call") == 0) { return makeCall(s, /*isReturn=*/true); } goto parse_error; - case '_': - if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); } - goto parse_error; + case '_': { + switch (op[12]) { + case 'i': + if (strcmp(op, "return_call_indirect") == 0) { return makeCallIndirect(s, /*isReturn=*/true); } + goto parse_error; + case 'r': + if (strcmp(op, "return_call_ref") == 0) { return makeCallRef(s, /*isReturn=*/true); } + goto parse_error; + default: goto parse_error; + } + } default: goto parse_error; } } diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp index 448c1f30a..54dde65bc 100644 --- a/src/ir/ReFinalize.cpp +++ b/src/ir/ReFinalize.cpp @@ -150,6 +150,7 @@ void ReFinalize::visitTupleMake(TupleMake* curr) { curr->finalize(); } void ReFinalize::visitTupleExtract(TupleExtract* curr) { curr->finalize(); } void ReFinalize::visitI31New(I31New* curr) { curr->finalize(); } void ReFinalize::visitI31Get(I31Get* curr) { curr->finalize(); } +void ReFinalize::visitCallRef(CallRef* curr) { curr->finalize(); } void ReFinalize::visitRefTest(RefTest* curr) { curr->finalize(); } void ReFinalize::visitRefCast(RefCast* curr) { curr->finalize(); } void ReFinalize::visitBrOnCast(BrOnCast* curr) { diff --git a/src/ir/cost.h b/src/ir/cost.h index c0845f7e2..333f599ee 100644 --- a/src/ir/cost.h +++ b/src/ir/cost.h @@ -65,6 +65,13 @@ struct CostAnalyzer : public OverriddenVisitor<CostAnalyzer, Index> { } return ret; } + Index visitCallRef(CallRef* curr) { + Index ret = 5 + visit(curr->target); + for (auto* child : curr->operands) { + ret += visit(child); + } + return ret; + } Index visitLocalGet(LocalGet* curr) { return 0; } Index visitLocalSet(LocalSet* curr) { return 1 + visit(curr->value); } Index visitGlobalGet(GlobalGet* curr) { return 1; } diff --git a/src/ir/effects.h b/src/ir/effects.h index ab8cafcb1..c0210c221 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -534,6 +534,17 @@ private: void visitTupleExtract(TupleExtract* curr) {} void visitI31New(I31New* curr) {} void visitI31Get(I31Get* curr) {} + void visitCallRef(CallRef* curr) { + parent.calls = true; + if (parent.features.hasExceptionHandling() && parent.tryDepth == 0) { + parent.throws = true; + } + if (curr->isReturn) { + parent.branchesOut = true; + } + // traps when the arg is null + parent.implicitTrap = true; + } void visitRefTest(RefTest* curr) { WASM_UNREACHABLE("TODO (gc): ref.test"); } diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h index c8776297c..2b1c6812c 100644 --- a/src/ir/module-utils.h +++ b/src/ir/module-utils.h @@ -331,10 +331,10 @@ template<typename T> struct CallGraphPropertyAnalysis { void visitCall(Call* curr) { info.callsTo.insert(module->getFunction(curr->target)); } - void visitCallIndirect(CallIndirect* curr) { info.hasNonDirectCall = true; } + void visitCallRef(CallRef* curr) { info.hasNonDirectCall = true; } private: Module* module; diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js index 9abe5e718..bbf2ae237 100644 --- a/src/js/binaryen.js-post.js +++ b/src/js/binaryen.js-post.js @@ -99,6 +99,7 @@ function initializeConstants() { 'Pop', 'I31New', 'I31Get', + 'CallRef', 'RefTest', 'RefCast', 'BrOnCast', diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index 89d03f461..34637cf5a 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -143,6 +143,12 @@ struct DAEScanner } } + void visitCallRef(CallRef* curr) { + if (curr->isReturn) { + info->hasTailCalls = true; + } + } + void visitDrop(Drop* curr) { if (auto* call = curr->value->dynCast<Call>()) { info->droppedCalls[call] = getCurrentPointer(); diff --git a/src/passes/Directize.cpp b/src/passes/Directize.cpp index 0c1132b04..f966d1a5a 100644 --- a/src/passes/Directize.cpp +++ b/src/passes/Directize.cpp @@ -41,6 +41,9 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> { FunctionDirectizer(TableUtils::FlatTable* flatTable) : flatTable(flatTable) {} void visitCallIndirect(CallIndirect* curr) { + if (!flatTable) { + return; + } if (auto* c = curr->target->dynCast<Const>()) { Index index = c->value.geti32(); // If the index is invalid, or the type is wrong, we can @@ -68,6 +71,15 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> { } } + void visitCallRef(CallRef* curr) { + if (auto* ref = curr->target->dynCast<RefFunc>()) { + // We know the target! + replaceCurrent( + Builder(*getModule()) + .makeCall(ref->func, curr->operands, curr->type, curr->isReturn)); + } + } + void doWalkFunction(Function* func) { WalkerPass<PostWalker<FunctionDirectizer>>::doWalkFunction(func); if (changedTypes) { @@ -76,7 +88,9 @@ struct FunctionDirectizer : public WalkerPass<PostWalker<FunctionDirectizer>> { } private: + // If null, then we cannot optimize call_indirects. TableUtils::FlatTable* flatTable; + bool changedTypes = false; void replaceWithUnreachable(CallIndirect* call) { @@ -92,23 +106,31 @@ private: struct Directize : public Pass { void run(PassRunner* runner, Module* module) override { + bool canOptimizeCallIndirect = true; + TableUtils::FlatTable flatTable(module->table); if (!module->table.exists) { - return; - } - if (module->table.imported()) { - return; - } - for (auto& ex : module->exports) { - if (ex->kind == ExternalKind::Table) { - return; + canOptimizeCallIndirect = false; + } else if (module->table.imported()) { + canOptimizeCallIndirect = false; + } else { + for (auto& ex : module->exports) { + if (ex->kind == ExternalKind::Table) { + canOptimizeCallIndirect = false; + } + } + if (!flatTable.valid) { + canOptimizeCallIndirect = false; } } - TableUtils::FlatTable flatTable(module->table); - if (!flatTable.valid) { + // Without typed function references, all we can do is optimize table + // accesses, so if we can't do that, stop. + if (!canOptimizeCallIndirect && + !module->features.hasTypedFunctionReferences()) { return; } // The table exists and is constant, so this is possible. - FunctionDirectizer(&flatTable).run(runner, module); + FunctionDirectizer(canOptimizeCallIndirect ? &flatTable : nullptr) + .run(runner, module); } }; diff --git a/src/passes/Inlining.cpp b/src/passes/Inlining.cpp index bcab7318f..a44f02426 100644 --- a/src/passes/Inlining.cpp +++ b/src/passes/Inlining.cpp @@ -211,6 +211,11 @@ struct Updater : public PostWalker<Updater> { handleReturnCall(curr, curr->sig.results); } } + void visitCallRef(CallRef* curr) { + if (curr->isReturn) { + handleReturnCall(curr, curr->target->type); + } + } void visitLocalGet(LocalGet* curr) { curr->index = localMapping[curr->index]; } diff --git a/src/passes/MergeBlocks.cpp b/src/passes/MergeBlocks.cpp index 4ecec6669..33dbec77c 100644 --- a/src/passes/MergeBlocks.cpp +++ b/src/passes/MergeBlocks.cpp @@ -564,7 +564,7 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { void visitCall(Call* curr) { handleCall(curr); } - void visitCallIndirect(CallIndirect* curr) { + template<typename T> void handleNonDirectCall(T* curr) { FeatureSet features = getModule()->features; Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { @@ -581,6 +581,10 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> { optimize(curr, curr->target, outer); } + void visitCallIndirect(CallIndirect* curr) { handleNonDirectCall(curr); } + + void visitCallRef(CallRef* curr) { handleNonDirectCall(curr); } + void visitThrow(Throw* curr) { Block* outer = nullptr; for (Index i = 0; i < curr->operands.size(); i++) { diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index e512d398f..864a46362 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -87,14 +87,35 @@ struct SigName { }; std::ostream& operator<<(std::ostream& os, SigName sigName) { - auto printType = [&](Type type) { + std::function<void(Type)> printType = [&](Type type) { if (type == Type::none) { os << "none"; } else { auto sep = ""; for (const auto& t : type) { - os << sep << t; + os << sep; sep = "_"; + if (t.isRef()) { + auto heapType = t.getHeapType(); + if (heapType.isSignature()) { + auto sig = heapType.getSignature(); + os << "ref"; + if (t.isNullable()) { + os << "_null"; + } + os << "<"; + for (auto s : sig.params) { + printType(s); + } + os << "_->_"; + for (auto s : sig.results) { + printType(s); + } + os << ">"; + continue; + } + } + os << t; } } }; @@ -1561,6 +1582,13 @@ struct PrintExpressionContents void visitI31Get(I31Get* curr) { printMedium(o, curr->signed_ ? "i31.get_s" : "i31.get_u"); } + void visitCallRef(CallRef* curr) { + if (curr->isReturn) { + printMedium(o, "return_call_ref"); + } else { + printMedium(o, "call_ref"); + } + } void visitRefTest(RefTest* curr) { printMedium(o, "ref.test"); WASM_UNREACHABLE("TODO (gc): ref.test"); @@ -2216,6 +2244,16 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> { printFullLine(curr->i31); decIndent(); } + void visitCallRef(CallRef* curr) { + o << '('; + PrintExpressionContents(currFunction, o).visit(curr); + incIndent(); + for (auto operand : curr->operands) { + printFullLine(operand); + } + printFullLine(curr->target); + decIndent(); + } void visitRefTest(RefTest* curr) { o << '('; PrintExpressionContents(currFunction, o).visit(curr); diff --git a/src/shared-constants.h b/src/shared-constants.h index e3c34e62f..569fd792d 100644 --- a/src/shared-constants.h +++ b/src/shared-constants.h @@ -43,6 +43,8 @@ extern Name GLOBAL; extern Name ELEM; extern Name LOCAL; extern Name TYPE; +extern Name REF; +extern Name NULL_; extern Name CALL; extern Name CALL_IMPORT; extern Name CALL_INDIRECT; diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 1c1359586..39298ce21 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -1081,13 +1081,15 @@ private: WeightedOption{&Self::makeGlobalGet, Important}, WeightedOption{&Self::makeConst, Important}); if (canMakeControlFlow) { - options.add(FeatureSet::MVP, - WeightedOption{&Self::makeBlock, Important}, - WeightedOption{&Self::makeIf, Important}, - WeightedOption{&Self::makeLoop, Important}, - WeightedOption{&Self::makeBreak, Important}, - &Self::makeCall, - &Self::makeCallIndirect); + options + .add(FeatureSet::MVP, + WeightedOption{&Self::makeBlock, Important}, + WeightedOption{&Self::makeIf, Important}, + WeightedOption{&Self::makeLoop, Important}, + WeightedOption{&Self::makeBreak, Important}, + &Self::makeCall, + &Self::makeCallIndirect) + .add(FeatureSet::TypedFunctionReferences, &Self::makeCallRef); } if (type.isSingle()) { options @@ -1146,7 +1148,8 @@ private: &Self::makeNop, &Self::makeGlobalSet) .add(FeatureSet::BulkMemory, &Self::makeBulkMemory) - .add(FeatureSet::Atomics, &Self::makeAtomic); + .add(FeatureSet::Atomics, &Self::makeAtomic) + .add(FeatureSet::TypedFunctionReferences, &Self::makeCallRef); return (this->*pick(options))(Type::none); } @@ -1154,22 +1157,24 @@ private: using Self = TranslateToFuzzReader; auto options = FeatureOptions<Expression* (Self::*)(Type)>(); using WeightedOption = decltype(options)::WeightedOption; - options.add(FeatureSet::MVP, - WeightedOption{&Self::makeLocalSet, VeryImportant}, - WeightedOption{&Self::makeBlock, Important}, - WeightedOption{&Self::makeIf, Important}, - WeightedOption{&Self::makeLoop, Important}, - WeightedOption{&Self::makeBreak, Important}, - WeightedOption{&Self::makeStore, Important}, - WeightedOption{&Self::makeUnary, Important}, - WeightedOption{&Self::makeBinary, Important}, - WeightedOption{&Self::makeUnreachable, Important}, - &Self::makeCall, - &Self::makeCallIndirect, - &Self::makeSelect, - &Self::makeSwitch, - &Self::makeDrop, - &Self::makeReturn); + options + .add(FeatureSet::MVP, + WeightedOption{&Self::makeLocalSet, VeryImportant}, + WeightedOption{&Self::makeBlock, Important}, + WeightedOption{&Self::makeIf, Important}, + WeightedOption{&Self::makeLoop, Important}, + WeightedOption{&Self::makeBreak, Important}, + WeightedOption{&Self::makeStore, Important}, + WeightedOption{&Self::makeUnary, Important}, + WeightedOption{&Self::makeBinary, Important}, + WeightedOption{&Self::makeUnreachable, Important}, + &Self::makeCall, + &Self::makeCallIndirect, + &Self::makeSelect, + &Self::makeSwitch, + &Self::makeDrop, + &Self::makeReturn) + .add(FeatureSet::TypedFunctionReferences, &Self::makeCallRef); return (this->*pick(options))(Type::unreachable); } @@ -1443,6 +1448,10 @@ private: return builder.makeCallIndirect(target, args, targetFn->sig, isReturn); } + Expression* makeCallRef(Type type) { + return makeTrivial(type); // FIXME + } + Expression* makeLocalGet(Type type) { auto& locals = funcContext->typeLocals[type]; if (locals.empty()) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 0918151c5..b0f41e69c 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -972,6 +972,11 @@ enum ASTNodes { Rethrow = 0x09, BrOnExn = 0x0a, + // typed function references opcodes + + CallRef = 0x14, + RetCallRef = 0x15, + // gc opcodes RefEq = 0xd5, @@ -1479,6 +1484,7 @@ public: void visitThrow(Throw* curr); void visitRethrow(Rethrow* curr); void visitBrOnExn(BrOnExn* curr); + void visitCallRef(CallRef* curr); void throwError(std::string text); diff --git a/src/wasm-builder.h b/src/wasm-builder.h index 6800aa2ed..50a6e97fb 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -257,6 +257,19 @@ public: call->finalize(); return call; } + template<typename T> + CallRef* makeCallRef(Expression* target, + const T& args, + Type type, + bool isReturn = false) { + auto* call = wasm.allocator.alloc<CallRef>(); + call->type = type; + call->target = target; + call->operands.set(args); + call->isReturn = isReturn; + call->finalize(); + return call; + } LocalGet* makeLocalGet(Index index, Type type) { auto* ret = wasm.allocator.alloc<LocalGet>(); ret->index = index; diff --git a/src/wasm-delegations-fields.h b/src/wasm-delegations-fields.h index 7f6e43d75..ca0a8f7cb 100644 --- a/src/wasm-delegations-fields.h +++ b/src/wasm-delegations-fields.h @@ -549,6 +549,14 @@ switch (DELEGATE_ID) { DELEGATE_END(I31Get); break; } + case Expression::Id::CallRefId: { + DELEGATE_START(CallRef); + DELEGATE_FIELD_CHILD(CallRef, target); + DELEGATE_FIELD_CHILD_VECTOR(CallRef, operands); + DELEGATE_FIELD_INT(CallRef, isReturn); + DELEGATE_END(CallRef); + break; + } case Expression::Id::RefTestId: { DELEGATE_START(RefTest); WASM_UNREACHABLE("TODO (gc): ref.test"); diff --git a/src/wasm-delegations.h b/src/wasm-delegations.h index 7212cbee9..50ee8247b 100644 --- a/src/wasm-delegations.h +++ b/src/wasm-delegations.h @@ -66,6 +66,7 @@ DELEGATE(TupleMake); DELEGATE(TupleExtract); DELEGATE(I31New); DELEGATE(I31Get); +DELEGATE(CallRef); DELEGATE(RefTest); DELEGATE(RefCast); DELEGATE(BrOnCast); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 406938a56..37719d4d9 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1272,6 +1272,7 @@ public: WASM_UNREACHABLE("unimp"); } Flow visitPop(Pop* curr) { WASM_UNREACHABLE("unimp"); } + Flow visitCallRef(CallRef* curr) { WASM_UNREACHABLE("unimp"); } Flow visitRefNull(RefNull* curr) { NOTE_ENTER("RefNull"); return Literal::makeNull(curr->type); @@ -1593,11 +1594,14 @@ public: } return Flow(NONCONSTANT_FLOW); } - Flow visitCallIndirect(CallIndirect* curr) { NOTE_ENTER("CallIndirect"); return Flow(NONCONSTANT_FLOW); } + Flow visitCallRef(CallRef* curr) { + NOTE_ENTER("CallRef"); + return Flow(NONCONSTANT_FLOW); + } Flow visitLoad(Load* curr) { NOTE_ENTER("Load"); return Flow(NONCONSTANT_FLOW); @@ -2095,6 +2099,34 @@ private: } return ret; } + Flow visitCallRef(CallRef* curr) { + NOTE_ENTER("CallRef"); + LiteralList arguments; + Flow flow = this->generateArguments(curr->operands, arguments); + if (flow.breaking()) { + return flow; + } + Flow target = this->visit(curr->target); + if (target.breaking()) { + return target; + } + Name funcName = target.getSingleValue().getFunc(); + auto* func = instance.wasm.getFunction(funcName); + Flow ret; + if (func->imported()) { + ret.values = instance.externalInterface->callImport(func, arguments); + } else { + ret.values = instance.callFunctionInternal(funcName, arguments); + } +#ifdef WASM_INTERPRETER_DEBUG + std::cout << "(returned to " << scope.function->name << ")\n"; +#endif + // TODO: make this a proper tail call (return first) + if (curr->isReturn) { + ret.breakTo = RETURN_FLOW; + } + return ret; + } Flow visitLocalGet(LocalGet* curr) { NOTE_ENTER("LocalGet"); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 085b58ba0..9a501171d 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -77,6 +77,11 @@ public: Element* setString(cashew::IString str__, bool dollared__, bool quoted__); Element* setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_); + // comparisons + bool operator==(Name name) { return isStr() && str() == name; } + + template<typename T> bool operator!=(T t) { return !(*this == t); } + // printing friend std::ostream& operator<<(std::ostream& o, Element& e); void dump(); @@ -144,6 +149,7 @@ private: UniqueNameMapper nameMapper; + // Given a function signature type's name, return the signature Signature getFunctionSignature(Element& s); Name getFunctionName(Element& s); Name getGlobalName(Element& s); @@ -246,6 +252,7 @@ private: Expression* makeBrOnExn(Element& s); Expression* makeTupleMake(Element& s); Expression* makeTupleExtract(Element& s); + Expression* makeCallRef(Element& s, bool isReturn); Expression* makeI31New(Element& s); Expression* makeI31Get(Element& s, bool signed_); Expression* makeRefTest(Element& s); @@ -288,6 +295,7 @@ private: void parseTable(Element& s, bool preParseImport = false); void parseElem(Element& s); void parseInnerElem(Element& s, Index i = 1, Expression* offset = nullptr); + Signature parseInlineFunctionSignature(Element& s); void parseType(Element& s); void parseEvent(Element& s, bool preParseImport = false); diff --git a/src/wasm.h b/src/wasm.h index e9fb4461b..6367fa345 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -588,6 +588,7 @@ public: TupleExtractId, I31NewId, I31GetId, + CallRefId, RefTestId, RefCastId, BrOnCastId, @@ -1294,6 +1295,17 @@ public: void finalize(); }; +class CallRef : public SpecificExpression<Expression::CallRefId> { +public: + CallRef(MixedArena& allocator) : operands(allocator) {} + ExpressionList operands; + Expression* target; + bool isReturn = false; + + void finalize(); + void finalize(Type type_); +}; + class RefTest : public SpecificExpression<Expression::RefTestId> { public: RefTest(MixedArena& allocator) {} diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index a96039bc2..20b0899a5 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -2760,6 +2760,16 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { visitMemoryGrow(grow); break; } + case BinaryConsts::CallRef: + visitCallRef((curr = allocator.alloc<CallRef>())->cast<CallRef>()); + break; + case BinaryConsts::RetCallRef: { + auto call = allocator.alloc<CallRef>(); + call->isReturn = true; + curr = call; + visitCallRef(call); + break; + } case BinaryConsts::AtomicPrefix: { code = static_cast<uint8_t>(getU32LEB()); if (maybeVisitLoad(curr, code, /*isAtomic=*/true)) { @@ -5426,6 +5436,26 @@ void WasmBinaryBuilder::visitBrOnExn(BrOnExn* curr) { curr->finalize(); } +void WasmBinaryBuilder::visitCallRef(CallRef* curr) { + BYN_TRACE("zz node: CallRef\n"); + curr->target = popNonVoidExpression(); + auto type = curr->target->type; + if (!type.isRef()) { + throwError("Non-ref type for a call_ref: " + type.toString()); + } + auto heapType = type.getHeapType(); + if (!heapType.isSignature()) { + throwError("Invalid reference type for a call_ref: " + type.toString()); + } + auto sig = heapType.getSignature(); + auto num = sig.params.size(); + curr->operands.resize(num); + for (size_t i = 0; i < num; i++) { + curr->operands[num - i - 1] = popNonVoidExpression(); + } + curr->finalize(sig.results); +} + bool WasmBinaryBuilder::maybeVisitI31New(Expression*& out, uint32_t code) { if (code != BinaryConsts::I31New) { return false; diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp index 6286ae090..d8d9fa779 100644 --- a/src/wasm/wasm-s-parser.cpp +++ b/src/wasm/wasm-s-parser.cpp @@ -539,11 +539,11 @@ SExpressionWasmBuilder::parseParamOrLocal(Element& s, size_t& localIndex) { if (s[i]->isStr()) { type = stringToType(s[i]->str()); } else { - if (elementStartsWith(s, PARAM)) { + type = elementToType(*s[i]); + if (elementStartsWith(s, PARAM) && type.isTuple()) { throw ParseException( "params may not have tuple types", s[i]->line, s[i]->col); } - type = elementToType(*s[i]); } namedParams.emplace_back(name, type); } @@ -925,10 +925,48 @@ Type SExpressionWasmBuilder::elementToType(Element& s) { if (s.isStr()) { return stringToType(s.str(), false, false); } - auto& tuple = s.list(); + auto& list = s.list(); + auto size = list.size(); + if (size > 0 && elementStartsWith(s, REF)) { + // It's a reference. It should be in the form + // (ref $name) + // or + // (ref null $name) + // and also $name can be the expanded structure of the type and not a name, + // so something like (ref (func (result i32))), etc. + if (size != 2 && size != 3) { + throw ParseException( + std::string("invalid reference type size"), s.line, s.col); + } + if (size == 3 && *list[1] != NULL_) { + throw ParseException( + std::string("invalid reference type qualifier"), s.line, s.col); + } + bool nullable = false; + size_t i = 1; + if (size == 3) { + nullable = true; + i++; + } + Signature sig; + auto& last = *s[i]; + if (last.isStr()) { + // A string name of a signature. + sig = getFunctionSignature(last); + } else { + // A signature written out in full in-line. + if (*last[0] != FUNC) { + throw ParseException( + std::string("invalid reference type type"), s.line, s.col); + } + sig = parseInlineFunctionSignature(last); + } + return Type(HeapType(sig), nullable); + } + // It's a tuple. std::vector<Type> types; for (size_t i = 0; i < s.size(); ++i) { - types.push_back(stringToType(tuple[i]->str())); + types.push_back(stringToType(list[i]->str())); } return Type(types); } @@ -2026,6 +2064,24 @@ Expression* SExpressionWasmBuilder::makeTupleExtract(Element& s) { return ret; } +Expression* SExpressionWasmBuilder::makeCallRef(Element& s, bool isReturn) { + auto ret = allocator.alloc<CallRef>(); + parseCallOperands(s, 1, s.size() - 1, ret); + ret->target = parseExpression(s[s.size() - 1]); + ret->isReturn = isReturn; + if (!ret->target->type.isRef()) { + throw ParseException("Non-reference type for a call_ref", s.line, s.col); + } + auto heapType = ret->target->type.getHeapType(); + if (!heapType.isSignature()) { + throw ParseException( + "Invalid reference type for a call_ref", s.line, s.col); + } + auto sig = heapType.getSignature(); + ret->finalize(sig.results); + return ret; +} + Expression* SExpressionWasmBuilder::makeI31New(Element& s) { auto ret = allocator.alloc<I31New>(); ret->value = parseExpression(s[1]); @@ -2710,9 +2766,26 @@ void SExpressionWasmBuilder::parseInnerElem(Element& s, wasm.table.segments.push_back(segment); } -void SExpressionWasmBuilder::parseType(Element& s) { +Signature SExpressionWasmBuilder::parseInlineFunctionSignature(Element& s) { + if (*s[0] != FUNC) { + throw ParseException("invalid inline function signature", s.line, s.col); + } std::vector<Type> params; std::vector<Type> results; + for (size_t k = 1; k < s.size(); k++) { + Element& curr = *s[k]; + if (elementStartsWith(curr, PARAM)) { + auto newParams = parseParamOrLocal(curr); + params.insert(params.end(), newParams.begin(), newParams.end()); + } else if (elementStartsWith(curr, RESULT)) { + auto newResults = parseResults(curr); + results.insert(results.end(), newResults.begin(), newResults.end()); + } + } + return Signature(Type(params), Type(results)); +} + +void SExpressionWasmBuilder::parseType(Element& s) { size_t i = 1; if (s[i]->isStr()) { std::string name = s[i]->str().str; @@ -2722,18 +2795,7 @@ void SExpressionWasmBuilder::parseType(Element& s) { signatureIndices[name] = signatures.size(); i++; } - Element& func = *s[i]; - for (size_t k = 1; k < func.size(); k++) { - Element& curr = *func[k]; - if (elementStartsWith(curr, PARAM)) { - auto newParams = parseParamOrLocal(curr); - params.insert(params.end(), newParams.begin(), newParams.end()); - } else if (elementStartsWith(curr, RESULT)) { - auto newResults = parseResults(curr); - results.insert(results.end(), newResults.begin(), newResults.end()); - } - } - signatures.emplace_back(Type(params), Type(results)); + signatures.emplace_back(parseInlineFunctionSignature(*s[i])); } void SExpressionWasmBuilder::parseEvent(Element& s, bool preParseImport) { diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 021b05cb6..a6a4ce171 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -1875,6 +1875,11 @@ void BinaryInstWriter::visitI31Get(I31Get* curr) { << U32LEB(curr->signed_ ? BinaryConsts::I31GetS : BinaryConsts::I31GetU); } +void BinaryInstWriter::visitCallRef(CallRef* curr) { + o << int8_t(curr->isReturn ? BinaryConsts::RetCallRef + : BinaryConsts::CallRef); +} + void BinaryInstWriter::visitRefTest(RefTest* curr) { o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefTest); WASM_UNREACHABLE("TODO (gc): ref.test"); diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp index cf4404739..7de274e9c 100644 --- a/src/wasm/wasm-type.cpp +++ b/src/wasm/wasm-type.cpp @@ -394,6 +394,9 @@ bool Type::operator<(const Type& other) const { return false; } // Both are compound. + if (a.isNullable() != b.isNullable()) { + return a.isNullable(); + } auto aHeap = a.getHeapType(); auto bHeap = b.getHeapType(); if (aHeap.isSignature() && bHeap.isSignature()) { diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 78e123a90..5faa8b2f5 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -339,6 +339,7 @@ public: void visitBrOnExn(BrOnExn* curr); void visitTupleMake(TupleMake* curr); void visitTupleExtract(TupleExtract* curr); + void visitCallRef(CallRef* curr); void visitI31New(I31New* curr); void visitI31Get(I31Get* curr); void visitRefTest(RefTest* curr); @@ -406,6 +407,49 @@ private: size_t align, Type type, Index bytes, bool isAtomic, Expression* curr); void validateMemBytes(uint8_t bytes, Type type, Expression* curr); + template<typename T> void validateReturnCall(T* curr) { + shouldBeTrue(!curr->isReturn || getModule()->features.hasTailCall(), + curr, + "return_call* requires tail calls to be enabled"); + } + + template<typename T> + void validateCallParamsAndResult(T* curr, Signature sig) { + if (!shouldBeTrue(curr->operands.size() == sig.params.size(), + curr, + "call* param number must match")) { + return; + } + size_t i = 0; + for (const auto& param : sig.params) { + if (!shouldBeSubTypeOrFirstIsUnreachable(curr->operands[i]->type, + param, + curr, + "call param types must match") && + !info.quiet) { + getStream() << "(on argument " << i << ")\n"; + } + ++i; + } + if (curr->isReturn) { + shouldBeEqual(curr->type, + Type(Type::unreachable), + curr, + "return_call* should have unreachable type"); + shouldBeEqual( + getFunction()->sig.results, + sig.results, + curr, + "return_call* callee return type must match caller return type"); + } else { + shouldBeEqualOrFirstIsUnreachable( + curr->type, + sig.results, + curr, + "call* type must match callee return type"); + } + } + Type indexType() { return getModule()->memory.indexType; } }; @@ -748,9 +792,7 @@ void FunctionValidator::visitSwitch(Switch* curr) { } void FunctionValidator::visitCall(Call* curr) { - shouldBeTrue(!curr->isReturn || getModule()->features.hasTailCall(), - curr, - "return_call requires tail calls to be enabled"); + validateReturnCall(curr); if (!info.validateGlobally) { return; } @@ -758,104 +800,16 @@ void FunctionValidator::visitCall(Call* curr) { if (!shouldBeTrue(!!target, curr, "call target must exist")) { return; } - if (!shouldBeTrue(curr->operands.size() == target->sig.params.size(), - curr, - "call param number must match")) { - return; - } - size_t i = 0; - for (const auto& param : target->sig.params) { - if (!shouldBeSubTypeOrFirstIsUnreachable(curr->operands[i]->type, - param, - curr, - "call param types must match") && - !info.quiet) { - getStream() << "(on argument " << i << ")\n"; - } - ++i; - } - if (curr->isReturn) { - shouldBeEqual(curr->type, - Type(Type::unreachable), - curr, - "return_call should have unreachable type"); - shouldBeEqual( - getFunction()->sig.results, - target->sig.results, - curr, - "return_call callee return type must match caller return type"); - } else { - if (curr->type == Type::unreachable) { - bool hasUnreachableOperand = std::any_of( - curr->operands.begin(), curr->operands.end(), [](Expression* op) { - return op->type == Type::unreachable; - }); - shouldBeTrue( - hasUnreachableOperand, - curr, - "calls may only be unreachable if they have unreachable operands"); - } else { - shouldBeEqual(curr->type, - target->sig.results, - curr, - "call type must match callee return type"); - } - } + validateCallParamsAndResult(curr, target->sig); } void FunctionValidator::visitCallIndirect(CallIndirect* curr) { - shouldBeTrue(!curr->isReturn || getModule()->features.hasTailCall(), - curr, - "return_call_indirect requires tail calls to be enabled"); + validateReturnCall(curr); shouldBeEqualOrFirstIsUnreachable(curr->target->type, Type(Type::i32), curr, "indirect call target must be an i32"); - if (!shouldBeTrue(curr->operands.size() == curr->sig.params.size(), - curr, - "call param number must match")) { - return; - } - size_t i = 0; - for (const auto& param : curr->sig.params) { - if (!shouldBeSubTypeOrFirstIsUnreachable(curr->operands[i]->type, - param, - curr, - "call param types must match") && - !info.quiet) { - getStream() << "(on argument " << i << ")\n"; - } - ++i; - } - if (curr->isReturn) { - shouldBeEqual(curr->type, - Type(Type::unreachable), - curr, - "return_call_indirect should have unreachable type"); - shouldBeEqual( - getFunction()->sig.results, - curr->sig.results, - curr, - "return_call_indirect callee return type must match caller return type"); - } else { - if (curr->type == Type::unreachable) { - if (curr->target->type != Type::unreachable) { - bool hasUnreachableOperand = std::any_of( - curr->operands.begin(), curr->operands.end(), [](Expression* op) { - return op->type == Type::unreachable; - }); - shouldBeTrue(hasUnreachableOperand, - curr, - "call_indirects may only be unreachable if they have " - "unreachable operands"); - } - } else { - shouldBeEqual(curr->type, - curr->sig.results, - curr, - "call_indirect type must match callee return type"); - } - } + validateCallParamsAndResult(curr, curr->sig); } void FunctionValidator::visitConst(Const* curr) { @@ -2199,6 +2153,20 @@ void FunctionValidator::visitTupleExtract(TupleExtract* curr) { } } +void FunctionValidator::visitCallRef(CallRef* curr) { + validateReturnCall(curr); + shouldBeTrue(getModule()->features.hasTypedFunctionReferences(), + curr, + "call_ref requires typed-function-references to be enabled"); + shouldBeTrue(curr->target->type.isFunction(), + curr, + "call_ref target must be a function reference"); + if (curr->target->type != Type::unreachable) { + validateCallParamsAndResult( + curr, curr->target->type.getHeapType().getSignature()); + } +} + void FunctionValidator::visitI31New(I31New* curr) { shouldBeTrue( getModule()->features.hasGC(), curr, "i31.new requires gc to be enabled"); diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 6245a3575..ac76a63ac 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -73,6 +73,8 @@ Name TABLE("table"); Name ELEM("elem"); Name LOCAL("local"); Name TYPE("type"); +Name REF("ref"); +Name NULL_("null"); Name CALL("call"); Name CALL_INDIRECT("call_indirect"); Name BLOCK("block"); @@ -212,6 +214,8 @@ const char* getExpressionName(Expression* curr) { return "i31.new"; case Expression::Id::I31GetId: return "i31.get"; + case Expression::Id::CallRefId: + return "call_ref"; case Expression::Id::RefTestId: return "ref.test"; case Expression::Id::RefCastId: @@ -1060,6 +1064,21 @@ void I31Get::finalize() { } } +void CallRef::finalize() { + handleUnreachableOperands(this); + if (isReturn) { + type = Type::unreachable; + } + if (target->type == Type::unreachable) { + type = Type::unreachable; + } +} + +void CallRef::finalize(Type type_) { + type = type_; + finalize(); +} + // TODO (gc): ref.test // TODO (gc): ref.cast // TODO (gc): br_on_cast diff --git a/src/wasm2js.h b/src/wasm2js.h index 7a0f4692d..ddc3d7313 100644 --- a/src/wasm2js.h +++ b/src/wasm2js.h @@ -2198,6 +2198,10 @@ Ref Wasm2JSBuilder::processFunctionBody(Module* m, unimplemented(curr); WASM_UNREACHABLE("unimp"); } + Ref visitCallRef(CallRef* curr) { + unimplemented(curr); + WASM_UNREACHABLE("unimp"); + } Ref visitRefTest(RefTest* curr) { unimplemented(curr); WASM_UNREACHABLE("unimp"); diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js index 703e8cb5a..831bdd3d8 100644 --- a/test/binaryen.js/kitchen-sink.js +++ b/test/binaryen.js/kitchen-sink.js @@ -169,6 +169,7 @@ function test_ids() { console.log("TupleExtractId: " + binaryen.TupleExtractId); console.log("I31NewId: " + binaryen.I31NewId); console.log("I31GetId: " + binaryen.I31GetId); + console.log("CallRefId: " + binaryen.CallRefId); console.log("RefTestId: " + binaryen.RefTestId); console.log("RefCastId: " + binaryen.RefCastId); console.log("BrOnCastId: " + binaryen.BrOnCastId); diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 980a6d8c1..464862159 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -93,18 +93,19 @@ TupleMakeId: 49 TupleExtractId: 50 I31NewId: 51 I31GetId: 52 -RefTestId: 53 -RefCastId: 54 -BrOnCastId: 55 -RttCanonId: 56 -RttSubId: 57 -StructNewId: 58 -StructGetId: 59 -StructSetId: 60 -ArrayNewId: 61 -ArrayGetId: 62 -ArraySetId: 63 -ArrayLenId: 64 +CallRefId: 53 +RefTestId: 54 +RefCastId: 55 +BrOnCastId: 56 +RttCanonId: 57 +RttSubId: 58 +StructNewId: 59 +StructGetId: 60 +StructSetId: 61 +ArrayNewId: 62 +ArrayGetId: 63 +ArraySetId: 64 +ArrayLenId: 65 getExpressionInfo={"id":15,"type":4,"op":6} (f32.neg (f32.const -33.61199951171875) diff --git a/test/extra-unreachable.wast b/test/extra-unreachable.wast index 799665924..80725c92e 100644 --- a/test/extra-unreachable.wast +++ b/test/extra-unreachable.wast @@ -1,5 +1,5 @@ (module - (type $ii (param i32) (result i32)) + (type $ii (func (param i32) (result i32))) (memory (shared 1 1)) (table 0 funcref) (global $g (mut f32) (f32.const 0)) @@ -86,7 +86,7 @@ (call $foo (unreachable)) ) (global.set $g - (call_indirect (type $ii) (unreachable)) + (call_indirect (type $ii) (unreachable) (unreachable)) ) ;; unary diff --git a/test/extra-unreachable.wast.from-wast b/test/extra-unreachable.wast.from-wast index e084548f8..08aa4ed4b 100644 --- a/test/extra-unreachable.wast.from-wast +++ b/test/extra-unreachable.wast.from-wast @@ -1,7 +1,7 @@ (module (type $none_=>_none (func)) - (type $i32_=>_none (func (param i32))) (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_=>_none (func (param i32))) (memory $0 (shared 1 1)) (table $0 0 funcref) (global $g (mut f32) (f32.const 0)) @@ -64,7 +64,8 @@ ) ) (global.set $g - (call_indirect (type $none_=>_none) + (call_indirect (type $i32_=>_i32) + (unreachable) (unreachable) ) ) diff --git a/test/passes/directize_enable-tail-call.txt b/test/passes/directize_all-features.txt index 97956bee5..4b6934f06 100644 --- a/test/passes/directize_enable-tail-call.txt +++ b/test/passes/directize_all-features.txt @@ -209,3 +209,15 @@ ) ) ) +(module + (type $i32_i32_=>_none (func (param i32 i32))) + (func $foo (param $0 i32) (param $1 i32) + (unreachable) + ) + (func $bar (param $x i32) (param $y i32) + (call $foo + (local.get $x) + (local.get $y) + ) + ) +) diff --git a/test/passes/directize_enable-tail-call.wast b/test/passes/directize_all-features.wast index adebca6b6..b113af396 100644 --- a/test/passes/directize_enable-tail-call.wast +++ b/test/passes/directize_all-features.wast @@ -207,3 +207,17 @@ ) ) ) +;; call_ref +(module + (func $foo (param i32) (param i32) + (unreachable) + ) + (func $bar (param $x i32) (param $y i32) + (call_ref + (local.get $x) + (local.get $y) + (ref.func $foo) + ) + ) +) + diff --git a/test/passes/instrument-locals_all-features.txt b/test/passes/instrument-locals_all-features_disable-typed-function-references.txt index d027f54a0..d027f54a0 100644 --- a/test/passes/instrument-locals_all-features.txt +++ b/test/passes/instrument-locals_all-features_disable-typed-function-references.txt diff --git a/test/passes/instrument-locals_all-features.wast b/test/passes/instrument-locals_all-features_disable-typed-function-references.wast index c709630b5..c709630b5 100644 --- a/test/passes/instrument-locals_all-features.wast +++ b/test/passes/instrument-locals_all-features_disable-typed-function-references.wast diff --git a/test/passes/translate-to-fuzz_all-features.txt b/test/passes/translate-to-fuzz_all-features.txt index 055f80e00..17d913b4b 100644 --- a/test/passes/translate-to-fuzz_all-features.txt +++ b/test/passes/translate-to-fuzz_all-features.txt @@ -1,4 +1,5 @@ (module + (type $none_=>_i32_v128_f64_v128 (func (result i32 v128 f64 v128))) (type $none_=>_none (func)) (type $i32_=>_none (func (param i32))) (type $i64_=>_none (func (param i64))) @@ -35,7 +36,7 @@ (global.get $hangLimit) ) (return - (i32.const 235407412) + (i32.const 64) ) ) (global.set $hangLimit @@ -66,30 +67,48 @@ ) ) ) - (f32.store offset=1 align=1 - (i32.and - (i32.const -127) - (i32.const 15) - ) - (f32.const -8) - ) - (if - (i32.eqz - (ref.is_null - (ref.null exn) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (i32.const 303045650) + (i32.const 5) + ) + (i32.shl + (ref.eq + (i31.new + (i32.const -65535) + ) + (tuple.extract 2 + (tuple.make + (f64.const 274877906944) + (i31.new + (i32.const -134217728) + ) + (ref.null eq) + ) + ) + ) + (i32.const 5) + ) + ) + (i32.load8_u offset=2 + (i32.const 0) ) ) - (block $label$1 - (nop) - (br_if $label$1 - (loop $label$2 (result i32) + ) + (nop) + (local.set $0 + (i32.xor + (i32.add + (loop $label$1 (result i32) (block (if (i32.eqz (global.get $hangLimit) ) (return - (i32.const 980647737) + (i32.const 4878) ) ) (global.set $hangLimit @@ -99,125 +118,85 @@ ) ) ) - (block $label$3 (result i32) - (i32.const -1) - ) - ) - ) - ) - (block $label$4 - (block $label$5 - (nop) - (nop) - ) - (nop) - ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl - (local.get $0) - (i32.const 5) - ) - (i32.const 175316752) - ) - (i32.load8_u offset=4 - (i32.const 0) - ) - ) - ) - (local.tee $0 - (i32.xor - (i32.add - (if - (i32.eqz - (i32.const 128) - ) - (block $label$6 - (i32.store8 offset=4 - (i32.and - (loop $label$7 (result i32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const -2147483647) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) + (block $label$2 (result i32) + (drop + (f64.const 3402823466385288598117041e14) + ) + (loop $label$6 + (block + (if + (i32.eqz + (global.get $hangLimit) ) - (block $label$8 (result i32) - (loop $label$9 - (block - (if - (i32.eqz - (global.get $hangLimit) + (return + (i32.const -13) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (block $label$7 + (nop) + (if + (block $label$8 + (nop) + (br $label$1) + ) + (block $label$9 + (f64.store offset=2 align=2 + (i32.and + (i32.const 118163717) + (i32.const 15) + ) + (if (result f64) + (call $hashMemory) + (block $label$10 (result f64) + (memory.copy + (i32.const -129) + (i32.and + (i32.const 1195786829) + (i32.const 15) + ) + (i32.const -8) + ) + (f64.const -562949953421312.2) ) - (return - (i32.const 262143) + (block $label$11 + (nop) + (br $label$7) ) ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + ) + (if + (i32.eqz + (i32.atomic.load + (i32.and + (i32.const 65535) + (i32.const 15) + ) ) ) - ) - (block - (block $label$10 + (block $label$12 (nop) (nop) ) - (br_if $label$9 - (block - (i32.wrap_i64 - (i64.trunc_f32_u - (block $label$11 - (br_if $label$9 - (i32.eqz - (br_if $label$8 - (select - (i32.const 1162693199) - (i32.const 5909) - (i32.const 17989) - ) - (i32.const 236914958) - ) - ) - ) - (br $label$7) - ) - ) - ) - (drop - (i32.eqz - (i32.const 1548964443) - ) - ) - ) - ) - (v128.store offset=22 - (i32.and - (i32.const 307712331) - (i32.const 15) + (block $label$13 + (br_if $label$7 + (i32.const 10539) ) - (loop $label$12 (result v128) + (loop $label$14 (block (if (i32.eqz (global.get $hangLimit) ) (return - (i32.const -13) + (i32.const 1495078923) ) ) (global.set $hangLimit @@ -227,182 +206,25 @@ ) ) ) - (block (result v128) - (br_if $label$7 - (i32.eqz - (i32.const 24) - ) - ) - (br_if $label$12 - (i32.const -118) - ) - (i8x16.gt_u - (i16x8.splat - (loop $label$13 (result i32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const -14) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result i32) - (nop) - (br_if $label$13 - (i32.eqz - (i32.trunc_f32_u - (f32.const 1099511627776) - ) - ) - ) - (f32.le - (f32.const 4398046511104) - (f32.const 320406528) - ) - ) - ) - ) - (loop $label$14 (result v128) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 386596886) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block $label$15 (result v128) - (br_if $label$12 - (f32.eq - (f32.const -2147483648) - (f32.const 555950656) - ) - ) - (v128.const i32x4 0x80000000 0xffffff81 0x00008000 0x6e026f15) - ) - ) - ) - ) + (atomic.fence) ) ) ) ) - (i32.const 0) + (nop) ) ) - (i32.const 15) - ) - (i31.get_s - (block $label$16 (result i31ref) - (nop) - (i31.new - (i32.const -256) - ) + (br_if $label$6 + (i32.const -50) ) - ) - ) - (return - (i32.const -9) - ) - ) - (block $label$17 - (return - (i32.const 2376257) - ) - ) - ) - (local.get $0) - ) - (i32.load8_u offset=5 - (i32.const 0) - ) - ) - ) - (local.set $0 - (tuple.extract 0 - (tuple.make - (i32.const 262145) - (i32.const 110) - ) - ) - ) - (local.set $0 - (call $hashMemory) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl - (local.get $0) - (i32.const 5) - ) - (local.get $0) - ) - (i32.load8_u offset=6 - (i32.const 0) - ) - ) - ) - (local.set $0 - (i32.xor - (i32.add - (i32.shl - (local.get $0) - (i32.const 5) - ) - (i32.xor - (i32.add - (i32.shl - (local.get $0) - (i32.const 5) - ) - (local.get $0) - ) - (i32.load8_u offset=8 - (i32.const 0) - ) - ) - ) - (i32.load8_u offset=9 - (i32.const 0) - ) - ) - ) - (block $label$23 - (if - (if (result i32) - (i32.eqz - (i32.const 218235656) - ) - (block $label$24 - (global.set $global$_3 - (f64.add - (f64.mul - (loop $label$25 (result f64) + (loop $label$15 (block (if (i32.eqz (global.get $hangLimit) ) (return - (i32.const 56235029) + (i32.const -96) ) ) (global.set $hangLimit @@ -412,271 +234,360 @@ ) ) ) - (block (result f64) - (br_if $label$23 - (block $label$26 - (br $label$23) - ) - ) - (br_if $label$25 - (i31.get_s - (i31.new - (i32.const -256) - ) - ) - ) - (global.get $global$_3) - ) - ) - (f64.const 0.878) - ) - (block $label$27 - (nop) - (br $label$23) - ) - ) - ) - (br $label$23) - ) - (select - (call $hashMemory) - (i32.const -2147483647) - (i32.const 5909) - ) - ) - (nop) - (block $label$32 - (loop $label$33 - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 15) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block $label$34 - (br_if $label$23 - (i32.const 16911115) - ) - (block $label$35 - (atomic.fence) - (atomic.fence) - ) - ) - ) - (atomic.fence) - (atomic.fence) - ) - ) - (atomic.fence) - ) - (local.set $0 - (i32.xor - (memory.atomic.notify offset=4 - (loop $label$36 (result i32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 65512) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result i32) - (if - (ref.is_null - (block $label$37 - (nop) - (br $label$36) - ) - ) - (nop) - (nop) - ) - (br_if $label$36 - (i32.eqz - (block $label$38 (result i32) - (loop $label$39 - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 67108864) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (block $label$16 + (if + (i32.eqz + (br_if $label$2 + (i8x16.extract_lane_s 2 + (f64x2.gt + (v128.const i32x4 0xffffffc0 0xffffffff 0x60631d62 0x630f6218) + (if (result v128) + (i32.eqz + (f32.ne + (block $label$18 + (nop) + (br $label$15) + ) + (f32.const -nan:0x7fff8a) + ) + ) + (block $label$19 (result v128) + (nop) + (v128.const i32x4 0x00000000 0x38100000 0x00000000 0x40d89880) + ) + (block $label$20 + (if + (i32.atomic.rmw8.cmpxchg_u offset=4 + (i32.and + (i32.const 2) + (i32.const 15) + ) + (i32.const 65525) + (br_if $label$2 + (i32.const -33554432) + (i64.lt_u + (i64.const -65536) + (i64.const 6663) + ) + ) + ) + (block $label$21 + (br_if $label$21 + (i32.eqz + (i32.const 65535) + ) + ) + (nop) + ) + (block $label$22 + (v128.store offset=4 + (i32.and + (i32.const 55) + (i32.const 15) + ) + (v128.const i32x4 0xfffffe00 0x7fffffff 0x00005b34 0x00000039) + ) + (nop) + ) + ) + (br $label$16) + ) + ) + ) + ) + (i32.eqz + (i32.const 85459227) + ) ) ) - ) - (block - (br_if $label$39 - (i32.eqz - (i32.const -58) + (block $label$23 + (block $label$24 + (block $label$25 + (nop) + (nop) + ) + (nop) ) + (nop) ) - (br_if $label$39 - (loop $label$40 - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 29) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) + (block $label$26 + (if + (br_if $label$2 + (i32.const 1091126348) + (i32.const 1276841216) ) - (block $label$41 - (if - (i32.const 65534) - (block $label$42 - (nop) - (br_if $label$39 - (i32.eqz - (i32.const -61) + (br_if $label$26 + (i32.eqz + (tuple.extract 0 + (if (result i32 v128 f64 v128) + (i32.load8_u offset=3 + (i32.and + (f64.ge + (block $label$27 (result f64) + (nop) + (f64.const 41) + ) + (f64x2.extract_lane 0 + (v128.const i32x4 0xffffc000 0x00000007 0x00000001 0x473f2147) + ) + ) + (i32.const 15) + ) + ) + (block $label$28 (result i32 v128 f64 v128) + (tuple.make + (i32.const 67308125) + (v128.const i32x4 0xff00011d 0x0601e545 0x7f0e14ff 0x00b1f843) + (f64.const 1314148947) + (v128.const i32x4 0x0000003a 0x42d00000 0x00000000 0x40280000) + ) + ) + (block $label$29 (result i32 v128 f64 v128) + (br_if $label$15 + (i32.eqz + (br_if $label$2 + (i32.const -8192) + (i32.eqz + (i32.const -2) + ) + ) + ) + ) + (tuple.make + (i32.const 65534) + (v128.const i32x4 0x41530000 0x008045b2 0x01ff7f00 0xff2d8349) + (f64.const 1797693134862315708145274e284) + (v128.const i32x4 0xffbc630d 0xffb6ffbb 0x00015658 0xffb4ffff) + ) ) - ) - ) - (block $label$43 - (nop) - (drop - (ref.null exn) ) ) ) - (br $label$40) ) - ) - ) - (f32.store offset=3 align=2 - (i32.and - (select - (i32.const 42) - (loop $label$44 (result i32) - (block - (if - (i32.eqz - (global.get $hangLimit) + (block $label$30 + (if + (loop $label$31 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 1578633994) + ) ) - (return - (i32.const -9) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) ) ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) + (block $label$32 (result i32) + (block $label$33 + (if + (i32.eqz + (i32.atomic.rmw.and offset=4 + (i32.const -2147483648) + (block $label$34 + (loop $label$35 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 403574365) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (nop) + (br_if $label$35 + (i32.const -65536) + ) + (nop) + ) + ) + (br $label$1) + ) + ) + ) + (block $label$36 + (nop) + (br_if $label$16 + (i32.const 2136) + ) + ) + (block $label$37 + (nop) + (nop) + ) + ) + (atomic.fence) + ) + (f32.lt + (f32.const 2147483648) + (f32.const 2147483648) ) ) ) - (i16x8.extract_lane_s 2 - (v128.const i32x4 0x05030804 0x1e070503 0x80000001 0xffffffff) - ) - ) - (memory.atomic.notify offset=22 - (i32.and - (i32.atomic.load offset=22 + (block $label$40 + (i32.atomic.store16 offset=2 (i32.and - (i32.const 1499338050) + (i31.get_s + (loop $label$41 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const -2) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$42 + (loop $label$43 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const -13) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$44 + (nop) + (nop) + ) + ) + (block $label$45 + (nop) + (br $label$16) + ) + ) + ) + ) (i32.const 15) ) + (block $label$46 + (nop) + (ref.is_null + (if + (i32.eqz + (if (result i32) + (i32.eqz + (block $label$47 + (nop) + (br $label$16) + ) + ) + (i32.const -4194304) + (i32.const 5140) + ) + ) + (block $label$48 + (block $label$49 + (nop) + (br_if $label$15 + (i32.eqz + (loop $label$50 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 112) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (nop) + (br_if $label$50 + (i32.eqz + (i32.const 3) + ) + ) + (i31.get_s + (i31.new + (i32.const 4095) + ) + ) + ) + ) + ) + ) + ) + (br $label$40) + ) + (block $label$51 + (drop + (ref.null exn) + ) + (br $label$16) + ) + ) + ) + ) ) - (i32.const 15) ) - (i32.const -2) - ) - ) - (i32.const 15) - ) - (f32.const -9223372036854775808) - ) - ) - ) - (i32.const -2147483648) - ) - ) - ) - (select - (i32.const -127) - (i32.const 8) - (i32.const -255) - ) - ) - ) - (i32.const 608321884) - ) - (i32.and - (i32.const 72) - (i64.eqz - (loop $label$45 (result i64) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 2131838335) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result i64) - (block $label$46 - (nop) - ) - (br_if $label$45 - (i32.eqz - (block $label$47 - (br_if $label$45 - (i32.eqz - (i32.rem_s - (if (result i32) - (i32.rem_u - (i32.atomic.load16_u offset=2 - (i32.and - (if (result i32) - (i32.eqz - (loop $label$48 (result i32) + (block $label$52 + (loop $label$53 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 7506) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block + (nop) + (br_if $label$53 + (loop $label$54 (result i32) (block (if (i32.eqz (global.get $hangLimit) ) (return - (i32.const 55645307) + (i32.const 50922251) ) ) (global.set $hangLimit @@ -686,38 +597,115 @@ ) ) ) - (block (result i32) - (br_if $label$48 - (i32.eqz - (i32.const 4374) + (i32.const 6912) + ) + ) + (br_if $label$6 + (br_if $label$2 + (call $hashMemory) + (loop $label$55 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 319820056) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) ) ) - (br_if $label$48 - (i32.const 791226157) + (i32.const 2147483647) + ) + ) + ) + ) + ) + (memory.copy + (i32.and + (i32.atomic.load offset=3 + (i32.and + (loop $label$56 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const -2147483648) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) ) - (if (result i32) - (i32.eqz - (i32.const 270077278) + (loop $label$57 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 262143) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (block $label$58 + (if + (i32.eqz + (i32.const 775321902) + ) + (nop) + (if + (i32.eqz + (i32.const -18) + ) + (nop) + (nop) + ) + ) + (nop) + ) + (br_if $label$57 + (i32.const 4194304) + ) + (f32.le + (f32.const 18446744073709551615) + (f32.const -nan:0x7fff87) + ) ) - (i32.const -1) - (i32.const 96) ) ) + (i32.const 15) ) ) - (block $label$49 - (nop) - (br $label$45) - ) - (block $label$50 (result i32) - (loop $label$51 + (i32.const 15) + ) + (i32.and + (block $label$59 (result i32) + (loop $label$60 (block (if (i32.eqz (global.get $hangLimit) ) (return - (i32.const 44) + (i32.const -32768) ) ) (global.set $hangLimit @@ -728,94 +716,277 @@ ) ) (block - (nop) - (br_if $label$51 + (block $label$61 + (drop + (i31.new + (i32.const -8192) + ) + ) + (nop) + ) + (br_if $label$60 (i32.eqz - (i32.const 65507) + (loop $label$62 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 7) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block $label$63 (result i32) + (nop) + (i32.const 65521) + ) + ) ) ) - (br_if $label$51 - (i32.const 1413679622) + (memory.fill + (i32.trunc_f32_u + (f32.const 256) + ) + (i64.eq + (i64.const 256) + (i64.const -16384) + ) + (i32.const 387455770) ) ) ) - (f64.eq - (f64.const -4095.596) - (f64.const -54) + (i32.const 4627) + ) + (i32.const 15) + ) + (i32.load8_s offset=3 + (i32.and + (br_if $label$2 + (loop $label$64 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const -96) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (block $label$65 + (loop $label$66 + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 65518) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (nop) + ) + (nop) + ) + (br_if $label$64 + (tuple.extract 1 + (tuple.make + (v128.const i32x4 0x0606ff82 0xfffe0000 0x04052000 0xfffe0000) + (i32.const 1529028702) + ) + ) + ) + (loop $label$67 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const -1073741825) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (i32.const 128) + ) + ) + ) + (i32.eqz + (ref.is_null + (select (result anyref) + (ref.null eq) + (ref.null extern) + (i32.const 129) + ) + ) + ) ) + (i32.const 15) ) ) - (i32.const 15) ) ) - (i32.const 262143) ) - (i8x16.extract_lane_u 7 - (select - (global.get $global$_1) - (i32x4.lt_u - (i32x4.gt_u - (v128.const i32x4 0xffff0002 0x0000ffa0 0x000000ff 0xff800000) - (v128.const i32x4 0x00000001 0xffffffb7 0x585f5c5d 0xe0000001) - ) - (global.get $global$_1) - ) - (i31.get_s - (i31.new - (i32.const 1431912225) - ) - ) - ) + (nop) + ) + ) + (block $label$68 + (nop) + (nop) + ) + ) + ) + (nop) + ) + ) + ) + ) + (if (result i32) + (block $label$69 (result i32) + (if + (if (result i32) + (br_if $label$2 + (loop $label$71 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) ) - (block $label$52 - (if + (return + (i32.const 15) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (block (result i32) + (block $label$72 + (br_if $label$72 + (i32.const -1) + ) + (nop) + ) + (br_if $label$71 + (i32.eqz + (i32.const -2147483648) + ) + ) + (if (result i32) + (i32.eqz + (i32.const -32766) + ) + (block $label$73 (result i32) + (br_if $label$71 (i32.eqz - (memory.atomic.notify offset=4 - (i8x16.extract_lane_s 6 - (v128.const i32x4 0x0000040a 0x00000000 0x51080d5a 0x05040f53) - ) - (i32.atomic.rmw8.cmpxchg_u offset=22 - (i32.and - (if (result i32) - (i32.const 35) - (block $label$53 - (br $label$45) - ) - (i32.const 2147483647) - ) - (i32.const 15) - ) - (i32.const 47) - (i32.const 118818079) + (tuple.extract 1 + (block $label$74 + (nop) + (br $label$1) ) ) ) - (block $label$54 - (drop - (ref.null exn) - ) - (nop) - ) - (block $label$55 - (data.drop 0) - (nop) - ) ) - (br $label$45) + (ref.is_null + (global.get $global$_0) + ) + ) + (i32x4.all_true + (v128.const i32x4 0x3c800c01 0x193fff27 0x52a20001 0x970000ff) + ) + ) + ) + ) + (i32.eqz + (loop $label$70 (result i32) + (block + (if + (i32.eqz + (global.get $hangLimit) + ) + (return + (i32.const 1048576) + ) + ) + (global.set $hangLimit + (i32.sub + (global.get $hangLimit) + (i32.const 1) + ) + ) + ) + (i32.const 256) + ) + ) + ) + (i32.atomic.load8_u offset=22 + (i32.const 858607703) + ) + (block $label$75 (result i32) + (nop) + (br_if $label$75 + (i32.const 7) + (i31.get_s + (tuple.extract 0 + (tuple.make + (i31.new + (i32.const 219815443) + ) + (v128.const i32x4 0x00000001 0x151b1e13 0x00000001 0x1a5f0000) ) ) - (call $hashMemory) ) ) ) - (br $label$45) ) + (block $label$76 + (nop) + ) + (nop) ) + (i32.const 2097152) + ) + (i32.const -84) + (block $label$77 (result i32) + (i32.const -16) ) - (i64.const 5064094673136993862) ) ) ) + (local.get $0) + ) + (i32.load8_u offset=4 + (i32.const 0) ) ) ) @@ -828,148 +999,137 @@ ) (local.get $0) ) - (i32.shl + (i32.load8_u offset=5 + (i32.const 0) + ) + ) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) - (i32.const 5) ) + (i32.const 6506) ) ) (nop) (local.set $0 (i32.xor (i32.add - (i32.const 5) + (i32.shl + (local.get $0) + (i32.const 5) + ) (local.get $0) ) - (i32.load8_u offset=14 + (i32.load8_u offset=6 (i32.const 0) ) ) ) - (block $label$56 - (drop - (memory.atomic.notify offset=2 - (i32.and - (f64.ne - (f64.const -1797693134862315708145274e284) - (select - (global.get $global$_3) - (if (result f64) - (i32.eqz - (i32.const -2147483647) - ) - (f64.trunc - (f64.div - (if (result f64) - (i32.eqz - (i32.atomic.rmw8.xor_u offset=22 - (i32.const 488975911) - (select - (i32.const -16383) - (if (result i32) - (i32.eqz - (loop $label$57 (result i32) - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const 386995985) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block (result i32) - (nop) - (br_if $label$57 - (i32.const 453384208) - ) - (i32.const -2147483648) - ) - ) - ) - (i32.const 7753) - (block $label$58 - (nop) - (br $label$56) - ) - ) - (i32.const 0) - ) - ) - ) - (block $label$59 (result f64) - (nop) - (global.get $global$_3) - ) - (block $label$60 - (nop) - (br $label$56) - ) - ) - (tuple.extract 1 - (loop $label$61 - (block - (if - (i32.eqz - (global.get $hangLimit) - ) - (return - (i32.const -8) - ) - ) - (global.set $hangLimit - (i32.sub - (global.get $hangLimit) - (i32.const 1) - ) - ) - ) - (block $label$62 - (br $label$56) - ) - ) - ) - ) - ) - (block $label$63 (result f64) - (nop) - (f64.load offset=4 - (i32.and - (call $hashMemory) - (i32.const 15) - ) - ) - ) - ) - (ref.is_null - (ref.null eq) - ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (i32.const 1633371484) + (i32.const 5) + ) + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) ) + (local.get $0) ) - (i32.const 15) + (i32.load8_u offset=8 + (i32.const 0) + ) + ) + ) + (i32.load8_u offset=9 + (i32.const 0) + ) + ) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) ) - (i32.const 235020035) + (local.get $0) + ) + (i32.load8_u offset=10 + (i32.const 0) ) ) - (i64.atomic.store32 offset=22 - (i32.and - (memory.atomic.notify offset=22 - (i32.and - (i32.const 255) - (i32.const 15) - ) - (i32.const -255) + ) + (local.set $0 + (i32.xor + (i32.const 1937132399) + (i32.load8_u offset=11 + (i32.const 0) + ) + ) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (i32.const -85) + (i32.const 5) + ) + (local.get $0) + ) + (i32.shl + (local.get $0) + (i32.const 5) + ) + ) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) + ) + (local.get $0) + ) + (i32.load8_u offset=13 + (i32.const 0) + ) + ) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.const 5) + (local.get $0) + ) + (i32.load8_u offset=14 + (i32.const 0) + ) + ) + ) + (local.set $0 + (i32.xor + (i32.add + (i32.shl + (local.get $0) + (i32.const 5) ) - (i32.const 15) + (local.get $0) + ) + (i32.load8_u offset=12 + (i32.const 0) ) - (i64.const 33554432) ) ) (local.get $0) diff --git a/test/typed-function-references.wast b/test/typed-function-references.wast new file mode 100644 index 000000000..59346f615 --- /dev/null +++ b/test/typed-function-references.wast @@ -0,0 +1,24 @@ +(module + (type $i32-i32 (func (param i32) (result i32))) + + (func $call-ref + (call_ref (ref.func $call-ref)) + ) + (func $return-call-ref + (return_call_ref (ref.func $call-ref)) + ) + (func $call-ref-more (param i32) (result i32) + (call_ref (i32.const 42) (ref.func $call-ref-more)) + ) + (func $call_from-param (param $f (ref $i32-i32)) (result i32) + (call_ref (i32.const 42) (local.get $f)) + ) + (func $call_from-param-null (param $f (ref null $i32-i32)) (result i32) + (call_ref (i32.const 42) (local.get $f)) + ) + (func $call_from-local-null (result i32) + (local $f (ref null $i32-i32)) + (local.set $f (ref.func $call-ref-more)) + (call_ref (i32.const 42) (local.get $f)) + ) +) diff --git a/test/typed-function-references.wast.from-wast b/test/typed-function-references.wast.from-wast new file mode 100644 index 000000000..c098f351d --- /dev/null +++ b/test/typed-function-references.wast.from-wast @@ -0,0 +1,45 @@ +(module + (type $none_=>_none (func)) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $none_=>_i32 (func (result i32))) + (type $ref_null<i32_->_i32>_=>_i32 (func (param (ref null (func (param i32) (result i32)))) (result i32))) + (type $ref<i32_->_i32>_=>_i32 (func (param (ref (func (param i32) (result i32)))) (result i32))) + (func $call-ref + (call_ref + (ref.func $call-ref) + ) + ) + (func $return-call-ref + (return_call_ref + (ref.func $call-ref) + ) + ) + (func $call-ref-more (param $0 i32) (result i32) + (call_ref + (i32.const 42) + (ref.func $call-ref-more) + ) + ) + (func $call_from-param (param $f (ref (func (param i32) (result i32)))) (result i32) + (call_ref + (i32.const 42) + (local.get $f) + ) + ) + (func $call_from-param-null (param $f (ref null (func (param i32) (result i32)))) (result i32) + (call_ref + (i32.const 42) + (local.get $f) + ) + ) + (func $call_from-local-null (result i32) + (local $f (ref null (func (param i32) (result i32)))) + (local.set $f + (ref.func $call-ref-more) + ) + (call_ref + (i32.const 42) + (local.get $f) + ) + ) +) diff --git a/test/typed-function-references.wast.fromBinary b/test/typed-function-references.wast.fromBinary new file mode 100644 index 000000000..dcabbeaaf --- /dev/null +++ b/test/typed-function-references.wast.fromBinary @@ -0,0 +1,46 @@ +(module + (type $none_=>_none (func)) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $none_=>_i32 (func (result i32))) + (type $ref_null<i32_->_i32>_=>_i32 (func (param (ref null (func (param i32) (result i32)))) (result i32))) + (type $ref<i32_->_i32>_=>_i32 (func (param (ref (func (param i32) (result i32)))) (result i32))) + (func $call-ref + (call_ref + (ref.func $call-ref) + ) + ) + (func $return-call-ref + (return_call_ref + (ref.func $call-ref) + ) + ) + (func $call-ref-more (param $0 i32) (result i32) + (call_ref + (i32.const 42) + (ref.func $call-ref-more) + ) + ) + (func $call_from-param (param $f (ref (func (param i32) (result i32)))) (result i32) + (call_ref + (i32.const 42) + (local.get $f) + ) + ) + (func $call_from-param-null (param $f (ref null (func (param i32) (result i32)))) (result i32) + (call_ref + (i32.const 42) + (local.get $f) + ) + ) + (func $call_from-local-null (result i32) + (local $f (ref null (func (param i32) (result i32)))) + (local.set $f + (ref.func $call-ref-more) + ) + (call_ref + (i32.const 42) + (local.get $f) + ) + ) +) + diff --git a/test/typed-function-references.wast.fromBinary.noDebugInfo b/test/typed-function-references.wast.fromBinary.noDebugInfo new file mode 100644 index 000000000..0808e0c52 --- /dev/null +++ b/test/typed-function-references.wast.fromBinary.noDebugInfo @@ -0,0 +1,46 @@ +(module + (type $none_=>_none (func)) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $none_=>_i32 (func (result i32))) + (type $ref_null<i32_->_i32>_=>_i32 (func (param (ref null (func (param i32) (result i32)))) (result i32))) + (type $ref<i32_->_i32>_=>_i32 (func (param (ref (func (param i32) (result i32)))) (result i32))) + (func $0 + (call_ref + (ref.func $0) + ) + ) + (func $1 + (return_call_ref + (ref.func $0) + ) + ) + (func $2 (param $0 i32) (result i32) + (call_ref + (i32.const 42) + (ref.func $2) + ) + ) + (func $3 (param $0 (ref (func (param i32) (result i32)))) (result i32) + (call_ref + (i32.const 42) + (local.get $0) + ) + ) + (func $4 (param $0 (ref null (func (param i32) (result i32)))) (result i32) + (call_ref + (i32.const 42) + (local.get $0) + ) + ) + (func $5 (result i32) + (local $0 (ref null (func (param i32) (result i32)))) + (local.set $0 + (ref.func $2) + ) + (call_ref + (i32.const 42) + (local.get $0) + ) + ) +) + diff --git a/test/unit/test_features.py b/test/unit/test_features.py index 889b149b1..01ea4c754 100644 --- a/test/unit/test_features.py +++ b/test/unit/test_features.py @@ -153,7 +153,7 @@ class FeatureValidationTest(utils.BinaryenTestCase): ) ) ''' - self.check_tail_call(module, 'return_call requires tail calls to be enabled') + self.check_tail_call(module, 'return_call* requires tail calls to be enabled') def test_tail_call_indirect(self): module = ''' @@ -167,7 +167,7 @@ class FeatureValidationTest(utils.BinaryenTestCase): ) ) ''' - self.check_tail_call(module, 'return_call_indirect requires tail calls to be enabled') + self.check_tail_call(module, 'return_call* requires tail calls to be enabled') def test_reference_types_externref(self): module = ''' diff --git a/test/unit/test_tail_call_type.py b/test/unit/test_tail_call_type.py index 01c6ca88e..1221dd0a7 100644 --- a/test/unit/test_tail_call_type.py +++ b/test/unit/test_tail_call_type.py @@ -21,7 +21,7 @@ class TailCallTypeTest(utils.BinaryenTestCase): input=module, check=False, capture_output=True) self.assertNotEqual(p.returncode, 0) self.assertIn( - 'return_call callee return type must match caller return type', + 'return_call* callee return type must match caller return type', p.stderr) def test_return_call_indirect(self): @@ -41,5 +41,5 @@ class TailCallTypeTest(utils.BinaryenTestCase): input=module, check=False, capture_output=True) self.assertNotEqual(p.returncode, 0) self.assertIn( - 'return_call_indirect callee return type must match caller return type', + 'return_call* callee return type must match caller return type', p.stderr) |