summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xscripts/gen-s-parser.py3
-rw-r--r--src/gen-s-parser.inc28
-rw-r--r--src/ir/ReFinalize.cpp1
-rw-r--r--src/ir/cost.h7
-rw-r--r--src/ir/effects.h11
-rw-r--r--src/ir/module-utils.h2
-rw-r--r--src/js/binaryen.js-post.js1
-rw-r--r--src/passes/DeadArgumentElimination.cpp6
-rw-r--r--src/passes/Directize.cpp44
-rw-r--r--src/passes/Inlining.cpp5
-rw-r--r--src/passes/MergeBlocks.cpp6
-rw-r--r--src/passes/Print.cpp42
-rw-r--r--src/shared-constants.h2
-rw-r--r--src/tools/fuzzing.h57
-rw-r--r--src/wasm-binary.h6
-rw-r--r--src/wasm-builder.h13
-rw-r--r--src/wasm-delegations-fields.h8
-rw-r--r--src/wasm-delegations.h1
-rw-r--r--src/wasm-interpreter.h34
-rw-r--r--src/wasm-s-parser.h8
-rw-r--r--src/wasm.h12
-rw-r--r--src/wasm/wasm-binary.cpp30
-rw-r--r--src/wasm/wasm-s-parser.cpp96
-rw-r--r--src/wasm/wasm-stack.cpp5
-rw-r--r--src/wasm/wasm-type.cpp3
-rw-r--r--src/wasm/wasm-validator.cpp156
-rw-r--r--src/wasm/wasm.cpp19
-rw-r--r--src/wasm2js.h4
-rw-r--r--test/binaryen.js/kitchen-sink.js1
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt25
-rw-r--r--test/extra-unreachable.wast4
-rw-r--r--test/extra-unreachable.wast.from-wast5
-rw-r--r--test/passes/directize_all-features.txt (renamed from test/passes/directize_enable-tail-call.txt)12
-rw-r--r--test/passes/directize_all-features.wast (renamed from test/passes/directize_enable-tail-call.wast)14
-rw-r--r--test/passes/instrument-locals_all-features_disable-typed-function-references.txt (renamed from test/passes/instrument-locals_all-features.txt)0
-rw-r--r--test/passes/instrument-locals_all-features_disable-typed-function-references.wast (renamed from test/passes/instrument-locals_all-features.wast)0
-rw-r--r--test/passes/translate-to-fuzz_all-features.txt1598
-rw-r--r--test/typed-function-references.wast24
-rw-r--r--test/typed-function-references.wast.from-wast45
-rw-r--r--test/typed-function-references.wast.fromBinary46
-rw-r--r--test/typed-function-references.wast.fromBinary.noDebugInfo46
-rw-r--r--test/unit/test_features.py4
-rw-r--r--test/unit/test_tail_call_type.py4
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)