summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2024-11-19 15:26:09 -0800
committerGitHub <noreply@github.com>2024-11-19 15:26:09 -0800
commite13bf0fb72fca160f457570b930c4ba3c35ead3a (patch)
treed840d824a6da6213cb9e27051cda3afd138863ed
parent206ad2906c9e0af92ec4c4da223c96755243aa2e (diff)
downloadbinaryen-e13bf0fb72fca160f457570b930c4ba3c35ead3a.tar.gz
binaryen-e13bf0fb72fca160f457570b930c4ba3c35ead3a.tar.bz2
binaryen-e13bf0fb72fca160f457570b930c4ba3c35ead3a.zip
Improve fuzzing of both closed and open world styles of modules (#7090)
Before, we would simply not export a function that had an e.g. anyref param. As a result, the modules were effectively "closed", which was good for testing full closed-world mode, but not for testing degrees of open world. To improve that, this PR allows the fuzzer to export such functions, and an "enclose world" pass is added that "closes" the wasm (makes it more compatible with closed-world) that is run 50% of the time, giving us coverage of both styles.
-rwxr-xr-xscripts/fuzz_opt.py6
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/EncloseWorld.cpp155
-rw-r--r--src/passes/pass.cpp3
-rw-r--r--src/passes/passes.h1
-rw-r--r--src/tools/fuzzing/fuzzing.cpp37
-rw-r--r--src/tools/wasm-reduce.cpp1
-rw-r--r--test/lit/help/wasm-metadce.test3
-rw-r--r--test/lit/help/wasm-opt.test3
-rw-r--r--test/lit/help/wasm2js.test3
-rw-r--r--test/lit/passes/enclose-world.wast237
-rw-r--r--test/passes/fuzz_metrics_passes_noprint.bin.txt58
-rw-r--r--test/passes/translate-to-fuzz_all-features_metrics_noprint.txt72
13 files changed, 497 insertions, 83 deletions
diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py
index cd583e026..4fc92f367 100755
--- a/scripts/fuzz_opt.py
+++ b/scripts/fuzz_opt.py
@@ -225,6 +225,11 @@ def randomize_fuzz_settings():
# optimizations we use to create any other wasm file.
FUZZ_OPTS += ['--dce']
+ # Enclose the world much of the time when fuzzing closed-world, so that many
+ # types are private and hence optimizable.
+ if CLOSED_WORLD and random.random() < 0.5:
+ GEN_ARGS += ['--enclose-world']
+
print('randomized settings (NaNs, OOB, legalize):', NANS, OOB, LEGALIZE)
@@ -1790,6 +1795,7 @@ opt_choices = [
("--dce",),
("--directize",),
("--discard-global-effects",),
+ ("--enclose-world",),
("--flatten", "--dfo",),
("--duplicate-function-elimination",),
("--flatten",),
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index e46163406..6b78e487d 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -35,6 +35,7 @@ set(passes_SOURCES
DuplicateImportElimination.cpp
DuplicateFunctionElimination.cpp
DWARF.cpp
+ EncloseWorld.cpp
ExtractFunction.cpp
Flatten.cpp
FuncCastEmulation.cpp
diff --git a/src/passes/EncloseWorld.cpp b/src/passes/EncloseWorld.cpp
new file mode 100644
index 000000000..5c6b70546
--- /dev/null
+++ b/src/passes/EncloseWorld.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2024 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// "Closes" the world, in the sense of making it more compatible with the
+// --closed-world flag, in a potentially destructive manner. This is mainly
+// useful for fuzzing (in that a random module is usually very incomptable with
+// closed world, with most types being public and hence unoptimizable, but
+// running this pass makes as many as we can fully private).
+//
+// The fixup we do is to find references sent out/received in, and to
+// externalize / internalize them. For example, this export:
+//
+// (func $refs (export "refs") (param $x (ref $X)) (result (ref $Y))
+//
+// would have the following function exported in its place:
+//
+// (func $refs-closed (export "refs") (param $x externref) (result externref)
+// (extern.convert_any
+// (call $refs
+// (ref.cast (ref $X)
+// (any.convert_extern
+// (local.get $x))))))
+//
+
+#include "ir/names.h"
+#include "pass.h"
+#include "wasm-builder.h"
+#include "wasm.h"
+
+namespace wasm {
+
+namespace {
+
+struct EncloseWorld : public Pass {
+ void run(Module* module) override {
+ // Handle exports.
+ // TODO: Non-function exports.
+ std::vector<std::unique_ptr<Export>> newExports;
+ for (auto& ex : module->exports) {
+ if (ex->kind == ExternalKind::Function) {
+ auto* func = module->getFunction(ex->value);
+ // If this opens up types, replace it with an enclosed stub.
+ if (opensTypes(func)) {
+ auto stubName = makeStubStubForExport(func, module);
+ ex->value = stubName;
+ }
+ }
+ }
+ for (auto& ex : newExports) {
+ module->addExport(std::move(ex));
+ }
+
+ // TODO: Handle imports.
+ }
+
+private:
+ // Whether a type is an "open" ref, that is, a type that closed-world would
+ // consider to keep things public and prevent some amount of closed-world
+ // optimizations.
+ bool isOpenRef(Type t) {
+ // Only externref keeps things closed, and we must ignore things that
+ // cannot be converted to/from it (like funcrefs), so we can just check for
+ // the top type being any.
+ return t.isRef() && t.getHeapType().getTop() == HeapType::any;
+ }
+
+ // Whether a function causes types to be open.
+ bool opensTypes(Function* func) {
+ for (const auto& param : func->getParams()) {
+ if (isOpenRef(param)) {
+ return true;
+ }
+ }
+ // TODO: Handle tuple results.
+ return isOpenRef(func->getResults());
+ }
+
+ // Make an enclosed stub function for an exported function, and return its
+ // name.
+ Name makeStubStubForExport(Function* func, Module* module) {
+ // Pick a valid name for the stub we are about to create.
+ auto stubName = Names::getValidFunctionName(
+ *module, std::string("stub$") + func->name.toString());
+
+ // Create the stub.
+ Builder builder(*module);
+
+ // The stub's body is just a call to the original function, but with some
+ // conversions to/from externref.
+ std::vector<Expression*> params;
+
+ auto externref = Type(HeapType::ext, Nullable);
+
+ // Handle params.
+ std::vector<Type> stubParams;
+ for (const auto& param : func->getParams()) {
+ if (!isOpenRef(param)) {
+ // A normal parameter. Just pass it to the original function.
+ auto* get = builder.makeLocalGet(stubParams.size(), param);
+ params.push_back(get);
+ stubParams.push_back(param);
+ } else {
+ // A type we must fix up: receive as an externref and then internalize
+ // and cast before sending to the original function.
+ auto* get = builder.makeLocalGet(stubParams.size(), externref);
+ auto* interned = builder.makeRefAs(AnyConvertExtern, get);
+ // This cast may be trivial, but we leave it to the optimizer to remove.
+ auto* cast = builder.makeRefCast(interned, param);
+ params.push_back(cast);
+ stubParams.push_back(externref);
+ }
+ }
+
+ auto* call = builder.makeCall(func->name, params, func->getResults());
+
+ // Generate the stub's type.
+ auto oldResults = func->getResults();
+ Type resultsType = isOpenRef(oldResults) ? externref : oldResults;
+ auto type = Signature(Type(stubParams), resultsType);
+
+ // Handle the results and make the body.
+ Expression* body;
+ if (!isOpenRef(oldResults)) {
+ // Just use the call.
+ body = call;
+ } else {
+ // Fix up the call's result.
+ body = builder.makeRefAs(ExternConvertAny, call);
+ }
+
+ module->addFunction(builder.makeFunction(stubName, type, {}, body));
+
+ return stubName;
+ }
+};
+
+} // anonymous namespace
+
+Pass* createEncloseWorldPass() { return new EncloseWorld(); }
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index 4be24cebf..7f6985e0f 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -159,6 +159,9 @@ void PassRegistry::registerPasses() {
registerPass("emit-target-features",
"emit the target features section in the output",
createEmitTargetFeaturesPass);
+ registerPass("enclose-world",
+ "modify the wasm (destructively) for closed-world",
+ createEncloseWorldPass);
registerPass("extract-function",
"leaves just one function (useful for debugging)",
createExtractFunctionPass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index aadd26d41..b313b3431 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -46,6 +46,7 @@ Pass* createDWARFDumpPass();
Pass* createDuplicateImportEliminationPass();
Pass* createDuplicateFunctionEliminationPass();
Pass* createEmitTargetFeaturesPass();
+Pass* createEncloseWorldPass();
Pass* createExtractFunctionPass();
Pass* createExtractFunctionIndexPass();
Pass* createFlattenPass();
diff --git a/src/tools/fuzzing/fuzzing.cpp b/src/tools/fuzzing/fuzzing.cpp
index ed653ef6b..135e50393 100644
--- a/src/tools/fuzzing/fuzzing.cpp
+++ b/src/tools/fuzzing/fuzzing.cpp
@@ -62,6 +62,17 @@ void TranslateToFuzzReader::pickPasses(OptimizationOptions& options) {
// things like ClusterFuzz, where we are using Binaryen to fuzz other things
// than itself). As a result, the list of passes here is different from
// fuzz_opt.py.
+
+ // Enclose the world, some of the time. We do this before picking any other
+ // passes so that we make the initial fuzz contents more optimizable by
+ // closed-world passes later. Note that we do this regardless of whether we
+ // are in closed-world mode or not, as it is good to get this variety
+ // regardless.
+ if (oneIn(2)) {
+ options.passes.push_back("enclose-world");
+ }
+
+ // Main selection of passes.
while (options.passes.size() < 20 && !random.finished() && !oneIn(3)) {
switch (upTo(42)) {
case 0:
@@ -1075,30 +1086,14 @@ Function* TranslateToFuzzReader::addFunction() {
// Add hang limit checks after all other operations on the function body.
wasm.addFunction(std::move(allocation));
// Export some functions, but not all (to allow inlining etc.). Try to export
- // at least one, though, to keep each testcase interesting. Only functions
- // with valid params and returns can be exported because the trap fuzzer
- // depends on that (TODO: fix this).
- auto validExportType = [](Type t) {
- if (!t.isRef()) {
- return true;
- }
- auto heapType = t.getHeapType();
- return heapType == HeapType::ext || heapType == HeapType::func ||
- heapType == HeapType::string;
- };
+ // at least one, though, to keep each testcase interesting. Avoid non-
+ // nullable params, as those cannot be constructed by the fuzzer on the
+ // outside.
bool validExportParams =
std::all_of(paramType.begin(), paramType.end(), [&](Type t) {
- return validExportType(t) && t.isDefaultable();
+ return t.isDefaultable();
});
- // Note: spec discussions around JS API integration are still ongoing, and it
- // is not clear if we should allow nondefaultable types in exports or not
- // (in imports, we cannot allow them in the fuzzer anyhow, since it can't
- // construct such values in JS to send over to the wasm from the fuzzer
- // harness).
- bool validExportResults =
- std::all_of(resultType.begin(), resultType.end(), validExportType);
- if (validExportParams && validExportResults &&
- (numAddedFunctions == 0 || oneIn(2)) &&
+ if (validExportParams && (numAddedFunctions == 0 || oneIn(2)) &&
!wasm.getExportOrNull(func->name)) {
auto* export_ = new Export;
export_->name = func->name;
diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp
index 8d9858b78..026825118 100644
--- a/src/tools/wasm-reduce.cpp
+++ b/src/tools/wasm-reduce.cpp
@@ -275,6 +275,7 @@ struct Reducer
"--dae-optimizing",
"--dce",
"--duplicate-function-elimination",
+ "--enclose-world",
"--gto",
"--inlining",
"--inlining-optimizing",
diff --git a/test/lit/help/wasm-metadce.test b/test/lit/help/wasm-metadce.test
index 4dc03883a..e44e51a16 100644
--- a/test/lit/help/wasm-metadce.test
+++ b/test/lit/help/wasm-metadce.test
@@ -143,6 +143,9 @@
;; CHECK-NEXT: --emit-target-features emit the target features section
;; CHECK-NEXT: in the output
;; CHECK-NEXT:
+;; CHECK-NEXT: --enclose-world modify the wasm (destructively)
+;; CHECK-NEXT: for closed-world
+;; CHECK-NEXT:
;; CHECK-NEXT: --extract-function leaves just one function (useful
;; CHECK-NEXT: for debugging)
;; CHECK-NEXT:
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index 62a30a770..5c978ee81 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -152,6 +152,9 @@
;; CHECK-NEXT: --emit-target-features emit the target features section
;; CHECK-NEXT: in the output
;; CHECK-NEXT:
+;; CHECK-NEXT: --enclose-world modify the wasm (destructively)
+;; CHECK-NEXT: for closed-world
+;; CHECK-NEXT:
;; CHECK-NEXT: --extract-function leaves just one function (useful
;; CHECK-NEXT: for debugging)
;; CHECK-NEXT:
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index e2a450bc8..c1dd0401d 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -106,6 +106,9 @@
;; CHECK-NEXT: --emit-target-features emit the target features section
;; CHECK-NEXT: in the output
;; CHECK-NEXT:
+;; CHECK-NEXT: --enclose-world modify the wasm (destructively)
+;; CHECK-NEXT: for closed-world
+;; CHECK-NEXT:
;; CHECK-NEXT: --extract-function leaves just one function (useful
;; CHECK-NEXT: for debugging)
;; CHECK-NEXT:
diff --git a/test/lit/passes/enclose-world.wast b/test/lit/passes/enclose-world.wast
new file mode 100644
index 000000000..0077c27be
--- /dev/null
+++ b/test/lit/passes/enclose-world.wast
@@ -0,0 +1,237 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt --enclose-world -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (type $A (struct))
+ (type $A (struct))
+ ;; CHECK: (type $B (struct (field i32)))
+ (type $B (struct (field i32)))
+ ;; CHECK: (type $2 (func (param externref)))
+
+ ;; CHECK: (type $3 (func (result externref)))
+
+ ;; CHECK: (type $4 (func (param anyref) (result anyref)))
+
+ ;; CHECK: (type $5 (func (param externref) (result externref)))
+
+ ;; CHECK: (type $C (struct (field i32) (field f64)))
+ (type $C (struct (field i32 f64)))
+
+ ;; CHECK: (type $7 (func (param i32) (result f64)))
+
+ ;; CHECK: (type $8 (func (param anyref)))
+
+ ;; CHECK: (type $9 (func (result anyref)))
+
+ ;; CHECK: (type $10 (func (param (ref $A) (ref null $B)) (result (ref $C))))
+
+ ;; CHECK: (type $11 (func (param i32 (ref $A) funcref)))
+
+ ;; CHECK: (type $12 (func (param externref externref) (result externref)))
+
+ ;; CHECK: (type $13 (func (param i32 externref funcref)))
+
+ ;; CHECK: (export "normal" (func $normal))
+
+ ;; CHECK: (export "externref-param" (func $externref-param))
+
+ ;; CHECK: (export "externref-result" (func $externref-result))
+
+ ;; CHECK: (export "anyref-param" (func $stub$anyref-param))
+
+ ;; CHECK: (export "anyref-result" (func $stub$anyref-result))
+
+ ;; CHECK: (export "anyref-both" (func $stub$anyref-both))
+
+ ;; CHECK: (export "anyref-both-dupe" (func $stub$anyref-both-dupe))
+
+ ;; CHECK: (export "many" (func $stub$many))
+
+ ;; CHECK: (export "mixed" (func $stub$mixed))
+
+ ;; CHECK: (func $normal (type $7) (param $x i32) (result f64)
+ ;; CHECK-NEXT: (f64.const 3.14159)
+ ;; CHECK-NEXT: )
+ (func $normal (export "normal") (param $x i32) (result f64)
+ ;; A normal function which we do not need to do anything with.
+ (f64.const 3.14159)
+ )
+
+ ;; CHECK: (func $externref-param (type $2) (param $x externref)
+ ;; CHECK-NEXT: )
+ (func $externref-param (export "externref-param") (param $x externref)
+ ;; An externref param is fine, we don't need to do anything.
+ )
+
+ ;; CHECK: (func $externref-result (type $3) (result externref)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $externref-result (export "externref-result") (result externref)
+ ;; An externref result is also fine.
+ (unreachable)
+ )
+
+ ;; CHECK: (func $anyref-param (type $8) (param $x anyref)
+ ;; CHECK-NEXT: )
+ (func $anyref-param (export "anyref-param") (param $x anyref)
+ ;; An anyref requires a fixup. We will call this from a stub that has an
+ ;; externref param, which calls this.
+ )
+
+ ;; CHECK: (func $anyref-result (type $9) (result anyref)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $anyref-result (export "anyref-result") (result anyref)
+ ;; An anyref result also requires a fixup.
+ (unreachable)
+ )
+
+ ;; CHECK: (func $anyref-both (type $4) (param $x anyref) (result anyref)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $anyref-both (export "anyref-both") (param $x anyref) (result anyref)
+ ;; Here we must fix up both the param and the result.
+ (unreachable)
+ )
+
+ ;; CHECK: (func $anyref-both-dupe (type $4) (param $x anyref) (result anyref)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $anyref-both-dupe (export "anyref-both-dupe") (param $x anyref) (result anyref)
+ ;; Identical to the above function, and should be fixed up in the same
+ ;; manner. In theory we could use the same stub for both, but we leave that
+ ;; for the optimizer.
+ (unreachable)
+ )
+
+ ;; CHECK: (func $many (type $10) (param $a (ref $A)) (param $b (ref null $B)) (result (ref $C))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $many (export "many") (param $a (ref $A)) (param $b (ref null $B)) (result (ref $C))
+ ;; Various declared types are used, and must be fixed up.
+ (unreachable)
+ )
+
+ ;; CHECK: (func $mixed (type $11) (param $a i32) (param $b (ref $A)) (param $c funcref)
+ ;; CHECK-NEXT: )
+ (func $mixed (export "mixed") (param $a i32) (param $b (ref $A)) (param $c funcref)
+ ;; One param needs to be fixed, two others do not: we can ignore i32 and
+ ;; funcref.
+ )
+)
+
+;; CHECK: (func $stub$anyref-param (type $2) (param $0 externref)
+;; CHECK-NEXT: (call $anyref-param
+;; CHECK-NEXT: (ref.cast anyref
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $stub$anyref-result (type $3) (result externref)
+;; CHECK-NEXT: (extern.convert_any
+;; CHECK-NEXT: (call $anyref-result)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $stub$anyref-both (type $5) (param $0 externref) (result externref)
+;; CHECK-NEXT: (extern.convert_any
+;; CHECK-NEXT: (call $anyref-both
+;; CHECK-NEXT: (ref.cast anyref
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $stub$anyref-both-dupe (type $5) (param $0 externref) (result externref)
+;; CHECK-NEXT: (extern.convert_any
+;; CHECK-NEXT: (call $anyref-both-dupe
+;; CHECK-NEXT: (ref.cast anyref
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $stub$many (type $12) (param $0 externref) (param $1 externref) (result externref)
+;; CHECK-NEXT: (extern.convert_any
+;; CHECK-NEXT: (call $many
+;; CHECK-NEXT: (ref.cast (ref $A)
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (ref.cast (ref null $B)
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $stub$mixed (type $13) (param $0 i32) (param $1 externref) (param $2 funcref)
+;; CHECK-NEXT: (call $mixed
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: (ref.cast (ref $A)
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $1)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: (local.get $2)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+(module
+ ;; Two exports of a single function that needs fixups. We could reuse a
+ ;; single stub, but we leave that for the optimizer.
+
+ (export "a" (func $anyref-both))
+
+ (export "b" (func $anyref-both))
+
+ ;; CHECK: (type $0 (func (param externref) (result externref)))
+
+ ;; CHECK: (type $1 (func (param anyref) (result anyref)))
+
+ ;; CHECK: (export "a" (func $stub$anyref-both))
+
+ ;; CHECK: (export "b" (func $stub$anyref-both_2))
+
+ ;; CHECK: (func $anyref-both (type $1) (param $x anyref) (result anyref)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $anyref-both (param $x anyref) (result anyref)
+ (unreachable)
+ )
+)
+;; CHECK: (func $stub$anyref-both (type $0) (param $0 externref) (result externref)
+;; CHECK-NEXT: (extern.convert_any
+;; CHECK-NEXT: (call $anyref-both
+;; CHECK-NEXT: (ref.cast anyref
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+
+;; CHECK: (func $stub$anyref-both_2 (type $0) (param $0 externref) (result externref)
+;; CHECK-NEXT: (extern.convert_any
+;; CHECK-NEXT: (call $anyref-both
+;; CHECK-NEXT: (ref.cast anyref
+;; CHECK-NEXT: (any.convert_extern
+;; CHECK-NEXT: (local.get $0)
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
+;; CHECK-NEXT: )
diff --git a/test/passes/fuzz_metrics_passes_noprint.bin.txt b/test/passes/fuzz_metrics_passes_noprint.bin.txt
index b4d67bab0..5e0dbfe07 100644
--- a/test/passes/fuzz_metrics_passes_noprint.bin.txt
+++ b/test/passes/fuzz_metrics_passes_noprint.bin.txt
@@ -1,35 +1,35 @@
Metrics
total
- [exports] : 23
- [funcs] : 34
- [globals] : 30
+ [exports] : 50
+ [funcs] : 64
+ [globals] : 24
[imports] : 5
[memories] : 1
- [memory-data] : 17
- [table-data] : 6
+ [memory-data] : 15
+ [table-data] : 16
[tables] : 1
[tags] : 0
- [total] : 9415
- [vars] : 105
- Binary : 726
- Block : 1537
- Break : 331
- Call : 306
- CallIndirect : 10
- Const : 1479
- Drop : 83
- GlobalGet : 778
- GlobalSet : 584
- If : 531
- Load : 164
- LocalGet : 774
- LocalSet : 570
- Loop : 244
- Nop : 105
- RefFunc : 6
- Return : 94
- Select : 70
- Store : 86
- Switch : 2
- Unary : 654
- Unreachable : 281
+ [total] : 6973
+ [vars] : 223
+ Binary : 534
+ Block : 1168
+ Break : 228
+ Call : 282
+ CallIndirect : 38
+ Const : 1083
+ Drop : 115
+ GlobalGet : 580
+ GlobalSet : 444
+ If : 354
+ Load : 113
+ LocalGet : 501
+ LocalSet : 367
+ Loop : 148
+ Nop : 99
+ RefFunc : 16
+ Return : 91
+ Select : 53
+ Store : 70
+ Switch : 1
+ Unary : 466
+ Unreachable : 222
diff --git a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
index a77708b2e..8df9d033c 100644
--- a/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
+++ b/test/passes/translate-to-fuzz_all-features_metrics_noprint.txt
@@ -1,48 +1,54 @@
Metrics
total
- [exports] : 6
- [funcs] : 12
+ [exports] : 9
+ [funcs] : 10
[globals] : 4
[imports] : 8
[memories] : 1
[memory-data] : 112
- [table-data] : 3
+ [table-data] : 2
[tables] : 1
- [tags] : 0
- [total] : 608
- [vars] : 48
- ArrayNew : 5
+ [tags] : 1
+ [total] : 682
+ [vars] : 37
+ ArrayLen : 1
+ ArrayNew : 7
ArrayNewFixed : 5
- Binary : 75
- Block : 80
- BrOn : 5
- Break : 4
- Call : 21
- CallRef : 1
- Const : 113
+ ArraySet : 1
+ AtomicNotify : 1
+ Binary : 79
+ Block : 72
+ BrOn : 4
+ Break : 7
+ Call : 19
+ Const : 149
Drop : 15
- GlobalGet : 39
- GlobalSet : 36
- If : 21
- Load : 17
- LocalGet : 45
- LocalSet : 20
+ GlobalGet : 35
+ GlobalSet : 32
+ If : 20
+ Load : 20
+ LocalGet : 55
+ LocalSet : 26
Loop : 7
- Nop : 7
- RefAs : 2
+ MemoryFill : 1
+ Nop : 9
+ Pop : 1
+ RefAs : 1
+ RefCast : 1
RefEq : 1
- RefFunc : 9
- RefI31 : 1
- RefIsNull : 1
+ RefFunc : 17
+ RefI31 : 2
+ RefIsNull : 2
RefNull : 8
Return : 5
- Select : 2
+ SIMDExtract : 3
Store : 1
- StringConst : 4
- StringEq : 1
- StringMeasure : 2
- StructNew : 13
- TupleExtract : 1
+ StringConst : 3
+ StringMeasure : 1
+ StringWTF16Get : 1
+ StructNew : 23
+ Try : 1
+ TupleExtract : 3
TupleMake : 4
- Unary : 19
- Unreachable : 18
+ Unary : 23
+ Unreachable : 16