summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2020-11-23 11:14:19 -0800
committerGitHub <noreply@github.com>2020-11-23 11:14:19 -0800
commitb2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef (patch)
tree10c773c5a21deb179043929e3e21db51ff4ccd59
parent68294338a1cc7337e808671e75933b1134d18a90 (diff)
downloadbinaryen-b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef.tar.gz
binaryen-b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef.tar.bz2
binaryen-b2d797f1f9f1192b8f4d2664f76a8d0b9278a0ef.zip
[TypedFunctionReferences] Add Typed Function References feature and use the types (#3388)
This adds the new feature and starts to use the new types where relevant. We use them even without the feature being enabled, as we don't know the features during wasm loading - but the hope is that given the type is a subtype, it should all work out. In practice, if you print out the internal type you may see a typed function reference-specific type for a ref.func for example, instead of a generic funcref, but it should not affect anything else. This PR does not support non-nullable types, that is, everything is nullable for now. As suggested by @tlively this is simpler for now and leaves nullability for later work (which will apparently require let or something else, and many passes may need to be changed). To allow this PR to work, we need to provide a type on creating a RefFunc. The wasm-builder.h internal API is updated for this, as are the C and JS APIs, which are breaking changes. cc @dcodeIO We must also write and read function types properly. This PR improves collectSignatures to find all the types, and also to sort them by the dependencies between them (as we can't emit X in the binary if it depends on Y, and Y has not been emitted - we need to give Y's index). This sorting ends up changing a few test outputs. InstrumentLocals support for printing function types that are not funcref is disabled for now, until we figure out how to make that work and/or decide if it's important enough to work on. The fuzzer has various fixes to emit valid types for things (mostly whitespace there). Also two drive-by fixes to call makeTrivial where it should be (when we fail to create a specific node, we can't just try to make another node, in theory it could infinitely recurse). Binary writing changes here to replace calls to a standalone function to write out a type with one that is called on the binary writer object itself, which maintains a mapping of type indexes (getFunctionSignatureByIndex).
-rw-r--r--CHANGELOG.md3
-rw-r--r--src/binaryen-c.cpp8
-rw-r--r--src/binaryen-c.h3
-rw-r--r--src/ir/ReFinalize.cpp6
-rw-r--r--src/ir/module-utils.h78
-rw-r--r--src/js/binaryen.js-post.js4
-rw-r--r--src/passes/InstrumentLocals.cpp81
-rw-r--r--src/tools/fuzzing.h54
-rw-r--r--src/tools/tool-options.h2
-rw-r--r--src/wasm-binary.h86
-rw-r--r--src/wasm-builder.h14
-rw-r--r--src/wasm-features.h11
-rw-r--r--src/wasm.h1
-rw-r--r--src/wasm/wasm-binary.cpp145
-rw-r--r--src/wasm/wasm-s-parser.cpp5
-rw-r--r--src/wasm/wasm-stack.cpp18
-rw-r--r--src/wasm/wasm-type.cpp8
-rw-r--r--src/wasm/wasm-validator.cpp1
-rw-r--r--src/wasm/wasm.cpp8
-rw-r--r--test/binaryen.js/expressions.js5
-rw-r--r--test/binaryen.js/kitchen-sink.js4
-rw-r--r--test/binaryen.js/kitchen-sink.js.txt2
-rw-r--r--test/example/c-api-kitchen-sink.c18
-rw-r--r--test/example/c-api-kitchen-sink.txt2
-rw-r--r--test/passes/fuzz-exec_all-features.txt12
-rw-r--r--test/passes/fuzz-exec_all-features.wast6
-rw-r--r--test/passes/fuzz_metrics_noprint.bin.txt48
-rw-r--r--test/passes/instrument-locals_all-features.txt36
-rw-r--r--test/passes/strip-target-features_roundtrip_print-features_all-features.txt1
-rw-r--r--test/passes/translate-to-fuzz_all-features.txt1629
-rw-r--r--test/unit/test_features.py3
31 files changed, 1233 insertions, 1069 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9bfab9c04..1d6edfac3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,9 @@ full changeset diff at the end of each section.
Current Trunk
-------------
+- `RefFunc` C and JS API constructors (`BinaryenRefFunc` and `ref.func`
+ respectively) now take an extra `type` parameter, similar to `RefNull`. This
+ is necessary for typed function references support.
- JS API functions for atomic notify/wait instructions are renamed.
- `module.atomic.notify` -> `module.memory.atomic.notify`
- `module.i32.atomic.wait` -> `module.memory.atomic.wait32`
diff --git a/src/binaryen-c.cpp b/src/binaryen-c.cpp
index 443549de9..7365a3863 100644
--- a/src/binaryen-c.cpp
+++ b/src/binaryen-c.cpp
@@ -1187,9 +1187,11 @@ BinaryenExpressionRef BinaryenRefIsNull(BinaryenModuleRef module,
Builder(*(Module*)module).makeRefIsNull((Expression*)value));
}
-BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
- const char* func) {
- return static_cast<Expression*>(Builder(*(Module*)module).makeRefFunc(func));
+BinaryenExpressionRef
+BinaryenRefFunc(BinaryenModuleRef module, const char* func, BinaryenType type) {
+ Type type_(type);
+ return static_cast<Expression*>(
+ Builder(*(Module*)module).makeRefFunc(func, type_));
}
BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module,
diff --git a/src/binaryen-c.h b/src/binaryen-c.h
index 45beb3657..c4517257a 100644
--- a/src/binaryen-c.h
+++ b/src/binaryen-c.h
@@ -792,7 +792,8 @@ BINARYEN_API BinaryenExpressionRef BinaryenRefNull(BinaryenModuleRef module,
BINARYEN_API BinaryenExpressionRef
BinaryenRefIsNull(BinaryenModuleRef module, BinaryenExpressionRef value);
BINARYEN_API BinaryenExpressionRef BinaryenRefFunc(BinaryenModuleRef module,
- const char* func);
+ const char* func,
+ BinaryenType type);
BINARYEN_API BinaryenExpressionRef BinaryenRefEq(BinaryenModuleRef module,
BinaryenExpressionRef left,
BinaryenExpressionRef right);
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
index 19fed54a7..448c1f30a 100644
--- a/src/ir/ReFinalize.cpp
+++ b/src/ir/ReFinalize.cpp
@@ -126,7 +126,11 @@ void ReFinalize::visitMemorySize(MemorySize* curr) { curr->finalize(); }
void ReFinalize::visitMemoryGrow(MemoryGrow* curr) { curr->finalize(); }
void ReFinalize::visitRefNull(RefNull* curr) { curr->finalize(); }
void ReFinalize::visitRefIsNull(RefIsNull* curr) { curr->finalize(); }
-void ReFinalize::visitRefFunc(RefFunc* curr) { curr->finalize(); }
+void ReFinalize::visitRefFunc(RefFunc* curr) {
+ // TODO: should we look up the function and update the type from there? This
+ // could handle a change to the function's type, but is also not really what
+ // this class has been meant to do.
+}
void ReFinalize::visitRefEq(RefEq* curr) { curr->finalize(); }
void ReFinalize::visitTry(Try* curr) { curr->finalize(); }
void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); }
diff --git a/src/ir/module-utils.h b/src/ir/module-utils.h
index a2d256073..c8776297c 100644
--- a/src/ir/module-utils.h
+++ b/src/ir/module-utils.h
@@ -414,16 +414,29 @@ collectSignatures(Module& wasm,
Counts& counts;
TypeCounter(Counts& counts) : counts(counts) {}
+
void visitExpression(Expression* curr) {
- if (auto* call = curr->dynCast<CallIndirect>()) {
+ if (curr->is<RefNull>()) {
+ maybeNote(curr->type);
+ } else if (auto* call = curr->dynCast<CallIndirect>()) {
counts[call->sig]++;
} else if (Properties::isControlFlowStructure(curr)) {
- // TODO: Allow control flow to have input types as well
+ maybeNote(curr->type);
if (curr->type.isTuple()) {
+ // TODO: Allow control flow to have input types as well
counts[Signature(Type::none, curr->type)]++;
}
}
}
+
+ void maybeNote(Type type) {
+ if (type.isRef()) {
+ auto heapType = type.getHeapType();
+ if (heapType.isSignature()) {
+ counts[heapType.getSignature()]++;
+ }
+ }
+ }
};
TypeCounter(counts).walk(func->body);
};
@@ -434,6 +447,14 @@ collectSignatures(Module& wasm,
Counts counts;
for (auto& curr : wasm.functions) {
counts[curr->sig]++;
+ for (auto type : curr->vars) {
+ if (type.isRef()) {
+ auto heapType = type.getHeapType();
+ if (heapType.isSignature()) {
+ counts[heapType.getSignature()]++;
+ }
+ }
+ }
}
for (auto& curr : wasm.events) {
counts[curr->sig]++;
@@ -444,10 +465,61 @@ collectSignatures(Module& wasm,
counts[innerPair.first] += innerPair.second;
}
}
+
+ // TODO: recursively traverse each reference type, which may have a child type
+ // this is itself a reference type.
+
+ // We must sort all the dependencies of a signature before it. For example,
+ // (func (param (ref (func)))) must appear after (func). To do that, find the
+ // depth of dependencies of each signature. For example, if A depends on B
+ // which depends on C, then A's depth is 2, B's is 1, and C's is 0 (assuming
+ // no other dependencies).
+ Counts depthOfDependencies;
+ std::unordered_map<Signature, std::unordered_set<Signature>> isDependencyOf;
+ // To calculate the depth of dependencies, we'll do a flow analysis, visiting
+ // each signature as we find out new things about it.
+ std::set<Signature> toVisit;
+ for (auto& pair : counts) {
+ auto sig = pair.first;
+ depthOfDependencies[sig] = 0;
+ toVisit.insert(sig);
+ for (Type type : {sig.params, sig.results}) {
+ for (auto element : type) {
+ if (element.isRef()) {
+ auto heapType = element.getHeapType();
+ if (heapType.isSignature()) {
+ isDependencyOf[heapType.getSignature()].insert(sig);
+ }
+ }
+ }
+ }
+ }
+ while (!toVisit.empty()) {
+ auto iter = toVisit.begin();
+ auto sig = *iter;
+ toVisit.erase(iter);
+ // Anything that depends on this has a depth of dependencies equal to this
+ // signature's, plus this signature itself.
+ auto newDepth = depthOfDependencies[sig] + 1;
+ if (newDepth > counts.size()) {
+ Fatal() << "Cyclic signatures detected, cannot sort them.";
+ }
+ for (auto& other : isDependencyOf[sig]) {
+ if (depthOfDependencies[other] < newDepth) {
+ // We found something new to propagate.
+ depthOfDependencies[other] = newDepth;
+ toVisit.insert(other);
+ }
+ }
+ }
+ // Sort by frequency and then simplicity, and also keeping every signature
+ // before things that depend on it.
std::vector<std::pair<Signature, size_t>> sorted(counts.begin(),
counts.end());
std::sort(sorted.begin(), sorted.end(), [&](auto a, auto b) {
- // order by frequency then simplicity
+ if (depthOfDependencies[a.first] != depthOfDependencies[b.first]) {
+ return depthOfDependencies[a.first] < depthOfDependencies[b.first];
+ }
if (a.second != b.second) {
return a.second > b.second;
}
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index b78fdd996..9abe5e718 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -2112,8 +2112,8 @@ function wrapModule(module, self = {}) {
'is_null'(value) {
return Module['_BinaryenRefIsNull'](module, value);
},
- 'func'(func) {
- return preserveStack(() => Module['_BinaryenRefFunc'](module, strToStack(func)));
+ 'func'(func, type) {
+ return preserveStack(() => Module['_BinaryenRefFunc'](module, strToStack(func), type));
},
'eq'(left, right) {
return Module['_BinaryenRefEq'](module, left, right);
diff --git a/src/passes/InstrumentLocals.cpp b/src/passes/InstrumentLocals.cpp
index 81494463f..004bfba74 100644
--- a/src/passes/InstrumentLocals.cpp
+++ b/src/passes/InstrumentLocals.cpp
@@ -135,45 +135,48 @@ struct InstrumentLocals : public WalkerPass<PostWalker<InstrumentLocals>> {
Builder builder(*getModule());
Name import;
auto type = curr->value->type;
- if (type.isFunction()) {
- import = set_funcref;
- } else {
- TODO_SINGLE_COMPOUND(curr->value->type);
- switch (type.getBasic()) {
- case Type::i32:
- import = set_i32;
- break;
- case Type::i64:
- return; // TODO
- case Type::f32:
- import = set_f32;
- break;
- case Type::f64:
- import = set_f64;
- break;
- case Type::v128:
- import = set_v128;
- break;
- case Type::externref:
- import = set_externref;
- break;
- case Type::exnref:
- import = set_exnref;
- break;
- case Type::anyref:
- import = set_anyref;
- break;
- case Type::eqref:
- import = set_eqref;
- break;
- case Type::i31ref:
- import = set_i31ref;
- break;
- case Type::unreachable:
- return; // nothing to do here
- default:
- WASM_UNREACHABLE("unexpected type");
- }
+ if (type.isFunction() && type != Type::funcref) {
+ // FIXME: support typed function references
+ return;
+ }
+ TODO_SINGLE_COMPOUND(curr->value->type);
+ switch (type.getBasic()) {
+ case Type::i32:
+ import = set_i32;
+ break;
+ case Type::i64:
+ return; // TODO
+ case Type::f32:
+ import = set_f32;
+ break;
+ case Type::f64:
+ import = set_f64;
+ break;
+ case Type::v128:
+ import = set_v128;
+ break;
+ case Type::funcref:
+ import = set_funcref;
+ break;
+ case Type::externref:
+ import = set_externref;
+ break;
+ case Type::exnref:
+ import = set_exnref;
+ break;
+ case Type::anyref:
+ import = set_anyref;
+ break;
+ case Type::eqref:
+ import = set_eqref;
+ break;
+ case Type::i31ref:
+ import = set_i31ref;
+ break;
+ case Type::unreachable:
+ return; // nothing to do here
+ default:
+ WASM_UNREACHABLE("unexpected type");
}
curr->value = builder.makeCall(import,
{builder.makeConst(int32_t(id++)),
diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h
index df7cf2226..1c1359586 100644
--- a/src/tools/fuzzing.h
+++ b/src/tools/fuzzing.h
@@ -321,6 +321,10 @@ private:
}
return Type(types);
}
+ if (type.isFunction() && type != Type::funcref) {
+ // TODO: specific typed function references types.
+ return type;
+ }
SmallVector<Type, 2> options;
options.push_back(type); // includes itself
TODO_SINGLE_COMPOUND(type);
@@ -653,6 +657,10 @@ private:
Index numVars = upToSquared(MAX_VARS);
for (Index i = 0; i < numVars; i++) {
auto type = getConcreteType();
+ if (type.isRef() && !type.isNullable()) {
+ // We can't use a nullable type as a var, which is null-initialized.
+ continue;
+ }
funcContext->typeLocals[type].push_back(params.size() +
func->vars.size());
func->vars.push_back(type);
@@ -1371,7 +1379,6 @@ private:
}
Expression* makeCall(Type type) {
- // seems ok, go on
int tries = TRIES;
bool isReturn;
while (tries-- > 0) {
@@ -1392,7 +1399,7 @@ private:
return builder.makeCall(target->name, args, type, isReturn);
}
// we failed to find something
- return make(type);
+ return makeTrivial(type);
}
Expression* makeCallIndirect(Type type) {
@@ -1418,7 +1425,7 @@ private:
i = 0;
}
if (i == start) {
- return make(type);
+ return makeTrivial(type);
}
}
// with high probability, make sure the type is valid otherwise, most are
@@ -2018,12 +2025,28 @@ private:
if (!wasm.functions.empty() && !oneIn(wasm.functions.size())) {
target = pick(wasm.functions).get();
}
- return builder.makeRefFunc(target->name);
+ auto type = Type(HeapType(target->sig), /* nullable = */ true);
+ return builder.makeRefFunc(target->name, type);
}
if (type == Type::i31ref) {
return builder.makeI31New(makeConst(Type::i32));
}
- return builder.makeRefNull(type);
+ if (oneIn(2) && type.isNullable()) {
+ return builder.makeRefNull(type);
+ }
+ // TODO: randomize the order
+ for (auto& func : wasm.functions) {
+ // FIXME: RefFunc type should be non-nullable, but we emit nullable
+ // types for now.
+ if (type == Type(HeapType(func->sig), /* nullable = */ true)) {
+ return builder.makeRefFunc(func->name, type);
+ }
+ }
+ // We failed to find a function, so create a null reference if we can.
+ if (type.isNullable()) {
+ return builder.makeRefNull(type);
+ }
+ WASM_UNREACHABLE("un-handleable non-nullable type");
}
if (type.isTuple()) {
std::vector<Expression*> operands;
@@ -2972,6 +2995,7 @@ private:
Type::anyref,
Type::eqref,
Type::i31ref));
+ // TODO: emit typed function references types
}
Type getSingleConcreteType() { return pick(getSingleConcreteTypes()); }
@@ -2997,12 +3021,24 @@ private:
Type getEqReferenceType() { return pick(getEqReferenceTypes()); }
+ Type getMVPType() {
+ return pick(items(FeatureOptions<Type>().add(
+ FeatureSet::MVP, Type::i32, Type::i64, Type::f32, Type::f64)));
+ }
+
Type getTupleType() {
std::vector<Type> elements;
- size_t numElements = 2 + upTo(MAX_TUPLE_SIZE - 1);
- elements.resize(numElements);
- for (size_t i = 0; i < numElements; ++i) {
- elements[i] = getSingleConcreteType();
+ size_t maxElements = 2 + upTo(MAX_TUPLE_SIZE - 1);
+ for (size_t i = 0; i < maxElements; ++i) {
+ auto type = getSingleConcreteType();
+ // Don't add a non-nullable type into a tuple, as currently we can't spill
+ // them into locals (that would require a "let").
+ if (!type.isNullable()) {
+ elements.push_back(type);
+ }
+ }
+ while (elements.size() < 2) {
+ elements.push_back(getMVPType());
}
return Type(elements);
}
diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h
index 4b084e191..70ce4efc0 100644
--- a/src/tools/tool-options.h
+++ b/src/tools/tool-options.h
@@ -89,6 +89,8 @@ struct ToolOptions : public Options {
.addFeature(FeatureSet::Multivalue, "multivalue functions")
.addFeature(FeatureSet::GC, "garbage collection")
.addFeature(FeatureSet::Memory64, "memory64")
+ .addFeature(FeatureSet::TypedFunctionReferences,
+ "typed function references")
.add("--no-validation",
"-n",
"Disables validation, assumes inputs are correct",
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index ef3f9c9d1..0918151c5 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -346,6 +346,10 @@ enum EncodedType {
anyref = -0x12, // 0x6e
// comparable reference type
eqref = -0x13, // 0x6d
+ // nullable typed function reference type, with parameter
+ nullable = -0x14, // 0x6c
+ // non-nullable typed function reference type, with parameter
+ nonnullable = -0x15, // 0x6b
// integer reference type
i31ref = -0x16, // 0x6a
// exception reference type
@@ -386,6 +390,7 @@ extern const char* ReferenceTypesFeature;
extern const char* MultivalueFeature;
extern const char* GCFeature;
extern const char* Memory64Feature;
+extern const char* TypedFunctionReferencesFeature;
enum Subsection {
NameModule = 0,
@@ -1009,82 +1014,6 @@ enum FeaturePrefix {
} // namespace BinaryConsts
-inline S32LEB binaryType(Type type) {
- int ret = 0;
- TODO_SINGLE_COMPOUND(type);
- switch (type.getBasic()) {
- // None only used for block signatures. TODO: Separate out?
- case Type::none:
- ret = BinaryConsts::EncodedType::Empty;
- break;
- case Type::i32:
- ret = BinaryConsts::EncodedType::i32;
- break;
- case Type::i64:
- ret = BinaryConsts::EncodedType::i64;
- break;
- case Type::f32:
- ret = BinaryConsts::EncodedType::f32;
- break;
- case Type::f64:
- ret = BinaryConsts::EncodedType::f64;
- break;
- case Type::v128:
- ret = BinaryConsts::EncodedType::v128;
- break;
- case Type::funcref:
- ret = BinaryConsts::EncodedType::funcref;
- break;
- case Type::externref:
- ret = BinaryConsts::EncodedType::externref;
- break;
- case Type::exnref:
- ret = BinaryConsts::EncodedType::exnref;
- break;
- case Type::anyref:
- ret = BinaryConsts::EncodedType::anyref;
- break;
- case Type::eqref:
- ret = BinaryConsts::EncodedType::eqref;
- break;
- case Type::i31ref:
- ret = BinaryConsts::EncodedType::i31ref;
- break;
- case Type::unreachable:
- WASM_UNREACHABLE("unexpected type");
- }
- return S32LEB(ret);
-}
-
-inline S32LEB binaryHeapType(HeapType type) {
- int ret = 0;
- switch (type.kind) {
- case HeapType::FuncKind:
- ret = BinaryConsts::EncodedHeapType::func;
- break;
- case HeapType::ExternKind:
- ret = BinaryConsts::EncodedHeapType::extern_;
- break;
- case HeapType::ExnKind:
- ret = BinaryConsts::EncodedHeapType::exn;
- break;
- case HeapType::AnyKind:
- ret = BinaryConsts::EncodedHeapType::any;
- break;
- case HeapType::EqKind:
- ret = BinaryConsts::EncodedHeapType::eq;
- break;
- case HeapType::I31Kind:
- ret = BinaryConsts::EncodedHeapType::i31;
- break;
- case HeapType::SignatureKind:
- case HeapType::StructKind:
- case HeapType::ArrayKind:
- WASM_UNREACHABLE("TODO: compound GC types");
- }
- return S32LEB(ret); // TODO: Actually encoded as s33
-}
-
// Writes out wasm to the binary format
class WasmBinaryWriter {
@@ -1234,6 +1163,9 @@ public:
Module* getModule() { return wasm; }
+ void writeType(Type type);
+ void writeHeapType(HeapType type);
+
private:
Module* wasm;
BufferWithRandomAccess& o;
@@ -1342,6 +1274,8 @@ public:
std::vector<Signature> functionSignatures;
void readFunctionSignatures();
+ Signature getFunctionSignatureByIndex(Index index);
+
size_t nextLabel;
Name getNextLabel();
diff --git a/src/wasm-builder.h b/src/wasm-builder.h
index d3af93896..6800aa2ed 100644
--- a/src/wasm-builder.h
+++ b/src/wasm-builder.h
@@ -588,10 +588,10 @@ public:
ret->finalize();
return ret;
}
- RefFunc* makeRefFunc(Name func) {
+ RefFunc* makeRefFunc(Name func, Type type) {
auto* ret = wasm.allocator.alloc<RefFunc>();
ret->func = func;
- ret->finalize();
+ ret->finalize(type);
return ret;
}
RefEq* makeRefEq(Expression* left, Expression* right) {
@@ -769,8 +769,7 @@ public:
}
if (type.isFunction()) {
if (!value.isNull()) {
- // TODO: with typed function references we need to do more for the type
- return makeRefFunc(value.getFunc());
+ return makeRefFunc(value.getFunc(), type);
}
return makeRefNull(type);
}
@@ -951,7 +950,12 @@ public:
return makeConstantExpression(Literal::makeZeros(curr->type));
}
if (curr->type.isFunction()) {
- return ExpressionManipulator::refNull(curr, curr->type);
+ if (curr->type.isNullable()) {
+ return ExpressionManipulator::refNull(curr, curr->type);
+ } else {
+ // We can't do any better, keep the original.
+ return curr;
+ }
}
Literal value;
// TODO: reuse node conditionally when possible for literals
diff --git a/src/wasm-features.h b/src/wasm-features.h
index a2bb52971..d2e3f343f 100644
--- a/src/wasm-features.h
+++ b/src/wasm-features.h
@@ -38,7 +38,8 @@ struct FeatureSet {
Multivalue = 1 << 9,
GC = 1 << 10,
Memory64 = 1 << 11,
- All = (1 << 12) - 1
+ TypedFunctionReferences = 1 << 12,
+ All = (1 << 13) - 1
};
static std::string toString(Feature f) {
@@ -67,6 +68,8 @@ struct FeatureSet {
return "gc";
case Memory64:
return "memory64";
+ case TypedFunctionReferences:
+ return "typed-function-references";
default:
WASM_UNREACHABLE("unexpected feature");
}
@@ -92,6 +95,9 @@ struct FeatureSet {
bool hasMultivalue() const { return (features & Multivalue) != 0; }
bool hasGC() const { return (features & GC) != 0; }
bool hasMemory64() const { return (features & Memory64) != 0; }
+ bool hasTypedFunctionReferences() const {
+ return (features & TypedFunctionReferences) != 0;
+ }
bool hasAll() const { return (features & All) != 0; }
void makeMVP() { features = MVP; }
@@ -110,6 +116,9 @@ struct FeatureSet {
void setMultivalue(bool v = true) { set(Multivalue, v); }
void setGC(bool v = true) { set(GC, v); }
void setMemory64(bool v = true) { set(Memory64, v); }
+ void setTypedFunctionReferences(bool v = true) {
+ set(TypedFunctionReferences, v);
+ }
void setAll(bool v = true) { features = v ? All : MVP; }
void enable(const FeatureSet& other) { features |= other.features; }
diff --git a/src/wasm.h b/src/wasm.h
index 1204eee0f..e9fb4461b 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -1198,6 +1198,7 @@ public:
Name func;
void finalize();
+ void finalize(Type type_);
};
class RefEq : public SpecificExpression<Expression::RefEqId> {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index b343c6caf..a96039bc2 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -221,7 +221,7 @@ void WasmBinaryWriter::writeTypes() {
for (auto& sigType : {sig.params, sig.results}) {
o << U32LEB(sigType.size());
for (const auto& type : sigType) {
- o << binaryType(type);
+ writeType(type);
}
}
}
@@ -250,7 +250,7 @@ void WasmBinaryWriter::writeImports() {
BYN_TRACE("write one global\n");
writeImportHeader(global);
o << U32LEB(int32_t(ExternalKind::Global));
- o << binaryType(global->type);
+ writeType(global->type);
o << U32LEB(global->mutable_);
});
ModuleUtils::iterImportedEvents(*wasm, [&](Event* event) {
@@ -389,7 +389,7 @@ void WasmBinaryWriter::writeGlobals() {
BYN_TRACE("write one\n");
size_t i = 0;
for (const auto& t : global->type) {
- o << binaryType(t);
+ writeType(t);
o << U32LEB(global->mutable_);
if (global->type.size() == 1) {
writeExpression(global->init);
@@ -492,7 +492,12 @@ uint32_t WasmBinaryWriter::getEventIndex(Name name) const {
uint32_t WasmBinaryWriter::getTypeIndex(Signature sig) const {
auto it = typeIndices.find(sig);
- assert(it != typeIndices.end());
+#ifndef NDEBUG
+ if (it == typeIndices.end()) {
+ std::cout << "Missing signature: " << sig << '\n';
+ assert(0);
+ }
+#endif
return it->second;
}
@@ -799,6 +804,8 @@ void WasmBinaryWriter::writeFeaturesSection() {
return BinaryConsts::UserSections::GCFeature;
case FeatureSet::Memory64:
return BinaryConsts::UserSections::Memory64Feature;
+ case FeatureSet::TypedFunctionReferences:
+ return BinaryConsts::UserSections::TypedFunctionReferencesFeature;
default:
WASM_UNREACHABLE("unexpected feature flag");
}
@@ -950,6 +957,100 @@ void WasmBinaryWriter::finishUp() {
}
}
+void WasmBinaryWriter::writeType(Type type) {
+ if (type.isRef()) {
+ auto heapType = type.getHeapType();
+ // TODO: fully handle non-signature reference types (GC), and in reading
+ if (heapType.isSignature()) {
+ if (type.isNullable()) {
+ o << S32LEB(BinaryConsts::EncodedType::nullable);
+ } else {
+ o << S32LEB(BinaryConsts::EncodedType::nonnullable);
+ }
+ writeHeapType(heapType);
+ return;
+ }
+ }
+ int ret = 0;
+ TODO_SINGLE_COMPOUND(type);
+ switch (type.getBasic()) {
+ // None only used for block signatures. TODO: Separate out?
+ case Type::none:
+ ret = BinaryConsts::EncodedType::Empty;
+ break;
+ case Type::i32:
+ ret = BinaryConsts::EncodedType::i32;
+ break;
+ case Type::i64:
+ ret = BinaryConsts::EncodedType::i64;
+ break;
+ case Type::f32:
+ ret = BinaryConsts::EncodedType::f32;
+ break;
+ case Type::f64:
+ ret = BinaryConsts::EncodedType::f64;
+ break;
+ case Type::v128:
+ ret = BinaryConsts::EncodedType::v128;
+ break;
+ case Type::funcref:
+ ret = BinaryConsts::EncodedType::funcref;
+ break;
+ case Type::externref:
+ ret = BinaryConsts::EncodedType::externref;
+ break;
+ case Type::exnref:
+ ret = BinaryConsts::EncodedType::exnref;
+ break;
+ case Type::anyref:
+ ret = BinaryConsts::EncodedType::anyref;
+ break;
+ case Type::eqref:
+ ret = BinaryConsts::EncodedType::eqref;
+ break;
+ case Type::i31ref:
+ ret = BinaryConsts::EncodedType::i31ref;
+ break;
+ default:
+ WASM_UNREACHABLE("unexpected type");
+ }
+ o << S32LEB(ret);
+}
+
+void WasmBinaryWriter::writeHeapType(HeapType type) {
+ if (type.isSignature()) {
+ auto sig = type.getSignature();
+ o << S32LEB(getTypeIndex(sig));
+ return;
+ }
+ int ret = 0;
+ switch (type.kind) {
+ case HeapType::FuncKind:
+ ret = BinaryConsts::EncodedHeapType::func;
+ break;
+ case HeapType::ExternKind:
+ ret = BinaryConsts::EncodedHeapType::extern_;
+ break;
+ case HeapType::ExnKind:
+ ret = BinaryConsts::EncodedHeapType::exn;
+ break;
+ case HeapType::AnyKind:
+ ret = BinaryConsts::EncodedHeapType::any;
+ break;
+ case HeapType::EqKind:
+ ret = BinaryConsts::EncodedHeapType::eq;
+ break;
+ case HeapType::I31Kind:
+ ret = BinaryConsts::EncodedHeapType::i31;
+ break;
+ case HeapType::SignatureKind:
+ case HeapType::StructKind:
+ case HeapType::ArrayKind:
+ WASM_UNREACHABLE("TODO: compound GC types");
+ }
+ o << S32LEB(ret); // TODO: Actually encoded as s33
+}
+
// reader
bool WasmBinaryBuilder::hasDWARFSections() {
@@ -1253,6 +1354,10 @@ Type WasmBinaryBuilder::getType() {
return Type::anyref;
case BinaryConsts::EncodedType::eqref:
return Type::eqref;
+ case BinaryConsts::EncodedType::nullable:
+ return Type(getHeapType(), /* nullable = */ true);
+ case BinaryConsts::EncodedType::nonnullable:
+ return Type(getHeapType(), /* nullable = */ false);
case BinaryConsts::EncodedType::i31ref:
return Type::i31ref;
default:
@@ -1581,6 +1686,18 @@ void WasmBinaryBuilder::readFunctionSignatures() {
}
}
+Signature WasmBinaryBuilder::getFunctionSignatureByIndex(Index index) {
+ Signature sig;
+ if (index < functionImports.size()) {
+ return functionImports[index]->sig;
+ }
+ Index adjustedIndex = index - functionImports.size();
+ if (adjustedIndex >= functionSignatures.size()) {
+ throwError("invalid function index");
+ }
+ return functionSignatures[adjustedIndex];
+}
+
void WasmBinaryBuilder::readFunctions() {
BYN_TRACE("== readFunctions\n");
size_t total = getU32LEB();
@@ -2471,6 +2588,9 @@ void WasmBinaryBuilder::readFeatures(size_t payloadLen) {
wasm.features.setGC();
} else if (name == BinaryConsts::UserSections::Memory64Feature) {
wasm.features.setMemory64();
+ } else if (name ==
+ BinaryConsts::UserSections::TypedFunctionReferencesFeature) {
+ wasm.features.setTypedFunctionReferences();
}
}
}
@@ -3042,17 +3162,7 @@ void WasmBinaryBuilder::visitSwitch(Switch* curr) {
void WasmBinaryBuilder::visitCall(Call* curr) {
BYN_TRACE("zz node: Call\n");
auto index = getU32LEB();
- Signature sig;
- if (index < functionImports.size()) {
- auto* import = functionImports[index];
- sig = import->sig;
- } else {
- Index adjustedIndex = index - functionImports.size();
- if (adjustedIndex >= functionSignatures.size()) {
- throwError("invalid call index");
- }
- sig = functionSignatures[adjustedIndex];
- }
+ auto sig = getFunctionSignatureByIndex(index);
auto num = sig.params.size();
curr->operands.resize(num);
for (size_t i = 0; i < num; i++) {
@@ -5169,7 +5279,10 @@ void WasmBinaryBuilder::visitRefFunc(RefFunc* curr) {
throwError("ref.func: invalid call index");
}
functionRefs[index].push_back(curr); // we don't know function names yet
- curr->finalize();
+ // To support typed function refs, we give the reference not just a general
+ // funcref, but a specific subtype with the actual signature.
+ curr->finalize(
+ Type(HeapType(getFunctionSignatureByIndex(index)), /* nullable = */ true));
}
void WasmBinaryBuilder::visitRefEq(RefEq* curr) {
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index 0636836d7..6286ae090 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -1890,7 +1890,10 @@ Expression* SExpressionWasmBuilder::makeRefFunc(Element& s) {
auto func = getFunctionName(*s[1]);
auto ret = allocator.alloc<RefFunc>();
ret->func = func;
- ret->finalize();
+ // To support typed function refs, we give the reference not just a general
+ // funcref, but a specific subtype with the actual signature.
+ ret->finalize(
+ Type(HeapType(functionSignatures[func]), /* nullable = */ true));
return ret;
}
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index c8a4f7a90..021b05cb6 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -24,11 +24,11 @@ static Name IMPOSSIBLE_CONTINUE("impossible-continue");
void BinaryInstWriter::emitResultType(Type type) {
if (type == Type::unreachable) {
- o << binaryType(Type::none);
+ parent.writeType(Type::none);
} else if (type.isTuple()) {
o << S32LEB(parent.getTypeIndex(Signature(Type::none, type)));
} else {
- o << binaryType(type);
+ parent.writeType(type);
}
}
@@ -1756,8 +1756,8 @@ void BinaryInstWriter::visitSelect(Select* curr) {
if (curr->type.isRef()) {
o << int8_t(BinaryConsts::SelectWithType) << U32LEB(curr->type.size());
for (size_t i = 0; i < curr->type.size(); i++) {
- o << binaryType(curr->type != Type::unreachable ? curr->type
- : Type::none);
+ parent.writeType(curr->type != Type::unreachable ? curr->type
+ : Type::none);
}
} else {
o << int8_t(BinaryConsts::Select);
@@ -1779,8 +1779,8 @@ void BinaryInstWriter::visitMemoryGrow(MemoryGrow* curr) {
}
void BinaryInstWriter::visitRefNull(RefNull* curr) {
- o << int8_t(BinaryConsts::RefNull)
- << binaryHeapType(curr->type.getHeapType());
+ o << int8_t(BinaryConsts::RefNull);
+ parent.writeHeapType(curr->type.getHeapType());
}
void BinaryInstWriter::visitRefIsNull(RefIsNull* curr) {
@@ -1966,7 +1966,8 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
o << U32LEB(func->getNumVars());
for (Index i = varStart; i < varEnd; i++) {
mappedLocals[std::make_pair(i, 0)] = i;
- o << U32LEB(1) << binaryType(func->getLocalType(i));
+ o << U32LEB(1);
+ parent.writeType(func->getLocalType(i));
}
return;
}
@@ -1995,7 +1996,8 @@ void BinaryInstWriter::mapLocalsAndEmitHeader() {
setScratchLocals();
o << U32LEB(numLocalsByType.size());
for (auto& typeCount : numLocalsByType) {
- o << U32LEB(typeCount.second) << binaryType(typeCount.first);
+ o << U32LEB(typeCount.second);
+ parent.writeType(typeCount.first);
}
}
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index dc4d50ef4..cf4404739 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -460,6 +460,14 @@ Type Type::reinterpret() const {
FeatureSet Type::getFeatures() const {
auto getSingleFeatures = [](Type t) -> FeatureSet {
+ if (t != Type::funcref && t.isFunction()) {
+ // Strictly speaking, typed function references require the typed function
+ // references feature, however, we use these types internally regardless
+ // of the presence of features (in particular, since during load of the
+ // wasm we don't know the features yet, so we apply the more refined
+ // types).
+ return FeatureSet::ReferenceTypes;
+ }
TODO_SINGLE_COMPOUND(t);
switch (t.getBasic()) {
case Type::v128:
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 809ca5a6a..78e123a90 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2313,6 +2313,7 @@ void FunctionValidator::visitFunction(Function* curr) {
for (const auto& var : curr->vars) {
features |= var.getFeatures();
shouldBeTrue(var.isConcrete(), curr, "vars must be concretely typed");
+ // TODO: check for nullability
}
shouldBeTrue(features <= getModule()->features,
curr->name,
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index c7a187b43..6245a3575 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -47,6 +47,7 @@ const char* ReferenceTypesFeature = "reference-types";
const char* MultivalueFeature = "multivalue";
const char* GCFeature = "gc";
const char* Memory64Feature = "memory64";
+const char* TypedFunctionReferencesFeature = "typed-function-references";
} // namespace UserSections
} // namespace BinaryConsts
@@ -984,7 +985,12 @@ void RefIsNull::finalize() {
type = Type::i32;
}
-void RefFunc::finalize() { type = Type::funcref; }
+void RefFunc::finalize() {
+ // No-op. We assume that the full proper typed function type has been applied
+ // previously.
+}
+
+void RefFunc::finalize(Type type_) { type = type_; }
void RefEq::finalize() {
if (left->type == Type::unreachable || right->type == Type::unreachable) {
diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js
index a340adcbe..61fc0a3ef 100644
--- a/test/binaryen.js/expressions.js
+++ b/test/binaryen.js/expressions.js
@@ -1378,7 +1378,7 @@ console.log("# RefFunc");
const module = new binaryen.Module();
var func = "a";
- const theRefFunc = binaryen.RefFunc(module.ref.func(func));
+ const theRefFunc = binaryen.RefFunc(module.ref.func(func, binaryen.funcref));
assert(theRefFunc instanceof binaryen.RefFunc);
assert(theRefFunc instanceof binaryen.Expression);
assert(theRefFunc.func === func);
@@ -1388,7 +1388,8 @@ console.log("# RefFunc");
assert(theRefFunc.func === func);
theRefFunc.type = binaryen.f64;
theRefFunc.finalize();
- assert(theRefFunc.type === binaryen.funcref);
+ // TODO The type is a subtype of funcref, but we can't check that in the JS
+ // API atm.
console.log(theRefFunc.toText());
assert(
diff --git a/test/binaryen.js/kitchen-sink.js b/test/binaryen.js/kitchen-sink.js
index 836b7ab17..703e8cb5a 100644
--- a/test/binaryen.js/kitchen-sink.js
+++ b/test/binaryen.js/kitchen-sink.js
@@ -539,8 +539,8 @@ function test_core() {
// Reference types
module.ref.is_null(module.ref.null(binaryen.externref)),
module.ref.is_null(module.ref.null(binaryen.funcref)),
- module.ref.is_null(module.ref.func("kitchen()sinker")),
- module.select(temp10, module.ref.null(binaryen.funcref), module.ref.func("kitchen()sinker"), binaryen.funcref),
+ module.ref.is_null(module.ref.func("kitchen()sinker", binaryen.funcref)),
+ module.select(temp10, module.ref.null(binaryen.funcref), module.ref.func("kitchen()sinker", binaryen.funcref), binaryen.funcref),
// GC
module.ref.eq(module.ref.null(binaryen.eqref), module.ref.null(binaryen.eqref)),
diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt
index 4990c0abd..980a6d8c1 100644
--- a/test/binaryen.js/kitchen-sink.js.txt
+++ b/test/binaryen.js/kitchen-sink.js.txt
@@ -41,7 +41,7 @@ Features.ReferenceTypes: 256
Features.Multivalue: 512
Features.GC: 1024
Features.Memory64: 2048
-Features.All: 4095
+Features.All: 8191
InvalidId: 0
BlockId: 1
IfId: 2
diff --git a/test/example/c-api-kitchen-sink.c b/test/example/c-api-kitchen-sink.c
index 8a2f22e8d..25f8b1937 100644
--- a/test/example/c-api-kitchen-sink.c
+++ b/test/example/c-api-kitchen-sink.c
@@ -310,7 +310,8 @@ void test_core() {
temp15 = makeInt32(module, 110), temp16 = makeInt64(module, 111);
BinaryenExpressionRef externrefExpr = BinaryenRefNull(module, BinaryenTypeExternref());
BinaryenExpressionRef funcrefExpr = BinaryenRefNull(module, BinaryenTypeFuncref());
- funcrefExpr = BinaryenRefFunc(module, "kitchen()sinker");
+ funcrefExpr =
+ BinaryenRefFunc(module, "kitchen()sinker", BinaryenTypeFuncref());
BinaryenExpressionRef exnrefExpr = BinaryenRefNull(module, BinaryenTypeExnref());
BinaryenExpressionRef i31refExpr = BinaryenI31New(module, makeInt32(module, 1));
@@ -737,15 +738,16 @@ void test_core() {
BinaryenRefIsNull(module, externrefExpr),
BinaryenRefIsNull(module, funcrefExpr),
BinaryenRefIsNull(module, exnrefExpr),
- BinaryenSelect(module,
- temp10,
- BinaryenRefNull(module, BinaryenTypeFuncref()),
- BinaryenRefFunc(module, "kitchen()sinker"),
- BinaryenTypeFuncref()),
+ BinaryenSelect(
+ module,
+ temp10,
+ BinaryenRefNull(module, BinaryenTypeFuncref()),
+ BinaryenRefFunc(module, "kitchen()sinker", BinaryenTypeFuncref()),
+ BinaryenTypeFuncref()),
// GC
BinaryenRefEq(module,
- BinaryenRefNull(module, BinaryenTypeEqref()),
- BinaryenRefNull(module, BinaryenTypeEqref())),
+ BinaryenRefNull(module, BinaryenTypeEqref()),
+ BinaryenRefNull(module, BinaryenTypeEqref())),
// Exception handling
BinaryenTry(module, tryBody, catchBody),
// Atomics
diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt
index ab9548924..dbbb30eb6 100644
--- a/test/example/c-api-kitchen-sink.txt
+++ b/test/example/c-api-kitchen-sink.txt
@@ -25,7 +25,7 @@ BinaryenFeatureReferenceTypes: 256
BinaryenFeatureMultivalue: 512
BinaryenFeatureGC: 1024
BinaryenFeatureMemory64: 2048
-BinaryenFeatureAll: 4095
+BinaryenFeatureAll: 8191
(f32.neg
(f32.const -33.61199951171875)
)
diff --git a/test/passes/fuzz-exec_all-features.txt b/test/passes/fuzz-exec_all-features.txt
index cbdcf4fdb..22e342fc1 100644
--- a/test/passes/fuzz-exec_all-features.txt
+++ b/test/passes/fuzz-exec_all-features.txt
@@ -207,3 +207,15 @@
)
[fuzz-exec] calling rmw-reads-modifies-and-writes-asymmetrical
[LoggingExternalInterface logging 214]
+[fuzz-exec] calling func
+[fuzz-exec] note result: func => funcref(func)
+(module
+ (type $none_=>_funcref (func (result funcref)))
+ (export "func" (func $func))
+ (func $func (result funcref)
+ (ref.func $func)
+ )
+)
+[fuzz-exec] calling func
+[fuzz-exec] note result: func => funcref(func)
+[fuzz-exec] comparing func
diff --git a/test/passes/fuzz-exec_all-features.wast b/test/passes/fuzz-exec_all-features.wast
index 4e5c926a5..960990a33 100644
--- a/test/passes/fuzz-exec_all-features.wast
+++ b/test/passes/fuzz-exec_all-features.wast
@@ -121,3 +121,9 @@
)
)
)
+(module
+ (export "func" (func $func))
+ (func $func (result funcref)
+ (ref.func $func)
+ )
+)
diff --git a/test/passes/fuzz_metrics_noprint.bin.txt b/test/passes/fuzz_metrics_noprint.bin.txt
index ad654067f..9e9b6f4f8 100644
--- a/test/passes/fuzz_metrics_noprint.bin.txt
+++ b/test/passes/fuzz_metrics_noprint.bin.txt
@@ -1,30 +1,30 @@
total
[events] : 0
- [exports] : 35
- [funcs] : 47
+ [exports] : 51
+ [funcs] : 67
[globals] : 7
[imports] : 4
[memory-data] : 4
- [table-data] : 16
- [total] : 6048
- [vars] : 137
- binary : 458
- block : 871
- break : 230
- call : 240
- call_indirect : 52
- const : 1081
- drop : 40
- global.get : 480
- global.set : 204
- if : 356
- load : 108
- local.get : 527
- local.set : 351
- loop : 155
- nop : 120
- return : 233
- select : 51
- store : 58
- unary : 432
+ [table-data] : 18
+ [total] : 4870
+ [vars] : 236
+ binary : 368
+ block : 699
+ break : 191
+ call : 300
+ call_indirect : 39
+ const : 847
+ drop : 91
+ global.get : 403
+ global.set : 171
+ if : 260
+ load : 85
+ local.get : 374
+ local.set : 251
+ loop : 111
+ nop : 86
+ return : 187
+ select : 30
+ store : 36
+ unary : 340
unreachable : 1
diff --git a/test/passes/instrument-locals_all-features.txt b/test/passes/instrument-locals_all-features.txt
index 03a5743e5..d027f54a0 100644
--- a/test/passes/instrument-locals_all-features.txt
+++ b/test/passes/instrument-locals_all-features.txt
@@ -157,18 +157,14 @@
)
)
(local.set $F
- (call $set_funcref
- (i32.const 15)
- (i32.const 4)
- (ref.func $test)
- )
+ (ref.func $test)
)
(local.set $X
(call $set_externref
- (i32.const 17)
+ (i32.const 16)
(i32.const 5)
(call $get_externref
- (i32.const 16)
+ (i32.const 15)
(i32.const 5)
(local.get $X)
)
@@ -176,10 +172,10 @@
)
(local.set $E
(call $set_exnref
- (i32.const 19)
+ (i32.const 18)
(i32.const 6)
(call $get_exnref
- (i32.const 18)
+ (i32.const 17)
(i32.const 6)
(local.get $E)
)
@@ -187,7 +183,7 @@
)
(local.set $x
(call $set_i32
- (i32.const 20)
+ (i32.const 19)
(i32.const 0)
(i32.const 11)
)
@@ -197,24 +193,24 @@
)
(local.set $z
(call $set_f32
- (i32.const 21)
+ (i32.const 20)
(i32.const 2)
(f32.const 33.209999084472656)
)
)
(local.set $w
(call $set_f64
- (i32.const 22)
+ (i32.const 21)
(i32.const 3)
(f64.const 44.321)
)
)
(local.set $F
(call $set_funcref
- (i32.const 24)
+ (i32.const 23)
(i32.const 4)
(call $get_funcref
- (i32.const 23)
+ (i32.const 22)
(i32.const 4)
(local.get $F)
)
@@ -222,10 +218,10 @@
)
(local.set $X
(call $set_externref
- (i32.const 26)
+ (i32.const 25)
(i32.const 5)
(call $get_externref
- (i32.const 25)
+ (i32.const 24)
(i32.const 5)
(local.get $X)
)
@@ -233,10 +229,10 @@
)
(local.set $E
(call $set_exnref
- (i32.const 28)
+ (i32.const 27)
(i32.const 6)
(call $get_exnref
- (i32.const 27)
+ (i32.const 26)
(i32.const 6)
(local.get $E)
)
@@ -274,14 +270,14 @@
)
(drop
(call $get_v128
- (i32.const 29)
+ (i32.const 28)
(i32.const 7)
(local.get $S)
)
)
(local.set $S
(call $set_v128
- (i32.const 30)
+ (i32.const 29)
(i32.const 7)
(v128.const i32x4 0x00000000 0x00000001 0x00000002 0x00000003)
)
diff --git a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt
index 18cee797f..96cb9b785 100644
--- a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt
+++ b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt
@@ -10,6 +10,7 @@
--enable-multivalue
--enable-gc
--enable-memory64
+--enable-typed-function-references
(module
(type $none_=>_v128_externref (func (result v128 externref)))
(func $foo (result v128 externref)
diff --git a/test/passes/translate-to-fuzz_all-features.txt b/test/passes/translate-to-fuzz_all-features.txt
index 6614e2009..055f80e00 100644
--- a/test/passes/translate-to-fuzz_all-features.txt
+++ b/test/passes/translate-to-fuzz_all-features.txt
@@ -2,23 +2,12 @@
(type $none_=>_none (func))
(type $i32_=>_none (func (param i32)))
(type $i64_=>_none (func (param i64)))
+ (type $i64_i32_=>_none (func (param i64 i32)))
(type $f32_=>_none (func (param f32)))
(type $f64_=>_none (func (param f64)))
(type $v128_=>_none (func (param v128)))
- (type $funcref_=>_none (func (param funcref)))
(type $exnref_=>_none (func (param exnref)))
(type $none_=>_i32 (func (result i32)))
- (type $anyref_anyref_externref_eqref_v128_=>_i64 (func (param anyref anyref externref eqref v128) (result i64)))
- (type $eqref_externref_exnref_v128_exnref_=>_i64 (func (param eqref externref exnref v128 exnref) (result i64)))
- (type $exnref_=>_f32 (func (param exnref) (result f32)))
- (type $exnref_i64_funcref_f64_f64_=>_f32 (func (param exnref i64 funcref f64 f64) (result f32)))
- (type $none_=>_funcref_f32_exnref_exnref (func (result funcref f32 exnref exnref)))
- (type $funcref_funcref_=>_funcref_f32_exnref_exnref (func (param funcref funcref) (result funcref f32 exnref exnref)))
- (type $none_=>_externref (func (result externref)))
- (type $i31ref_v128_=>_exnref (func (param i31ref v128) (result exnref)))
- (type $funcref_v128_=>_eqref (func (param funcref v128) (result eqref)))
- (type $anyref_=>_eqref (func (param anyref) (result eqref)))
- (type $i31ref_externref_v128_f32_f32_=>_eqref (func (param i31ref externref v128 f32 f32) (result eqref)))
(import "fuzzing-support" "log-i32" (func $log-i32 (param i32)))
(import "fuzzing-support" "log-i64" (func $log-i64 (param i64)))
(import "fuzzing-support" "log-f32" (func $log-f32 (param f32)))
@@ -27,37 +16,16 @@
(import "fuzzing-support" "log-exnref" (func $log-exnref (param exnref)))
(memory $0 (shared 16 17))
(data (i32.const 0) "N\0fN\f5\f9\b1\ff\fa\eb\e5\fe\a7\ec\fb\fc\f4\a6\e4\ea\f0\ae\e3")
- (table $0 17 funcref)
- (elem (i32.const 0) $func_1 $func_1 $func_1 $func_5 $func_5 $func_6 $func_6 $func_6 $func_6 $func_6 $func_6 $func_6 $func_6 $func_6 $func_6 $func_8 $func_9)
+ (table $0 0 funcref)
(global $global$ (mut eqref) (ref.null eq))
- (global $global$_0 (mut i32) (i32.const 470177031))
- (global $global$_1 (mut f64) (f64.const 2147483647))
- (global $global$_2 (mut (eqref f32 eqref funcref funcref i64)) (tuple.make
- (ref.null eq)
- (f32.const -2147483648)
- (ref.null eq)
- (ref.null func)
- (ref.null func)
- (i64.const -32)
- ))
- (global $global$_3 (mut f32) (f32.const -32769))
+ (global $global$_0 (mut externref) (ref.null extern))
+ (global $global$_1 (mut v128) (v128.const i32x4 0xfffffff9 0x06071c48 0x3f800000 0xc3800ae1))
+ (global $global$_2 (mut eqref) (ref.null eq))
+ (global $global$_3 (mut f64) (f64.const 0))
(global $hangLimit (mut i32) (i32.const 10))
+ (event $event$ (attr 0) (param i64 i32))
(export "hashMemory" (func $hashMemory))
(export "memory" (memory $0))
- (export "func" (func $func))
- (export "func_invoker" (func $func_invoker))
- (export "func_1_invoker" (func $func_1_invoker))
- (export "func_2" (func $func_2))
- (export "func_2_invoker" (func $func_2_invoker))
- (export "func_3" (func $func_3))
- (export "func_3_invoker" (func $func_3_invoker))
- (export "func_4_invoker" (func $func_4_invoker))
- (export "func_5" (func $func_5))
- (export "func_5_invoker" (func $func_5_invoker))
- (export "func_6" (func $func_6))
- (export "func_6_invoker" (func $func_6_invoker))
- (export "func_7" (func $func_7))
- (export "func_8" (func $func_8))
(export "hangLimitInitializer" (func $hangLimitInitializer))
(func $hashMemory (result i32)
(local $0 i32)
@@ -67,7 +35,7 @@
(global.get $hangLimit)
)
(return
- (i32.const 69)
+ (i32.const 235407412)
)
)
(global.set $hangLimit
@@ -82,18 +50,7 @@
(i32.const 5381)
)
(local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u
- (i32.const 0)
- )
- )
+ (i32.const 1140933654)
)
(local.set $0
(i32.xor
@@ -102,109 +59,58 @@
(local.get $0)
(i32.const 5)
)
- (local.get $0)
+ (i32.const -127)
)
(i32.load8_u offset=1
(i32.const 0)
)
)
)
- (local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u offset=2
- (i32.const 0)
- )
- )
- )
- (local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u offset=3
- (i32.const 0)
- )
- )
- )
- (local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u offset=4
- (i32.const 0)
- )
- )
- )
- (local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u offset=5
- (i32.const 0)
- )
+ (f32.store offset=1 align=1
+ (i32.and
+ (i32.const -127)
+ (i32.const 15)
)
+ (f32.const -8)
)
- (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)
+ (if
+ (i32.eqz
+ (ref.is_null
+ (ref.null exn)
)
)
- )
- (local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
+ (block $label$1
+ (nop)
+ (br_if $label$1
+ (loop $label$2 (result i32)
+ (block
+ (if
+ (i32.eqz
+ (global.get $hangLimit)
+ )
+ (return
+ (i32.const 980647737)
+ )
+ )
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block $label$3 (result i32)
+ (i32.const -1)
+ )
)
- (local.get $0)
- )
- (i32.load8_u offset=7
- (i32.const 0)
)
)
- )
- (local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u offset=8
- (i32.const 0)
+ (block $label$4
+ (block $label$5
+ (nop)
+ (nop)
)
+ (nop)
)
)
(local.set $0
@@ -214,54 +120,231 @@
(local.get $0)
(i32.const 5)
)
- (local.get $0)
+ (i32.const 175316752)
)
- (i32.load8_u offset=9
+ (i32.load8_u offset=4
(i32.const 0)
)
)
)
- (local.set $0
+ (local.tee $0
(i32.xor
(i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
+ (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$8 (result i32)
+ (loop $label$9
+ (block
+ (if
+ (i32.eqz
+ (global.get $hangLimit)
+ )
+ (return
+ (i32.const 262143)
+ )
+ )
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block
+ (block $label$10
+ (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)
+ )
+ (loop $label$12 (result v128)
+ (block
+ (if
+ (i32.eqz
+ (global.get $hangLimit)
+ )
+ (return
+ (i32.const -13)
+ )
+ )
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (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)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ (i32.const 0)
+ )
+ )
+ (i32.const 15)
+ )
+ (i31.get_s
+ (block $label$16 (result i31ref)
+ (nop)
+ (i31.new
+ (i32.const -256)
+ )
+ )
+ )
+ )
+ (return
+ (i32.const -9)
+ )
+ )
+ (block $label$17
+ (return
+ (i32.const 2376257)
+ )
+ )
)
(local.get $0)
)
- (i32.load8_u offset=10
+ (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.load8_u offset=11
- (i32.const 0)
+ (tuple.extract 0
+ (tuple.make
+ (i32.const 262145)
+ (i32.const 110)
)
)
)
(local.set $0
- (i32.xor
- (i32.add
- (i32.shl
- (local.get $0)
- (i32.const 5)
- )
- (local.get $0)
- )
- (i32.load8_u offset=12
- (i32.const 0)
- )
- )
+ (call $hashMemory)
)
(local.set $0
(i32.xor
@@ -272,7 +355,7 @@
)
(local.get $0)
)
- (i32.load8_u offset=13
+ (i32.load8_u offset=6
(i32.const 0)
)
)
@@ -284,380 +367,283 @@
(local.get $0)
(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.xor
+ (i32.add
+ (i32.shl
+ (local.get $0)
+ (i32.const 5)
+ )
+ (local.get $0)
+ )
+ (i32.load8_u offset=8
+ (i32.const 0)
+ )
)
- (local.get $0)
)
- (i32.load8_u offset=15
+ (i32.load8_u offset=9
(i32.const 0)
)
)
)
- (local.get $0)
- )
- )
- (func $func (param $0 i31ref) (param $1 v128) (result exnref)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (ref.null exn)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (ref.null exn)
- )
- (func $func_invoker
- (drop
- (call $func
- (i31.new
- (i32.const 1633240409)
- )
- (v128.const i32x4 0xcf800000 0x4c816020 0x3e1a1a1a 0x00000000)
- )
- )
- )
- (func $func_0 (param $0 anyref) (param $1 anyref) (param $2 externref) (param $3 eqref) (param $4 v128) (result i64)
- (local $5 eqref)
- (local $6 (funcref externref i32 f64 f32 i64))
- (local $7 (f32 funcref eqref i32 i31ref i32))
- (local $8 v128)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (i64.const 2147483647)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (block $label$0 (result i64)
- (nop)
- (br_if $label$0
- (tuple.extract 1
- (tuple.make
- (f64.const -255)
- (i64.const -127)
- (ref.null func)
- )
- )
- (i32.const -24)
- )
- )
- )
- (func $func_1 (param $0 i31ref) (param $1 externref) (param $2 v128) (param $3 f32) (param $4 f32) (result eqref)
- (local $5 v128)
- (local $6 (exnref externref i64 i32 f64 funcref))
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (ref.null eq)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (ref.null eq)
- )
- (func $func_1_invoker
- (drop
- (call $func_1
- (i31.new
- (i32.const 16777216)
- )
- (ref.null extern)
- (v128.const i32x4 0x5d455846 0xcf800000 0x42aa0000 0x46b2a800)
- (f32.const -17592186044416)
- (f32.const -4294967296)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- (drop
- (call $func_1
- (i31.new
- (i32.const 73)
- )
- (ref.null extern)
- (v128.const i32x4 0xffff8001 0xffffffff 0x184c764c 0x10105676)
- (f32.const -9223372036854775808)
- (f32.const 129)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- (drop
- (call $func_1
- (i31.new
- (i32.const -4096)
- )
- (ref.null extern)
- (v128.const i32x4 0x00000000 0x00000020 0x00000001 0xffc00000)
- (f32.const -nan:0x7fffbd)
- (f32.const -255.8040008544922)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- (drop
- (call $func_1
- (i31.new
- (i32.const 1936946035)
- )
- (ref.null extern)
- (v128.const i32x4 0xff010001 0x4e07ffff 0x00060002 0xff00001f)
- (f32.const 6918)
- (f32.const 4.921484278772694e-25)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- )
- (func $func_2 (param $0 funcref)
- (local $1 exnref)
- (local $2 (f64 externref f32 f32 v128 eqref))
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return)
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (block $label$0
- (f32.store offset=3 align=2
- (i32.and
- (i32.const 15)
- (i32.const 15)
- )
- (f32.const 66)
- )
- (return)
- )
- )
- (func $func_2_invoker
- (call $func_2
- (ref.null func)
- )
- )
- (func $func_3 (param $0 exnref) (result f32)
- (local $1 i31ref)
- (local $2 i31ref)
- (local $3 (i32 externref f64 i32))
- (local $4 externref)
- (local $5 (f32 funcref))
- (local $6 i31ref)
- (local $7 i31ref)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (f32.const -nan:0x7fffa6)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (block $label$0
- (loop $label$1
- (block
- (if
+ (block $label$23
+ (if
+ (if (result i32)
(i32.eqz
- (global.get $hangLimit)
- )
- (return
- (f32.const 0.4049999713897705)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (block
- (block $label$2
- (i32.store offset=4 align=1
- (i32.and
- (block $label$32 (result i32)
- (block $label$33
- (nop)
- (if
- (i32.eqz
- (i32.const -1)
- )
- (nop)
- (nop)
- )
- )
- (loop $label$34 (result i32)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
+ (i32.const 218235656)
+ )
+ (block $label$24
+ (global.set $global$_3
+ (f64.add
+ (f64.mul
+ (loop $label$25 (result f64)
+ (block
+ (if
+ (i32.eqz
+ (global.get $hangLimit)
+ )
+ (return
+ (i32.const 56235029)
+ )
)
- (return
- (f32.const 1048576)
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
)
)
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
+ (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)
)
)
- (call $hashMemory)
+ (f64.const 0.878)
+ )
+ (block $label$27
+ (nop)
+ (br $label$23)
)
- )
- (i32.const 15)
- )
- (i32.load8_u offset=22
- (i32.and
- (i32.const -14)
- (i32.const 15)
)
)
+ (br $label$23)
)
- (nop)
- )
- (br_if $label$1
- (i32.const 33554431)
- )
- (br_if $label$1
- (i32.const 134217729)
- )
- )
- )
- (if
- (i32.eqz
- (i32.const -65535)
- )
- (block $label$4
- (global.set $global$_1
- (block $label$23
- (nop)
- (return
- (f32.const -4503599627370496)
- )
+ (select
+ (call $hashMemory)
+ (i32.const -2147483647)
+ (i32.const 5909)
)
)
- (call $log-i32
- (call $hashMemory)
- )
- )
- (loop $label$6
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (f32.const 2147483648)
+ (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)
+ )
+ )
)
- )
- (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)
)
- (if
- (i32.eqz
- (i16x8.extract_lane_s 7
- (v128.const i32x4 0x1c5e4449 0x4644c000 0xcf800000 0x477fd300)
- )
- )
- (if
- (i32.const -127)
- (block $label$7
- (memory.copy
- (i32.const 32768)
- (i32.and
- (i32.const 32768)
- (i32.const 15)
+ )
+ (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)
)
- (global.get $global$_0)
)
- (nop)
)
- (block $label$8
- (call $log-i32
- (call $hashMemory)
+ (block (result i32)
+ (if
+ (ref.is_null
+ (block $label$37
+ (nop)
+ (br $label$36)
+ )
+ )
+ (nop)
+ (nop)
)
- (i32.atomic.store8 offset=4
- (i32.and
- (i32.const -70)
- (i32.const 15)
+ (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
+ (br_if $label$39
+ (i32.eqz
+ (i32.const -58)
+ )
+ )
+ (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$41
+ (if
+ (i32.const 65534)
+ (block $label$42
+ (nop)
+ (br_if $label$39
+ (i32.eqz
+ (i32.const -61)
+ )
+ )
+ )
+ (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)
+ )
+ (return
+ (i32.const -9)
+ )
+ )
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (i16x8.extract_lane_s 2
+ (v128.const i32x4 0x05030804 0x1e070503 0x80000001 0xffffffff)
+ )
+ )
+ (memory.atomic.notify offset=22
+ (i32.and
+ (i32.atomic.load offset=22
+ (i32.and
+ (i32.const 1499338050)
+ (i32.const 15)
+ )
+ )
+ (i32.const 15)
+ )
+ (i32.const -2)
+ )
+ )
+ (i32.const 15)
+ )
+ (f32.const -9223372036854775808)
+ )
+ )
+ )
+ (i32.const -2147483648)
+ )
)
- (i32.const 3342)
+ )
+ (select
+ (i32.const -127)
+ (i32.const 8)
+ (i32.const -255)
)
)
)
- (block $label$9
- (global.set $global$_0
- (i32.const 0)
- )
- (loop $label$14
+ (i32.const 608321884)
+ )
+ (i32.and
+ (i32.const 72)
+ (i64.eqz
+ (loop $label$45 (result i64)
(block
(if
(i32.eqz
(global.get $hangLimit)
)
(return
- (f32.const -0)
+ (i32.const 2131838335)
)
)
(global.set $hangLimit
@@ -667,371 +653,326 @@
)
)
)
- (block
- (block $label$15
+ (block (result i64)
+ (block $label$46
(nop)
- (f64.store offset=4
- (i32.and
- (i16x8.extract_lane_s 3
- (v128.const i32x4 0x06143637 0x1df25a02 0x00304800 0x713ecf76)
- )
- (i32.const 15)
- )
- (global.get $global$_1)
- )
)
- (br_if $label$14
- (i31.get_s
- (local.get $1)
- )
- )
- (call $log-f64
- (f64.div
- (f64.load offset=2 align=2
- (i32.and
- (i32.const -1)
- (i32.const 15)
- )
- )
- (f64.load offset=2 align=2
- (i32.and
- (call $hashMemory)
- (i32.const 15)
+ (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
+ (if
+ (i32.eqz
+ (global.get $hangLimit)
+ )
+ (return
+ (i32.const 55645307)
+ )
+ )
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block (result i32)
+ (br_if $label$48
+ (i32.eqz
+ (i32.const 4374)
+ )
+ )
+ (br_if $label$48
+ (i32.const 791226157)
+ )
+ (if (result i32)
+ (i32.eqz
+ (i32.const 270077278)
+ )
+ (i32.const -1)
+ (i32.const 96)
+ )
+ )
+ )
+ )
+ (block $label$49
+ (nop)
+ (br $label$45)
+ )
+ (block $label$50 (result i32)
+ (loop $label$51
+ (block
+ (if
+ (i32.eqz
+ (global.get $hangLimit)
+ )
+ (return
+ (i32.const 44)
+ )
+ )
+ (global.set $hangLimit
+ (i32.sub
+ (global.get $hangLimit)
+ (i32.const 1)
+ )
+ )
+ )
+ (block
+ (nop)
+ (br_if $label$51
+ (i32.eqz
+ (i32.const 65507)
+ )
+ )
+ (br_if $label$51
+ (i32.const 1413679622)
+ )
+ )
+ )
+ (f64.eq
+ (f64.const -4095.596)
+ (f64.const -54)
+ )
+ )
+ )
+ (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)
+ )
+ )
+ )
+ )
+ (block $label$52
+ (if
+ (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)
+ )
+ )
+ )
+ (block $label$54
+ (drop
+ (ref.null exn)
+ )
+ (nop)
+ )
+ (block $label$55
+ (data.drop 0)
+ (nop)
+ )
+ )
+ (br $label$45)
+ )
+ )
+ (call $hashMemory)
+ )
+ )
)
+ (br $label$45)
)
)
)
+ (i64.const 5064094673136993862)
)
)
)
)
)
)
- (return
- (f32.const 4095.84912109375)
- )
- )
- )
- (func $func_3_invoker
- (drop
- (call $func_3
- (ref.null exn)
- )
- )
- (drop
- (call $func_3
- (ref.null exn)
- )
- )
- (drop
- (call $func_3
- (ref.null exn)
- )
- )
- )
- (func $func_4 (param $0 funcref) (param $1 funcref) (result funcref f32 exnref exnref)
- (local $2 f64)
- (local $3 externref)
- (local $4 f32)
- (local $5 (funcref eqref anyref i32 exnref))
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (tuple.make
- (ref.null func)
- (f32.const 4294967296)
- (ref.null exn)
- (ref.null exn)
- )
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (tuple.make
- (local.get $0)
- (f32.const 2147483648)
- (ref.null exn)
- (ref.null exn)
- )
- )
- (func $func_4_invoker
- (drop
- (call $func_4
- (ref.null func)
- (ref.null func)
- )
- )
- (drop
- (call $func_4
- (ref.null func)
- (ref.null func)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- (drop
- (call $func_4
- (ref.null func)
- (ref.null func)
- )
- )
- (drop
- (call $func_4
- (ref.null func)
- (ref.null func)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- )
- (func $func_5 (param $0 anyref) (result eqref)
- (local $1 (anyref anyref i32 f64 exnref f64))
- (local $2 f64)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (ref.null eq)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (return
- (ref.null eq)
- )
- )
- (func $func_5_invoker
- (drop
- (call $func_5
- (ref.null any)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- (drop
- (call $func_5
- (ref.null any)
- )
- )
- (call $log-i32
- (call $hashMemory)
- )
- )
- (func $func_6 (param $0 funcref) (param $1 v128) (result eqref)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (ref.null eq)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (block $label$0
- (block $label$1
- (if
- (i32.const 0)
- (block $label$2
- (nop)
- (nop)
- )
- (block $label$3
- (nop)
- (i32.atomic.store8 offset=3
- (i32.const 826952521)
- (i31.get_u
- (i31.new
- (i32.const 2147483647)
- )
- )
+ (local.set $0
+ (i32.xor
+ (i32.add
+ (i32.shl
+ (local.get $0)
+ (i32.const 5)
)
- )
- )
- (block $label$4
- (call $log-i32
- (call $hashMemory)
- )
- (local.set $0
(local.get $0)
)
- )
- )
- (return
- (ref.null eq)
- )
- )
- )
- (func $func_6_invoker
- (drop
- (call $func_6
- (ref.null func)
- (v128.const i32x4 0x0e165b0c 0x5b120b08 0x005353f8 0x41d00000)
- )
- )
- )
- (func $func_7 (param $0 exnref) (param $1 i64) (param $2 funcref) (param $3 f64) (param $4 f64) (result f32)
- (local $5 funcref)
- (local $6 (f64 f64 f64))
- (local $7 exnref)
- (local $8 f64)
- (local $9 eqref)
- (local $10 i64)
- (local $11 v128)
- (local $12 i32)
- (local $13 (i31ref v128 f32 i64 exnref))
- (local $14 anyref)
- (local $15 (externref eqref externref externref eqref))
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (f32.const 43)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (select
- (loop $label$0 (result f32)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (f32.const 18446744073709551615)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
+ (i32.shl
+ (local.get $0)
+ (i32.const 5)
)
)
- (f32.const -nan:0x7fffeb)
)
- (f32.const 18526)
- (i32.const -50)
- )
- )
- (func $func_8 (result externref)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (ref.null extern)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (block $label$0 (result externref)
- (call $log-i32
- (call $hashMemory)
- )
- (ref.null extern)
- )
- )
- (func $func_9 (param $0 eqref) (param $1 externref) (param $2 exnref) (param $3 v128) (param $4 exnref) (result i64)
- (local $5 f32)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (i64.const -18014398509481983)
- )
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
- )
- )
- )
- (loop $label$0 (result i64)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (i64.const 7709)
+ (nop)
+ (local.set $0
+ (i32.xor
+ (i32.add
+ (i32.const 5)
+ (local.get $0)
)
- )
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
+ (i32.load8_u offset=14
+ (i32.const 0)
)
)
)
- (block $label$1 (result i64)
- (br_if $label$0
- (i32.const 707800420)
- )
- (loop $label$2 (result i64)
- (block
- (if
- (i32.eqz
- (global.get $hangLimit)
- )
- (return
- (i64.const 2305843009213693952)
+ (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)
+ )
+ )
)
+ (i32.const 15)
)
- (global.set $hangLimit
- (i32.sub
- (global.get $hangLimit)
- (i32.const 1)
+ (i32.const 235020035)
+ )
+ )
+ (i64.atomic.store32 offset=22
+ (i32.and
+ (memory.atomic.notify offset=22
+ (i32.and
+ (i32.const 255)
+ (i32.const 15)
)
+ (i32.const -255)
)
+ (i32.const 15)
)
- (block $label$3 (result i64)
- (data.drop 0)
- (i64.const -116)
- )
+ (i64.const 33554432)
)
)
+ (local.get $0)
)
)
(func $hangLimitInitializer
diff --git a/test/unit/test_features.py b/test/unit/test_features.py
index a44d58bbd..889b149b1 100644
--- a/test/unit/test_features.py
+++ b/test/unit/test_features.py
@@ -408,5 +408,6 @@ class TargetFeaturesSectionTest(utils.BinaryenTestCase):
'--enable-reference-types',
'--enable-multivalue',
'--enable-gc',
- '--enable-memory64'
+ '--enable-memory64',
+ '--enable-typed-function-references',
], p2.stdout.splitlines())