summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/etc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/jit-test/etc
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/jit-test/etc')
-rw-r--r--js/src/jit-test/etc/generate-lookupswitch-tests.js365
-rw-r--r--js/src/jit-test/etc/generate-nosuchproperty-tests.js78
-rw-r--r--js/src/jit-test/etc/wasm/Makefile10
-rw-r--r--js/src/jit-test/etc/wasm/README.md83
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock266
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/Cargo.toml19
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/README.md62
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/config-lock.toml31
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/config.toml114
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/src/main.rs490
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml11
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/README.md3
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/convert.rs672
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/harness.js360
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/lib.rs18
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/license.js14
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/main.rs40
-rw-r--r--js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/out.rs180
-rw-r--r--js/src/jit-test/etc/wasm/spec-tests.patch784
19 files changed, 3600 insertions, 0 deletions
diff --git a/js/src/jit-test/etc/generate-lookupswitch-tests.js b/js/src/jit-test/etc/generate-lookupswitch-tests.js
new file mode 100644
index 0000000000..2fd2b73d1c
--- /dev/null
+++ b/js/src/jit-test/etc/generate-lookupswitch-tests.js
@@ -0,0 +1,365 @@
+
+/**
+ * A test case spec is an array of objects of the following kind:
+ * { 'match': Num|Str|Null,
+ * 'body': Num|Str|Null,
+ * 'fallthrough': Boolean }
+ *
+ * If the 'match' is null, then it represents a 'default:'
+ * If the 'match' is not null, it represents a 'case X:' where X is the value.
+ * If the 'body' is null, then it means that the case body is empty. Otherwise,
+ * it means that the case body is a single 'arr.push(V);' where "arr" is an input
+ * array to the function containing the switch statement, and V is the value.
+ * If 'fallthrough' is false, then the body is terminated with a break, otherwise
+ * it is not.
+ *
+ * So a spec: [{'match':3, 'body':null, 'fallthrough':false}, {'match':null, 'body':"foo", 'fallthrough':true}]
+ * Represents a switch function:
+ * function(x, arr) {
+ * switch(x) {
+ * case 3:
+ * break;
+ * default:
+ * arr.push('foo');
+ * }
+ * }
+ *
+ * GenerateSpecPermutes generates a bunch of relevant specs, using the given case match-values,
+ * and appends them to result the array passed into it.
+ *
+ * InterpretSwitch takes a spec, a value, and a result array, and behaves as if the switch specified
+ * by the spec had been called on the value and the result array.
+ *
+ * VerifySwitchSpec is there but not used in the code. I was using it while testing the test case
+ * generator. It verifies that a switch spec is sane.
+ *
+ * RunSpec uses eval to run the test directly. It's not used currently.
+ *
+ * GenerateSwitchCode generates a string of the form "function NAME(x, arg) { .... }" which
+ * contains the switch modeled by its input spec.
+ *
+ * RunTest is there to be used from within the generated script. Its code is dumped out
+ * to the generated script text, and invoked there.
+ *
+ * Hope all of this makes some sort of sense.
+ * -kannan
+ */
+
+/** HELPERS **/
+
+function ASSERT(cond, msg) { assertEq(cond, true, msg); }
+
+function IsUndef(x) { return typeof(x) == 'undefined'; }
+function IsNull(x) { return typeof(x) == 'object' && x == null; }
+function IsNum(x) { return typeof(x) == 'number'; }
+function IsStr(x) { return typeof(x) == 'string'; }
+function IsBool(x) { return typeof(x) == 'boolean'; }
+
+function Repr(x) {
+ ASSERT(IsNum(x) || IsStr(x), "Repr");
+ if(IsNum(x)) { return ""+x; }
+ else { return "'"+x+"'"; }
+}
+
+function RandBool() { return Math.random() >= 0.5; }
+function RandInt(max) {
+ if(IsUndef(max)) { max = 0x7fffffff; }
+ return (Math.random() * max)|0;
+}
+
+var CHARS = "abcdefghijklmnopqrstuvywxyzABCDEFGHIJKLMNOPQRSTUVYWXYZ0123456789~!@#$%^&*()-_=+{}[]";
+function RandStr() {
+ var arr = [];
+ var len = Math.floor(Math.random() * 10) + 1;
+ for(var i = 0; i < len; i++) {
+ var c = Math.floor(Math.random() * CHARS.length);
+ arr.push(CHARS[c]);
+ }
+ return arr.join('');
+}
+
+function RandVal() { return RandBool() ? RandInt() : RandStr(); }
+
+/**
+ * Compare two arrays and ensure they are equal.
+ */
+function ArraysEqual(arr1, arr2) {
+ ASSERT(arr1.length == arr2.length, "Lengths not equal");
+ for(var i = 0; i < arr1.length; i++) {
+ ASSERT(typeof(arr1[i]) == typeof(arr2[i]), "Types not equal for position " + i);
+ ASSERT(arr1[i] == arr2[i], "Values not equal for position " + i);
+ }
+}
+
+function VerifySwitchSpec(spec) {
+ var foundDefault = undefined;
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i],
+ match = caseSpec.match,
+ body = caseSpec.body,
+ fallthrough = caseSpec.fallthrough;
+ ASSERT(IsNum(match) || IsStr(match) || IsNull(match), "Invalid case match for " + i);
+ ASSERT(IsNum(body) || IsStr(body) || IsNull(body), "Invalid case body for " + i);
+ ASSERT(IsBool(fallthrough), "Invalid fallthrough for " + i);
+
+ if(IsNull(match)) {
+ ASSERT(IsUndef(foundDefault), "Duplicate default found at " + i);
+ foundDefault = i;
+ }
+ }
+}
+
+/**
+ * Do a manual interpretation of a particular spec, given an input
+ * and outputting to an output array.
+ */
+function InterpretSwitch(spec, input, outputArray) {
+ var foundMatch = undefined, foundDefault = undefined;
+ // Go through cases, trying to find a matching clause.
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i], match = caseSpec.match;
+
+ if(IsNull(match)) {
+ foundDefault = i;
+ continue;
+ } else if(match === input) {
+ foundMatch = i;
+ break;
+ }
+ }
+ // Select either matching clause or default.
+ var matchI = IsNum(foundMatch) ? foundMatch : foundDefault;
+
+ // If match or default was found, interpret body from that point on.
+ if(IsNum(matchI)) {
+ for(var i = matchI; i < spec.length; i++) {
+ var caseSpec = spec[i],
+ match = caseSpec.match,
+ body = caseSpec.body,
+ fallthrough = caseSpec.fallthrough;
+ if(!IsNull(body)) { outputArray.push(body); }
+ if(!fallthrough) { break; }
+ }
+ }
+}
+
+/**
+ * Generate the code string for a javascript function containing the
+ * switch specified by the spec, in pure JS syntax.
+ */
+function GenerateSwitchCode(spec, name) {
+ var lines = [];
+ if(!name) { name = ""; }
+
+ lines.push("function "+name+"(x, arr) {");
+ lines.push(" switch(x) {");
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i],
+ match = caseSpec.match,
+ body = caseSpec.body,
+ fallthrough = caseSpec.fallthrough;
+
+ if(IsNull(match)) { lines.push(" default:"); }
+ else { lines.push(" case "+Repr(match)+":"); }
+
+ if(!IsNull(body)) { lines.push(" arr.push("+Repr(body)+");"); }
+ if(!fallthrough) { lines.push(" break;"); }
+ }
+ lines.push(" }");
+ lines.push("}");
+ return lines.join("\n");
+}
+
+/**
+ * Generates all possible specs for a given set of case match values.
+ */
+function GenerateSpecPermutes(matchVals, resultArray) {
+ ASSERT((0 < matchVals.length) && (matchVals.length <= 5), "Invalid matchVals");
+ var maxPermuteBody = (1 << matchVals.length) - 1;
+ for(var bod_pm = 0; bod_pm <= maxPermuteBody; bod_pm++) {
+ var maxPermuteFallthrough = (1 << matchVals.length) - 1;
+
+ for(var ft_pm = 0; ft_pm <= maxPermuteFallthrough; ft_pm++) {
+ // use bod_m and ft_pm to decide the placement of null vs value bodies,
+ // and the placement of fallthroughs vs breaks.
+ // Empty bodies always fall through, so fallthrough bitmask 1s must be
+ // a subset of the body bitmask 1s.
+ if((bod_pm | ft_pm) != bod_pm) {
+ continue;
+ }
+
+ var spec = [];
+ for(var k = 0; k < matchVals.length; k++) {
+ var match = matchVals[k];
+ var body = ((bod_pm & (1 << k)) > 0) ? null : RandVal();
+ var fallthrough = ((ft_pm & (1 << k)) > 0) ? true : false;
+ var caseSpec = {'match':match, 'body':body, 'fallthrough':fallthrough};
+ spec.push(caseSpec);
+ }
+
+ // Variant specs for following cases:
+
+ // Default with empty body, fallthrough
+ GenerateDefaultPermutes(spec, null, true, resultArray);
+ // Default with nonempty body, fallthrough
+ GenerateDefaultPermutes(spec, RandVal(), true, resultArray);
+ // Default with nonempty body, break
+ GenerateDefaultPermutes(spec, RandVal(), false, resultArray);
+ }
+ }
+}
+function GenerateDefaultPermutes(spec, body, fallthrough, resultArray) {
+ if(spec.length <= 2) {
+ for(var i = 0; i <= spec.length; i++) {
+ var copy = CopySpec(spec);
+ if(IsNull(body)) {
+ copy.splice(i,0,{'match':null, 'body':null, 'fallthrough':true});
+ } else {
+ copy.splice(i,0,{'match':null, 'body':body, 'fallthrough':fallthrough});
+ }
+ resultArray.push(copy);
+ }
+ } else {
+ var posns = [0, Math.floor(spec.length / 2), spec.length];
+ posns.forEach(function (i) {
+ var copy = CopySpec(spec);
+ if(IsNull(body)) {
+ copy.splice(i,0,{'match':null, 'body':null, 'fallthrough':true});
+ } else {
+ copy.splice(i,0,{'match':null, 'body':body, 'fallthrough':fallthrough});
+ }
+ resultArray.push(copy);
+ });
+ }
+}
+function CopySpec(spec) {
+ var newSpec = [];
+ for(var i = 0; i < spec.length; i++) {
+ var caseSpec = spec[i];
+ newSpec.push({'match':caseSpec.match,
+ 'body':caseSpec.body,
+ 'fallthrough':caseSpec.fallthrough});
+ }
+ return newSpec;
+}
+
+
+function RunSpec(spec, matchVals) {
+ var code = GenerateSwitchCode(spec);
+
+ // Generate roughly 200 inputs for the test case spec, exercising
+ // every match value, as well as 3 randomly generated values for every
+ // iteration of the match values.
+ var inputs = [];
+ while(inputs.length < 500) {
+ for(var i = 0; i < matchVals.length; i++) { inputs.push(matchVals[i]); }
+ for(var i = 0; i < 3; i++) { inputs.push(RandVal()); }
+ }
+
+ // Interpret the lookupswitch with the inputs.
+ var interpResults = [];
+ for(var i = 0; i < inputs.length; i++) {
+ InterpretSwitch(spec, inputs[i], interpResults);
+ }
+
+ // Run compiled lookupswitch with the inputs.
+ var fn = eval("_ = " + code);
+ print("Running spec: " + code);
+ var compileResults = RunCompiled(fn, inputs);
+ print("Done Running spec");
+
+ // Verify that they produce the same output.
+ ASSERT(interpResults.length == compileResults.length, "Length mismatch");
+ for(var i = 0; i < interpResults.length; i++) {
+ ASSERT(interpResults[i] == compileResults[i], "Value mismatch");
+ }
+}
+function RunCompiled(fn, inputs) {
+ var results = [];
+ var len = inputs.length;
+ for(var i = 0; i < len; i++) { fn(inputs[i], results); }
+ return results;
+}
+
+function PrintSpec(spec, inputs, fname) {
+ var code = GenerateSwitchCode(spec, fname);
+ var input_s = fname + ".INPUTS = [" + inputs.map(Repr).join(', ') + "];";
+ var spec_s = fname + ".SPEC = " + JSON.stringify(spec) + ";";
+ print(code + "\n" + input_s + "\n" + spec_s);
+}
+
+function RunTest(test) {
+ // Exercise every test case as well as one case which won't match with any of the
+ // ("But what if it randomly generates a string case match whose value is
+ // UNMATCHED_CASE?!", you ask incredulously. Well, RandStr limits itself to 11 chars.
+ // So there.)
+ var inputs = test.INPUTS;
+ inputs.push("UNMATCHED_CASE");
+ var spec = test.SPEC;
+
+ var results1 = [];
+ for(var i = 0; i < 80; i++) {
+ for(var j = 0; j < inputs.length; j++) {
+ test(inputs[j], results1);
+ }
+ }
+
+ var results2 = [];
+ for(var i = 0; i < 80; i++) {
+ for(var j = 0; j < inputs.length; j++) {
+ InterpretSwitch(spec, inputs[j], results2);
+ }
+ }
+ ArraysEqual(results1, results2);
+}
+
+// NOTES:
+// * RunTest is used within the generated test script.
+// * InterpretSwitch is printed out into the generated test script.
+
+print("/////////////////////////////////////////");
+print("// This is a generated file!");
+print("// See jit-tests/etc/generate-lookupswitch-tests.js for the code");
+print("// that generated this code!");
+print("/////////////////////////////////////////");
+print("");
+print("/////////////////////////////////////////");
+print("// PRELUDE //");
+print("/////////////////////////////////////////");
+print("");
+print("// Avoid eager compilation of the global-scope.");
+print("try{} catch (x) {};");
+print("");
+print(ASSERT);
+print(IsNull);
+print(IsNum);
+print(ArraysEqual);
+print(InterpretSwitch);
+print(RunTest);
+print("");
+print("/////////////////////////////////////////");
+print("// TEST CASES //");
+print("/////////////////////////////////////////");
+print("");
+print("var TESTS = [];");
+var MATCH_SETS = [["foo", "bar", "zing"]];
+var count = 0;
+for(var i = 0; i < MATCH_SETS.length; i++) {
+ var matchSet = MATCH_SETS[i];
+ var specs = [];
+ GenerateSpecPermutes(matchSet, specs);
+ for(var j = 0; j < specs.length; j++) {
+ count++;
+ PrintSpec(specs[j], matchSet.slice(), 'test_'+count);
+ print("TESTS.push(test_"+count+");\n");
+ }
+}
+
+print("");
+print("/////////////////////////////////////////");
+print("// RUNNER //");
+print("/////////////////////////////////////////");
+print("");
+print("for(var i = 0; i < TESTS.length; i++) {");
+print(" RunTest(TESTS[i]);");
+print("}");
diff --git a/js/src/jit-test/etc/generate-nosuchproperty-tests.js b/js/src/jit-test/etc/generate-nosuchproperty-tests.js
new file mode 100644
index 0000000000..a0bb3e47db
--- /dev/null
+++ b/js/src/jit-test/etc/generate-nosuchproperty-tests.js
@@ -0,0 +1,78 @@
+
+// This code generates the test cases jit-test/tests/baseline/no-such-property-getprop.js
+//
+// In particular, it generates the testChain_<N>_<I>() and runChain_<N>_<I>() functions
+// at the tail of the file.
+
+var TEST_CASE_FUNCS = [];
+function runChain_NNNN_DDDD(obj) {
+ var sum = 0;
+ for (var i = 0; i < 100; i++)
+ sum += obj.foo;
+ return sum;
+}
+function testChain_NNNN_DDDD() {
+ var obj = createTower(NNNN);
+ assertEq(runChain_NNNN_DDDD(obj), NaN);
+ updateChain(obj, DDDD, 'foo', 9);
+ assertEq(runChain_NNNN_DDDD(obj), 900);
+}
+function generateTestCase(n, d) {
+ var runFuncName = "runChain_" + n + "_" + d;
+ var testFuncName = "testChain_" + n + "_" + d;
+ TEST_CASE_FUNCS.push(testFuncName);
+
+ print("//// Test chain of length " + n + " with late-property-addition at depth " + d);
+ print(String(runChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
+ print(String(testChain_NNNN_DDDD).replace(/NNNN/g, ''+n).replace(/DDDD/g, ''+d));
+ print("");
+}
+
+// Helper function to create an object with a proto-chain of length N.
+function createTower(n) {
+ var result = Object.create(null);
+ for (var i = 0; i < n; i++)
+ result = Object.create(result);
+ return result;
+}
+
+function updateChain(obj, depth, prop, value) {
+ // Walk down the proto chain |depth| iterations and set |prop| to |value|.
+ var cur = obj;
+ for (var i = 0; i < depth; i++)
+ cur = Object.getPrototypeOf(cur);
+
+ var desc = {value:value, writable:true, configurable:true, enumerable:true};
+ Object.defineProperty(cur, prop, desc);
+}
+
+print("/////////////////////////////////////////");
+print("// This is a generated file!");
+print("// See jit-tests/etc/generate-nosuchproperty-tests.js for the code");
+print("// that generated this code!");
+print("/////////////////////////////////////////");
+print("");
+print("/////////////////////////////////////////");
+print("// PRELUDE //");
+print("/////////////////////////////////////////");
+print("");
+print(createTower);
+print(updateChain);
+print("");
+print("/////////////////////////////////////////");
+print("// TEST CASES //");
+print("/////////////////////////////////////////");
+print("");
+for (var n = 0; n <= 10; n++) {
+ for (var d = 0; d <= n; d++) {
+ generateTestCase(n, d);
+ }
+}
+
+print("");
+print("/////////////////////////////////////////");
+print("// RUNNER //");
+print("/////////////////////////////////////////");
+print("");
+for (var i = 0; i < TEST_CASE_FUNCS.length; i++)
+ print(TEST_CASE_FUNCS[i] + "();");
diff --git a/js/src/jit-test/etc/wasm/Makefile b/js/src/jit-test/etc/wasm/Makefile
new file mode 100644
index 0000000000..c659a7000e
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/Makefile
@@ -0,0 +1,10 @@
+.PHONY: update
+
+warning = '\# Wasm Spec Tests\n\nThese tests are autogenerated using a tool, do not edit.\n\nSee `jit-test/etc/wasm/` for more information.'
+
+update:
+ (cd ./generate-spectests && RUST_BACKTRACE=1 RUST_LOG=info cargo run --release)
+ rm -r ../../tests/wasm/spec
+ cp -R generate-spectests/tests/js ../../tests/wasm/spec
+ echo $(warning) > ../../tests/wasm/spec/README.md
+ [ -f ./spec-tests.patch ] && (cd ../../tests/wasm/spec && patch -u -p7 < ../../../etc/wasm/spec-tests.patch)
diff --git a/js/src/jit-test/etc/wasm/README.md b/js/src/jit-test/etc/wasm/README.md
new file mode 100644
index 0000000000..75e4798a88
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/README.md
@@ -0,0 +1,83 @@
+# Wasm Spec Tests
+
+This directory contains scripts and configs to manage the in-tree import of the
+wasm spec testsuite.
+
+## Format of tests
+
+Spec tests are given in `test/core` of the `spec` repository as `.wast` files.
+A `.wast` file is a superset of the `.wat` format with commands for running
+tests.
+
+The spec interpreter can natively consume `.wast` files to run the tests, but
+we cannot run them directly ourselves. To workaround this, we have a tool which
+can convert `.wast` files to `.js` that can be run efficiently in our jit-test
+harness.
+
+## Running tests
+
+Tests are imported to `jit-test` to be run in the JS shell.
+
+To run under `jit-test`:
+```bash
+cd js/src
+./jit-test.py path_to_build/dist/bin/js wasm/spec/
+```
+
+## Test importing
+
+There are many proposals in addition to the canonical spec. Each proposal is a
+fork of the canonical spec and may modify any amount of files.
+
+This causes a challenge for any engine implementing multiple proposals, as any
+proposal may conflict (and often will) with each other. This makes it
+infeasible to merge all spec and proposal repos together.
+
+Testing each proposal separately in full isn't an attractive option either, as
+most tests are unchanged and that would cause a significant amount of wasted
+computation time.
+
+For this reason, we generate a set of separate test-suites that are 'pruned' to
+obtain a minimal set of tests. The tool works by merging each proposal with the
+proposal it is based off of and removing tests that have not changed.
+
+### Configuration
+
+The import tool relies on `config.toml` for the list of proposals to import,
+and `config-lock.toml` for a list of commits to use for each proposal.
+
+The lock file makes test importing deterministic and controllable. This is
+useful as proposals often make inconvenient and breaking changes.
+
+### Operation
+
+```bash
+# Add, remove, or modify proposals
+vim generate-spectests/config.toml
+# Remove locks for any proposals you wish to pull the latest changes on
+vim generate-spectests/config-lock.toml
+# Import the tests
+make update
+# View the tests that were imported
+hg stat
+# Run the imported tests and note failures
+./jit-test.py dist/bin/js wasm/spec/
+# Exclude test failures
+vim generate-spectests/config.toml
+# Re-import the tests to exclude failing tests
+make update
+# Commit the changes
+hg commit
+```
+
+### Debugging import failures
+
+Proposals can often have conflicts with their upstream proposals. This is okay,
+and the test importer will fallback to building tests on an unmerged tree.
+
+This will likely result in extra tests being imported due to spurious
+differences between the proposal and upstream, but generally is okay.
+
+The import tool uses `RUST_LOG` to output debug information. `Makefile`
+automatically uses `RUST_LOG=info`. Change that to `RUST_LOG=debug` to get
+verbose output of all the commands run.
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock b/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock
new file mode 100644
index 0000000000..fc48ba7a55
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/Cargo.lock
@@ -0,0 +1,266 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b"
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+dependencies = [
+ "hermit-abi",
+ "libc",
+ "winapi",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "env_logger"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
+dependencies = [
+ "atty",
+ "humantime",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "humantime"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+dependencies = [
+ "quick-error",
+]
+
+[[package]]
+name = "leb128"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3576a87f2ba00f6f106fdfcd16db1d698d648a26ad8e0573cad8537c3c362d2a"
+
+[[package]]
+name = "libc"
+version = "0.2.96"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5600b4e6efc5421841a2138a6b082e07fe12f9aaa12783d50e5d13325b26b4fc"
+
+[[package]]
+name = "log"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quick-error"
+version = "1.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+
+[[package]]
+name = "quote"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
+
+[[package]]
+name = "serde"
+version = "1.0.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.126"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "unicode-width"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "wasm-encoder"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7"
+dependencies = [
+ "leb128",
+]
+
+[[package]]
+name = "wasm-generate-spectests"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "env_logger",
+ "log",
+ "regex",
+ "serde",
+ "serde_derive",
+ "toml",
+ "wast2js",
+]
+
+[[package]]
+name = "wast"
+version = "55.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4984d3e1406571f4930ba5cf79bd70f75f41d0e87e17506e0bd19b0e5d085f05"
+dependencies = [
+ "leb128",
+ "memchr",
+ "unicode-width",
+ "wasm-encoder",
+]
+
+[[package]]
+name = "wast2js"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "wast",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/Cargo.toml b/js/src/jit-test/etc/wasm/generate-spectests/Cargo.toml
new file mode 100644
index 0000000000..eb28a53537
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/Cargo.toml
@@ -0,0 +1,19 @@
+[package]
+name = "wasm-generate-spectests"
+version = "0.1.0"
+authors = ["Ryan Hunt <rhunt@eqrion.net>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+regex = "1"
+serde = "1"
+serde_derive = "1"
+toml = "0.5.6"
+log = "0.4"
+env_logger = "0.7"
+anyhow = "1.0.19"
+wast2js = { path = "./wast2js" }
+
+[workspace]
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/README.md b/js/src/jit-test/etc/wasm/generate-spectests/README.md
new file mode 100644
index 0000000000..4423a31077
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/README.md
@@ -0,0 +1,62 @@
+# generate-spectests
+
+A tool to generate a combined testsuite for the wasm spec and all proposals.
+
+This tool tries to be as robust as possible to deal with upstream breakage, while still generating a minimal testsuite for proposals that don't change many tests.
+
+## Usage
+
+```bash
+# Configure the tests you want
+vim config.toml
+
+# Generate the tests
+# This will create a `repos/` and `tests/` in your working directory
+cargo run
+```
+
+## config.toml
+
+```toml
+# (optional) Text to add to a 'directives.txt' file put in 'js/${repo}/harness'
+harness_directive = ""
+
+# (optional) Text to add to a 'directives.txt' file put in 'js/${repo}'
+directive = ""
+
+# (optional) Tests to include even if they haven't changed with respect to their parent repository
+included_tests = ["test.wast"]
+
+# (optional) Tests to exclude
+excluded_tests = ["test.wast"]
+
+[[repos]]
+# Name of the repository
+name = "sign-extension-ops"
+
+# Url of the repository
+url = "https://github.com/WebAssembly/sign-extension-ops"
+
+# (optional) Name of the repository that is the upstream for this repository.
+# This repository will attempt to merge with this upstream when generating
+# tests. The parent repository must be specified before this repository in the
+# 'config.toml'. If you change this, you must delete the 'repos' directory
+# before generating tests again.
+parent = "spec"
+
+# (optional) Whether to skip merging with upstream, if it exists.
+skip_merge = "false"
+
+# (optional) The commit to checkout when generating tests. If not specified,
+# defaults to the latest 'origin/master'.
+commit = "df34ea92"
+
+# (optional) Text to add to a 'directives.txt' file put in 'js/{$repo}'
+directive = ""
+
+# (optional) Tests to include even if they haven't changed with respect to their parent repository
+included_tests = ["test.wast"]
+
+# (optional) Tests to exclude
+excluded_tests = ["test.wast"]
+```
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/config-lock.toml b/js/src/jit-test/etc/wasm/generate-spectests/config-lock.toml
new file mode 100644
index 0000000000..7db5f73e43
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/config-lock.toml
@@ -0,0 +1,31 @@
+[[repos]]
+name = 'spec'
+commit = 'b1fbe1a8'
+
+[[repos]]
+name = 'threads'
+commit = '85b562cd'
+
+[[repos]]
+name = 'simd'
+commit = 'a78b98a6'
+
+[[repos]]
+name = 'exception-handling'
+commit = '76419ef8'
+
+[[repos]]
+name = 'memory64'
+commit = '8d8f532d'
+
+[[repos]]
+name = 'function-references'
+commit = 'c4eef9a8'
+
+[[repos]]
+name = 'relaxed-simd'
+commit = 'c4c9ddaf'
+
+[[repos]]
+name = 'extended-const'
+commit = 'dd72ab96'
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/config.toml b/js/src/jit-test/etc/wasm/generate-spectests/config.toml
new file mode 100644
index 0000000000..9341a70ab7
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/config.toml
@@ -0,0 +1,114 @@
+# Standard 'directives.txt' prologues for jit-tests
+harness_directive = "|jit-test| skip-if: true"
+directive = "|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--wasm-test-serialization; test-also=--test-wasm-await-tier2; test-also=--disable-wasm-huge-memory; skip-variant-if: --disable-wasm-huge-memory, !wasmHugeMemorySupported(); local-include:harness/harness.js"
+
+# Failing tests across all testsuites
+excluded_tests = [
+ # false-positive windows-specific failures
+ "align.wast",
+ # bulk-memory-operations/issues/133 (data.wast:161)
+ "data.wast",
+ # memory limits can be encoded with more bytes now
+ "binary.wast",
+ "binary-leb128.wast",
+ # testing that multiple tables fail (imports.wast:309)
+ "imports.wast",
+ # bulk-memory-operations/issues/133 (linking.wast:206)
+ "linking.wast",
+ # bulk-memory-operations/issues/133 (elem.wast:142)
+ "elem.wast",
+ # test harness doesn't acquire global values correctly
+ "exports.wast",
+ # false-positive windows-specific failure
+ "memory_trap.wast",
+ # false-positive error on invalid UTF-8 custom section name (utf8-custom-section-id.wast:6)
+ "utf8-custom-section-id.wast",
+ # invalid table maximum length for web embeddings
+ "table.wast",
+ # fails after a bottom-type has been added to validation
+ "unreached-invalid.wast",
+ # argument is not wasm value
+ "^select.wast",
+]
+
+[[repos]]
+name = "spec"
+url = "https://github.com/WebAssembly/spec"
+excluded_tests = []
+directive = "; test-also=--no-avx; skip-variant-if: --no-avx, !getBuildConfiguration().x86 && !getBuildConfiguration().x64 || getBuildConfiguration().simulator"
+
+[[repos]]
+name = "threads"
+url = "https://github.com/WebAssembly/threads"
+branch = "main"
+parent = "spec"
+excluded_tests = [
+ "atomic.wast",
+ # testing that multi-value isn't implemented (func.wast:492)
+ "func.wast",
+ # testing that multi-value isn't implemented (type.wast:52)
+ "type.wast"
+]
+# Skip in WPT where we can't guard on features being enabled
+skip_wpt = true
+# Skip in jit-test when it's not enabled
+directive = "; skip-if: !wasmThreadsEnabled()"
+
+[[repos]]
+name = "exception-handling"
+url = "https://github.com/WebAssembly/exception-handling"
+branch = "main"
+parent = "spec"
+# Skip in jit-test when it's not enabled
+directive = "; --wasm-exceptions; skip-if: !wasmExceptionsEnabled()"
+excluded_tests = [
+ # This gets included due to divergence between the proposal repo and the
+ # upstream spec, it can be removed when the repo is rebased.
+ "global.wast"
+]
+
+[[repos]]
+name = "memory64"
+url = "https://github.com/mozilla-spidermonkey/memory64"
+branch = "test-cases"
+directive = "; skip-if: !wasmMemory64Enabled()"
+excluded_tests = []
+
+[[repos]]
+name = "function-references"
+url = "https://github.com/WebAssembly/function-references"
+branch = "main"
+parent = "spec"
+directive = "; --wasm-function-references; skip-if: !wasmFunctionReferencesEnabled()"
+excluded_tests = [
+ # return_call_ref not implemented
+ "return_call_ref.wast",
+ # table function elem subtyping
+ "ref_is_null.wast",
+ # cannot expose indexed reference type
+ "ref_null.wast",
+ # parameter subtyping
+ "type-equivalence.wast",
+ # NYI WasmValType.h:259
+ "table-sub.wast",
+ # unrelated
+ "tokens.wast",
+ # irrelevant
+ "simd_lane.wast",
+]
+
+[[repos]]
+name = "relaxed-simd"
+url = "https://github.com/WebAssembly/relaxed-simd"
+branch = "main"
+parent = "spec"
+directive = "; --wasm-relaxed-simd; skip-if: !wasmRelaxedSimdEnabled()"
+excluded_tests = []
+
+[[repos]]
+name = "extended-const"
+url = "https://github.com/WebAssembly/extended-const"
+branch = "main"
+parent = "spec"
+directive = "; --wasm-extended-const; skip-if: !wasmExtendedConstEnabled()"
+excluded_tests = []
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/src/main.rs b/js/src/jit-test/etc/wasm/generate-spectests/src/main.rs
new file mode 100644
index 0000000000..40f27afcc4
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/src/main.rs
@@ -0,0 +1,490 @@
+use std::env;
+use std::ffi::OsStr;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use anyhow::{bail, Result};
+use regex::{RegexSet, RegexSetBuilder};
+use serde_derive::{Deserialize, Serialize};
+use toml;
+use wast2js;
+
+use log::{debug, info, warn};
+
+// Data structures
+
+#[derive(Debug, Default, Serialize, Deserialize)]
+struct Config {
+ #[serde(default)]
+ harness_directive: Option<String>,
+ #[serde(default)]
+ directive: Option<String>,
+ #[serde(default)]
+ included_tests: Vec<String>,
+ #[serde(default)]
+ excluded_tests: Vec<String>,
+ repos: Vec<Repo>,
+}
+
+impl Config {
+ fn find_repo_mut(&mut self, name: &str) -> Option<&mut Repo> {
+ self.repos.iter_mut().find(|x| &x.name == name)
+ }
+}
+
+#[derive(Debug, Default, Serialize, Deserialize)]
+struct Repo {
+ name: String,
+ url: String,
+ #[serde(default)]
+ branch: Option<String>,
+ #[serde(default)]
+ parent: Option<String>,
+ #[serde(default)]
+ directive: Option<String>,
+ #[serde(default)]
+ included_tests: Vec<String>,
+ #[serde(default)]
+ excluded_tests: Vec<String>,
+ #[serde(default)]
+ skip_wast: bool,
+ #[serde(default)]
+ skip_js: bool,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize)]
+struct Lock {
+ repos: Vec<LockRepo>,
+}
+
+impl Lock {
+ fn find_commit(&self, name: &str) -> Option<&str> {
+ self.repos
+ .iter()
+ .find(|x| &x.name == name)
+ .map(|x| x.commit.as_ref())
+ }
+
+ fn set_commit(&mut self, name: &str, commit: &str) {
+ if let Some(lock) = self.repos.iter_mut().find(|x| &x.name == name) {
+ lock.commit = commit.to_owned();
+ } else {
+ self.repos.push(LockRepo {
+ name: name.to_owned(),
+ commit: commit.to_owned(),
+ });
+ }
+ }
+}
+
+#[derive(Debug, Default, Serialize, Deserialize)]
+struct LockRepo {
+ name: String,
+ commit: String,
+}
+
+#[derive(Debug)]
+enum Merge {
+ Standalone,
+ Merged,
+ Conflicted,
+}
+
+#[derive(Debug)]
+struct Status {
+ commit_base_hash: String,
+ commit_final_message: String,
+ merged: Merge,
+ built: bool,
+}
+
+// Roll-your-own CLI utilities
+
+fn run(name: &str, args: &[&str]) -> Result<String> {
+ debug!("{} {:?}", name, args);
+ let output = Command::new(name).args(args).output()?;
+ let stdout = String::from_utf8(output.stdout)?.trim().to_owned();
+ let stderr = String::from_utf8(output.stderr)?.trim().to_owned();
+ if !stdout.is_empty() {
+ debug!("{}", stdout);
+ }
+ if !stderr.is_empty() {
+ debug!("{}", stderr);
+ }
+
+ if output.status.success() {
+ Ok(stdout)
+ } else {
+ bail!("{}: {}\n{}", name.to_owned(), stdout, stderr)
+ }
+}
+
+fn change_dir(dir: &str) -> impl Drop {
+ #[must_use]
+ struct Reset {
+ previous: PathBuf,
+ }
+ impl Drop for Reset {
+ fn drop(&mut self) {
+ debug!("cd {}", self.previous.display());
+ env::set_current_dir(&self.previous).unwrap()
+ }
+ }
+
+ let previous = Reset {
+ previous: env::current_dir().unwrap(),
+ };
+ debug!("cd {}", dir);
+ env::set_current_dir(dir).unwrap();
+ previous
+}
+
+fn find(dir: &str) -> Vec<PathBuf> {
+ let mut paths = Vec::new();
+
+ fn find(dir: &str, paths: &mut Vec<PathBuf>) {
+ for entry in fs::read_dir(dir).unwrap().map(|x| x.unwrap()) {
+ let path = entry.path();
+
+ if entry.file_type().unwrap().is_dir() {
+ find(path.to_str().unwrap(), paths);
+ } else {
+ paths.push(path);
+ }
+ }
+ }
+
+ find(dir, &mut paths);
+ paths
+}
+
+fn write_string<P: AsRef<Path>>(path: P, text: &str) -> Result<()> {
+ let path = path.as_ref();
+ if let Some(dir) = path.parent() {
+ let _ = fs::create_dir_all(dir);
+ }
+ fs::write(path, text.as_bytes())?;
+ Ok(())
+}
+
+// The main script
+
+fn main() {
+ env_logger::init();
+
+ // Load the config
+ let mut config: Config =
+ toml::from_str(&fs::read_to_string("config.toml").expect("failed to read config.toml"))
+ .expect("invalid config.toml");
+
+ // Load the lock file, or default to no pinned commits
+ let mut lock: Lock = if Path::new("config-lock.toml").exists() {
+ toml::from_str(
+ &fs::read_to_string("config-lock.toml").expect("failed to read config-lock.toml"),
+ )
+ .expect("invalid config-lock.toml")
+ } else {
+ Lock::default()
+ };
+
+ // Clean old tests and initialize the repo if it doesn't exist
+ let specs_dir = "specs/";
+ clean_and_init_dirs(specs_dir);
+
+ // Generate the tests
+ let mut successes = Vec::new();
+ let mut failures = Vec::new();
+ {
+ // Change to the `specs/` dir where all the work happens
+ let _cd = change_dir(specs_dir);
+ for repo in &config.repos {
+ info!("Processing {:#?}", repo);
+
+ match build_repo(repo, &config, &lock) {
+ Ok(status) => successes.push((repo.name.clone(), status)),
+ Err(err) => failures.push((repo.name.clone(), err)),
+ };
+ }
+ }
+
+ // Abort if we had a failure
+ if !failures.is_empty() {
+ warn!("Failed.");
+ for (name, err) in &failures {
+ warn!("{}: (failure) {:?}", name, err);
+ }
+ std::process::exit(1);
+ }
+
+ // Display successful results
+ info!("Done.");
+ for (name, status) in &successes {
+ let repo = config.find_repo_mut(&name).unwrap();
+ lock.set_commit(&name, &status.commit_base_hash);
+
+ info!(
+ "{}: ({} {}) {}",
+ repo.name,
+ match status.merged {
+ Merge::Standalone => "standalone",
+ Merge::Merged => "merged",
+ Merge::Conflicted => "conflicted",
+ },
+ if status.built { "building" } else { "broken" },
+ status.commit_final_message.trim_end()
+ );
+ }
+
+ // Commit the new lock file
+ write_string("config-lock.toml", &toml::to_string_pretty(&lock).unwrap()).unwrap();
+}
+
+fn clean_and_init_dirs(specs_dir: &str) {
+ if !Path::new(specs_dir).exists() {
+ fs::create_dir(specs_dir).unwrap();
+ run("git", &["-C", specs_dir, "init"]).unwrap();
+ }
+
+ let _ = fs::remove_dir_all("./tests");
+}
+
+fn build_repo(repo: &Repo, config: &Config, lock: &Lock) -> Result<Status> {
+ let remote_name = &repo.name;
+ let remote_url = &repo.url;
+ let remote_branch = repo.branch.as_ref().map(|x| x.as_str()).unwrap_or("master");
+ let branch_upstream = format!("{}/{}", repo.name, remote_branch);
+ let branch_base = repo.name.clone();
+
+ // Initialize our remote and branches if they don't exist
+ let remotes = run("git", &["remote"])?;
+ if !remotes.lines().any(|x| x == repo.name) {
+ run("git", &["remote", "add", remote_name, &remote_url])?;
+ run("git", &["fetch", remote_name])?;
+ run("git", &["branch", &branch_base, &branch_upstream])?;
+ }
+
+ // Set the upstream to the correct branch
+ run(
+ "git",
+ &[
+ "branch",
+ &branch_base,
+ "--set-upstream-to",
+ &branch_upstream,
+ ],
+ )?;
+
+ // Fetch the latest changes for this repo
+ run("git", &["fetch", remote_name])?;
+
+ // Checkout the pinned commit, if any, and get the absolute commit hash
+ let base_treeish = lock.find_commit(&repo.name).unwrap_or(&branch_upstream);
+ run("git", &["checkout", &branch_base])?;
+ run("git", &["reset", base_treeish, "--hard"])?;
+ let commit_base_hash = run("git", &["log", "--pretty=%h", "-n", "1"])?
+ .trim()
+ .to_owned();
+
+ // Try to merge with parent repo, if specified
+ let merged = try_merge_parent(repo, &commit_base_hash)?;
+
+ // Exclude files specified from the config and repo
+ let mut excluded_files = Vec::new();
+ excluded_files.extend_from_slice(&config.excluded_tests);
+ excluded_files.extend_from_slice(&repo.excluded_tests);
+ let exclude = RegexSetBuilder::new(&excluded_files).build().unwrap();
+
+ // Try to build the test suite on this commit. This may fail due to merging
+ // with a parent repo, in which case we will try again in an unmerged state.
+ let mut built = false;
+ match try_build_tests(&exclude) {
+ Ok(()) => built = true,
+ Err(err) => warn!("Failed to build tests: {:?}", err),
+ };
+ // if try_build_tests(&exclude).is_err() {
+ // if repo.parent.is_some() {
+ // warn!(
+ // "Failed to build interpreter. Retrying on unmerged commit ({})",
+ // &commit_base_hash
+ // );
+ // run("git", &["reset", &commit_base_hash, "--hard"])?;
+ // built = try_build_tests(&exclude).is_ok();
+ // } else {
+ // built = false;
+ // }
+ // }
+ // if !built {
+ // warn!("Failed to build interpreter, Won't emit js/html");
+ // }
+
+ // Get the final commit message we ended up on
+ let commit_final_message = run("git", &["log", "--oneline", "-n", "1"])?;
+
+ // Compute the source files that changed, and use that to filter the files
+ // we copy over. We can't compare the generated tests, because for a
+ // generated WPT we need to copy both the .js and .html even if only
+ // one of those is different from the master.
+ let tests_changed = find_tests_changed(repo)?;
+ info!("Changed tests: {:#?}", tests_changed);
+
+ // Include the changed tests, specified files, and `harness/` directory
+ let mut included_files = Vec::new();
+ included_files.extend_from_slice(&tests_changed);
+ included_files.extend_from_slice(&config.included_tests);
+ included_files.extend_from_slice(&repo.included_tests);
+ included_files.push("harness/".to_owned());
+ let include = RegexSetBuilder::new(&included_files).build().unwrap();
+
+ // Copy over all the desired test-suites
+ if !repo.skip_wast {
+ copy_tests(repo, "test/core", "../tests", "wast", &include, &exclude);
+ }
+ if built && !repo.skip_js {
+ copy_tests(repo, "js", "../tests", "js", &include, &exclude);
+ copy_directives(repo, config)?;
+ }
+
+ Ok(Status {
+ commit_final_message,
+ commit_base_hash,
+ merged,
+ built,
+ })
+}
+
+fn try_merge_parent(repo: &Repo, commit_base_hash: &str) -> Result<Merge> {
+ if !repo.parent.is_some() {
+ return Ok(Merge::Standalone);
+ }
+ let parent = repo.parent.as_ref().unwrap();
+
+ // Try to merge with the parent branch.
+ let message = format!("Merging {}:{}with {}", repo.name, commit_base_hash, parent);
+ Ok(
+ if !run("git", &["merge", "-q", parent, "-m", &message]).is_ok() {
+ // Ignore merge conflicts in the document directory.
+ if !run("git", &["checkout", "--ours", "document"]).is_ok()
+ || !run("git", &["add", "document"]).is_ok()
+ || !run("git", &["-c", "core.editor=true", "merge", "--continue"]).is_ok()
+ {
+ // Reset to master if we failed
+ warn!(
+ "Failed to merge {}, falling back to {}.",
+ repo.name, &commit_base_hash
+ );
+ run("git", &["merge", "--abort"])?;
+ run("git", &["reset", &commit_base_hash, "--hard"])?;
+ Merge::Conflicted
+ } else {
+ Merge::Merged
+ }
+ } else {
+ Merge::Merged
+ },
+ )
+}
+
+fn try_build_tests(exclude: &RegexSet) -> Result<()> {
+ let _ = fs::remove_dir_all("./js");
+ fs::create_dir("./js")?;
+
+ let paths = find("./test/core/");
+ for path in paths {
+ if path.extension() != Some(OsStr::new("wast")) {
+ continue;
+ }
+
+ let stripped_path = path.strip_prefix("./test/core/").unwrap();
+ let stripped_path_str = stripped_path.to_str().unwrap();
+ if exclude.is_match(stripped_path_str) {
+ continue;
+ }
+
+ let source = std::fs::read_to_string(&path)?;
+ let script = wast2js::convert(&path, &source)?;
+
+ std::fs::write(
+ Path::new("./js").join(&path.with_extension("wast.js").file_name().unwrap()),
+ &script,
+ )?;
+ }
+
+ fs::create_dir("./js/harness")?;
+ write_string("./js/harness/harness.js", &wast2js::harness())?;
+
+ Ok(())
+}
+
+fn copy_tests(
+ repo: &Repo,
+ src_dir: &str,
+ dst_dir: &str,
+ test_name: &str,
+ include: &RegexSet,
+ exclude: &RegexSet,
+) {
+ for path in find(src_dir) {
+ let stripped_path = path.strip_prefix(src_dir).unwrap();
+ let stripped_path_str = stripped_path.to_str().unwrap();
+
+ if !include.is_match(stripped_path_str) || exclude.is_match(stripped_path_str) {
+ continue;
+ }
+
+ let out_path = Path::new(dst_dir)
+ .join(test_name)
+ .join(&repo.name)
+ .join(&stripped_path);
+ let out_dir = out_path.parent().unwrap();
+ let _ = fs::create_dir_all(out_dir);
+ fs::copy(path, out_path).unwrap();
+ }
+}
+
+fn copy_directives(repo: &Repo, config: &Config) -> Result<()> {
+ // Write directives files
+ if let Some(harness_directive) = &config.harness_directive {
+ let directives_path = Path::new("../tests/js")
+ .join(&repo.name)
+ .join("harness/directives.txt");
+ write_string(&directives_path, harness_directive)?;
+ }
+ let directives = format!(
+ "{}{}",
+ config.directive.as_ref().map(|x| x.as_str()).unwrap_or(""),
+ repo.directive.as_ref().map(|x| x.as_str()).unwrap_or("")
+ );
+ if !directives.is_empty() {
+ let directives_path = Path::new("../tests/js")
+ .join(&repo.name)
+ .join("directives.txt");
+ write_string(&directives_path, &directives)?;
+ }
+ Ok(())
+}
+
+fn find_tests_changed(repo: &Repo) -> Result<Vec<String>> {
+ let files_changed = if let Some(parent) = repo.parent.as_ref() {
+ run(
+ "git",
+ &["diff", "--name-only", &repo.name, &parent, "test/core"],
+ )?
+ .lines()
+ .map(|x| PathBuf::from(x))
+ .collect()
+ } else {
+ find("test/core")
+ };
+
+ let mut tests_changed = Vec::new();
+ for path in files_changed {
+ if path.extension().map(|x| x.to_str().unwrap()) != Some("wast") {
+ continue;
+ }
+
+ let name = path.file_name().unwrap().to_str().unwrap().to_owned();
+ tests_changed.push(name);
+ }
+ Ok(tests_changed)
+}
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml
new file mode 100644
index 0000000000..4329cc10fb
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "wast2js"
+version = "0.1.0"
+authors = ["Ryan Hunt <rhunt@eqrion.net>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+anyhow = "1.0.19"
+wast = "55.0.0"
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/README.md b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/README.md
new file mode 100644
index 0000000000..c06210bb04
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/README.md
@@ -0,0 +1,3 @@
+# wast2js
+
+Mozilla specific converter from `.wast` to `.js` for SpiderMonkey jit-tests.
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/convert.rs b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/convert.rs
new file mode 100644
index 0000000000..c6516cb0d0
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/convert.rs
@@ -0,0 +1,672 @@
+/* Copyright 2021 Mozilla Foundation
+ *
+ * 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.
+ */
+
+use anyhow::{bail, Context as _, Result};
+use std::fmt::Write;
+use std::path::Path;
+use std::str;
+
+use super::out::*;
+
+const HARNESS: &'static str = include_str!("./harness.js");
+const LICENSE: &'static str = include_str!("./license.js");
+
+pub fn harness() -> String {
+ HARNESS.to_string()
+}
+
+pub fn convert<P: AsRef<Path>>(path: P, wast: &str) -> Result<String> {
+ let filename = path.as_ref();
+ let adjust_wast = |mut err: wast::Error| {
+ err.set_path(filename);
+ err.set_text(wast);
+ err
+ };
+
+ let mut lexer = wast::lexer::Lexer::new(wast);
+ // The 'names.wast' spec test has confusable unicode -- disable detection.
+ lexer.allow_confusing_unicode(filename.ends_with("names.wast"));
+ let buf = wast::parser::ParseBuffer::new_with_lexer(lexer).map_err(adjust_wast)?;
+ let ast = wast::parser::parse::<wast::Wast>(&buf).map_err(adjust_wast)?;
+
+ let mut out = String::new();
+
+ writeln!(&mut out, "{}", LICENSE)?;
+ writeln!(&mut out, "// {}", filename.display())?;
+
+ let mut current_instance: Option<usize> = None;
+
+ for directive in ast.directives {
+ let sp = directive.span();
+ let (line, col) = sp.linecol_in(wast);
+ writeln!(&mut out, "")?;
+ convert_directive(
+ directive,
+ &mut current_instance,
+ filename,
+ line,
+ col,
+ wast,
+ &mut out,
+ )
+ .with_context(|| {
+ format!(
+ "failed to convert directive on {}:{}:{}",
+ filename.display(),
+ line + 1,
+ col
+ )
+ })?;
+ }
+
+ Ok(out)
+}
+
+fn convert_directive(
+ directive: wast::WastDirective,
+ current_instance: &mut Option<usize>,
+ filename: &Path,
+ line: usize,
+ col: usize,
+ wast: &str,
+ out: &mut String,
+) -> Result<()> {
+ use wast::WastDirective::*;
+
+ if col == 1 {
+ writeln!(out, "// {}:{}", filename.display(), line + 1)?;
+ } else {
+ writeln!(out, "// {}:{}:{}", filename.display(), line + 1, col)?;
+ }
+ match directive {
+ Wat(wast::QuoteWat::Wat(wast::Wat::Module(module))) => {
+ let next_instance = current_instance.map(|x| x + 1).unwrap_or(0);
+ let module_text = module_to_js_string(&module, wast)?;
+
+ writeln!(
+ out,
+ "let ${} = instantiate(`{}`);",
+ next_instance, module_text
+ )?;
+ if let Some(id) = module.id {
+ writeln!(
+ out,
+ "register(${}, {});",
+ next_instance,
+ format!("`{}`", escape_template_string(id.name()))
+ )?;
+ }
+
+ *current_instance = Some(next_instance);
+ }
+ Wat(_) => {
+ write!(out, "// unsupported quote wat")?;
+ }
+ Register {
+ span: _,
+ name,
+ module,
+ } => {
+ let instanceish = module
+ .map(|x| format!("`{}`", escape_template_string(x.name())))
+ .unwrap_or_else(|| format!("${}", current_instance.unwrap()));
+
+ writeln!(
+ out,
+ "register({}, `{}`);",
+ instanceish,
+ escape_template_string(name)
+ )?;
+ }
+ Invoke(i) => {
+ let invoke_node = invoke_to_js(current_instance, i)?;
+ writeln!(out, "{};", invoke_node.output(0))?;
+ }
+ AssertReturn {
+ span: _,
+ exec,
+ results,
+ } => {
+ let exec_node = execute_to_js(current_instance, exec, wast)?;
+ let expected_node = to_js_value_array(&results, assert_expression_to_js_value)?;
+ writeln!(
+ out,
+ "{};",
+ JSNode::Assert {
+ name: "assert_return".to_string(),
+ exec: exec_node,
+ expected: expected_node,
+ }
+ .output(0),
+ )?;
+ }
+ AssertTrap {
+ span: _,
+ exec,
+ message,
+ } => {
+ let exec_node = execute_to_js(current_instance, exec, wast)?;
+ let expected_node = Box::new(JSNode::Raw(format!(
+ "`{}`",
+ escape_template_string(message)
+ )));
+ writeln!(
+ out,
+ "{};",
+ JSNode::Assert {
+ name: "assert_trap".to_string(),
+ exec: exec_node,
+ expected: expected_node,
+ }
+ .output(0),
+ )?;
+ }
+ AssertExhaustion {
+ span: _,
+ call,
+ message,
+ } => {
+ let exec_node = invoke_to_js(current_instance, call)?;
+ let expected_node = Box::new(JSNode::Raw(format!(
+ "`{}`",
+ escape_template_string(message)
+ )));
+ writeln!(
+ out,
+ "{};",
+ JSNode::Assert {
+ name: "assert_exhaustion".to_string(),
+ exec: exec_node,
+ expected: expected_node,
+ }
+ .output(0),
+ )?;
+ }
+ AssertInvalid {
+ span: _,
+ module,
+ message,
+ } => {
+ let text = match module {
+ wast::QuoteWat::Wat(wast::Wat::Module(m)) => module_to_js_string(&m, wast)?,
+ wast::QuoteWat::QuoteModule(_, source) => quote_module_to_js_string(source)?,
+ other => bail!("unsupported {:?} in assert_invalid", other),
+ };
+ let exec = Box::new(JSNode::Raw(format!("instantiate(`{}`)", text)));
+ let expected_node = Box::new(JSNode::Raw(format!(
+ "`{}`",
+ escape_template_string(message)
+ )));
+ writeln!(
+ out,
+ "{};",
+ JSNode::Assert {
+ name: "assert_invalid".to_string(),
+ exec: exec,
+ expected: expected_node,
+ }
+ .output(0),
+ )?;
+ }
+ AssertMalformed {
+ module,
+ span: _,
+ message,
+ } => {
+ let text = match module {
+ wast::QuoteWat::Wat(wast::Wat::Module(m)) => module_to_js_string(&m, wast)?,
+ wast::QuoteWat::QuoteModule(_, source) => quote_module_to_js_string(source)?,
+ other => bail!("unsupported {:?} in assert_malformed", other),
+ };
+ let exec = Box::new(JSNode::Raw(format!("instantiate(`{}`)", text)));
+ let expected_node = Box::new(JSNode::Raw(format!(
+ "`{}`",
+ escape_template_string(message)
+ )));
+ writeln!(
+ out,
+ "{};",
+ JSNode::Assert {
+ name: "assert_malformed".to_string(),
+ exec: exec,
+ expected: expected_node,
+ }
+ .output(0),
+ )?;
+ }
+ AssertUnlinkable {
+ span: _,
+ module,
+ message,
+ } => {
+ let text = match module {
+ wast::Wat::Module(module) => module_to_js_string(&module, wast)?,
+ other => bail!("unsupported {:?} in assert_unlinkable", other),
+ };
+ let exec = Box::new(JSNode::Raw(format!("instantiate(`{}`)", text)));
+ let expected_node = Box::new(JSNode::Raw(format!(
+ "`{}`",
+ escape_template_string(message)
+ )));
+ writeln!(
+ out,
+ "{};",
+ JSNode::Assert {
+ name: "assert_unlinkable".to_string(),
+ exec: exec,
+ expected: expected_node,
+ }
+ .output(0),
+ )?;
+ }
+ AssertException { span: _, exec } => {
+ // This assert doesn't have a second parameter, so we don't bother
+ // formatting it using an Assert node.
+ let exec_node = execute_to_js(current_instance, exec, wast)?;
+ writeln!(out, "assert_exception(() => {});", exec_node.output(0))?;
+ }
+ }
+
+ Ok(())
+}
+
+fn escape_template_string(text: &str) -> String {
+ let mut escaped = String::new();
+ for c in text.chars() {
+ match c {
+ '$' => escaped.push_str("$$"),
+ '\\' => escaped.push_str("\\\\"),
+ '`' => escaped.push_str("\\`"),
+ c if c.is_ascii_control() && c != '\n' && c != '\t' => {
+ escaped.push_str(&format!("\\x{:02x}", c as u32))
+ }
+ c if !c.is_ascii() => escaped.push_str(&c.escape_unicode().to_string()),
+ c => escaped.push(c),
+ }
+ }
+ escaped
+}
+
+fn span_to_offset(span: wast::token::Span, text: &str) -> Result<usize> {
+ let (span_line, span_col) = span.linecol_in(text);
+ let mut cur = 0;
+ // Use split_terminator instead of lines so that if there is a `\r`,
+ // it is included in the offset calculation. The `+1` values below
+ // account for the `\n`.
+ for (i, line) in text.split_terminator('\n').enumerate() {
+ if span_line == i {
+ assert!(span_col < line.len());
+ return Ok(cur + span_col);
+ }
+ cur += line.len() + 1;
+ }
+ bail!("invalid line/col");
+}
+
+fn closed_module(module: &str) -> Result<&str> {
+ enum State {
+ Module,
+ QStr,
+ EscapeQStr,
+ }
+
+ let mut i = 0;
+ let mut level = 1;
+ let mut state = State::Module;
+
+ let mut chars = module.chars();
+ while level != 0 {
+ let next = match chars.next() {
+ Some(next) => next,
+ None => bail!("was unable to close module"),
+ };
+ match state {
+ State::Module => match next {
+ '(' => level += 1,
+ ')' => level -= 1,
+ '"' => state = State::QStr,
+ _ => {}
+ },
+ State::QStr => match next {
+ '"' => state = State::Module,
+ '\\' => state = State::EscapeQStr,
+ _ => {}
+ },
+ State::EscapeQStr => match next {
+ _ => state = State::QStr,
+ },
+ }
+ i += next.len_utf8();
+ }
+ return Ok(&module[0..i]);
+}
+
+fn module_to_js_string(module: &wast::core::Module, wast: &str) -> Result<String> {
+ let offset = span_to_offset(module.span, wast)?;
+ let opened_module = &wast[offset..];
+ if !opened_module.starts_with("module") {
+ return Ok(escape_template_string(opened_module));
+ }
+ Ok(escape_template_string(&format!(
+ "({}",
+ closed_module(opened_module)?
+ )))
+}
+
+fn quote_module_to_js_string(quotes: Vec<(wast::token::Span, &[u8])>) -> Result<String> {
+ let mut text = String::new();
+ for (_, src) in quotes {
+ text.push_str(str::from_utf8(src)?);
+ text.push_str(" ");
+ }
+ let escaped = escape_template_string(&text);
+ Ok(escaped)
+}
+
+fn invoke_to_js(current_instance: &Option<usize>, i: wast::WastInvoke) -> Result<Box<JSNode>> {
+ let instanceish = i
+ .module
+ .map(|x| format!("`{}`", escape_template_string(x.name())))
+ .unwrap_or_else(|| format!("${}", current_instance.unwrap()));
+ let body = to_js_value_array(&i.args, arg_to_js_value)?;
+
+ Ok(Box::new(JSNode::Invoke {
+ instance: instanceish,
+ name: escape_template_string(i.name),
+ body: body,
+ }))
+}
+
+fn execute_to_js(
+ current_instance: &Option<usize>,
+ exec: wast::WastExecute,
+ wast: &str,
+) -> Result<Box<JSNode>> {
+ match exec {
+ wast::WastExecute::Invoke(invoke) => invoke_to_js(current_instance, invoke),
+ wast::WastExecute::Wat(module) => {
+ let text = match module {
+ wast::Wat::Module(module) => module_to_js_string(&module, wast)?,
+ other => bail!("unsupported {:?} at execute_to_js", other),
+ };
+ Ok(Box::new(JSNode::Raw(format!("instantiate(`{}`)", text))))
+ }
+ wast::WastExecute::Get { module, global } => {
+ let instanceish = module
+ .map(|x| format!("`{}`", escape_template_string(x.name())))
+ .unwrap_or_else(|| format!("${}", current_instance.unwrap()));
+ Ok(Box::new(JSNode::Raw(format!(
+ "get({}, `{}`)",
+ instanceish, global
+ ))))
+ }
+ }
+}
+
+fn to_js_value_array<T, F: Fn(&T) -> Result<String>>(vals: &[T], func: F) -> Result<Box<JSNode>> {
+ let mut value_nodes: Vec<Box<JSNode>> = vec![];
+ for val in vals {
+ let converted_value = (func)(val)?;
+ value_nodes.push(Box::new(JSNode::Raw(converted_value)));
+ }
+
+ Ok(Box::new(JSNode::Array(value_nodes)))
+}
+
+fn to_js_value_array_string<T, F: Fn(&T) -> Result<String>>(vals: &[T], func: F) -> Result<String> {
+ let array = to_js_value_array(vals, func)?;
+ Ok(array.output(NOWRAP))
+}
+
+fn f32_needs_bits(a: f32) -> bool {
+ if a.is_infinite() {
+ return false;
+ }
+ return a.is_nan()
+ || ((a as f64) as f32).to_bits() != a.to_bits()
+ || (format!("{:.}", a).parse::<f64>().unwrap() as f32).to_bits() != a.to_bits();
+}
+
+fn f64_needs_bits(a: f64) -> bool {
+ return a.is_nan();
+}
+
+fn f32x4_needs_bits(a: &[wast::token::Float32; 4]) -> bool {
+ a.iter().any(|x| {
+ let as_f32 = f32::from_bits(x.bits);
+ f32_needs_bits(as_f32)
+ })
+}
+
+fn f64x2_needs_bits(a: &[wast::token::Float64; 2]) -> bool {
+ a.iter().any(|x| {
+ let as_f64 = f64::from_bits(x.bits);
+ f64_needs_bits(as_f64)
+ })
+}
+
+fn f32_to_js_value(val: f32) -> String {
+ if val == f32::INFINITY {
+ format!("Infinity")
+ } else if val == f32::NEG_INFINITY {
+ format!("-Infinity")
+ } else if val.is_sign_negative() && val == 0f32 {
+ format!("-0")
+ } else {
+ format!("{:.}", val)
+ }
+}
+
+fn f64_to_js_value(val: f64) -> String {
+ if val == f64::INFINITY {
+ format!("Infinity")
+ } else if val == f64::NEG_INFINITY {
+ format!("-Infinity")
+ } else if val.is_sign_negative() && val == 0f64 {
+ format!("-0")
+ } else {
+ format!("{:.}", val)
+ }
+}
+
+fn float32_to_js_value(val: &wast::token::Float32) -> String {
+ let as_f32 = f32::from_bits(val.bits);
+ if f32_needs_bits(as_f32) {
+ format!(
+ "bytes(\"f32\", {})",
+ to_js_value_array_string(&val.bits.to_le_bytes(), |x| Ok(format!("0x{:x}", x)))
+ .unwrap(),
+ )
+ } else {
+ format!("value(\"f32\", {})", f32_to_js_value(as_f32))
+ }
+}
+
+fn float64_to_js_value(val: &wast::token::Float64) -> String {
+ let as_f64 = f64::from_bits(val.bits);
+ if f64_needs_bits(as_f64) {
+ format!(
+ "bytes(\"f64\", {})",
+ to_js_value_array_string(&val.bits.to_le_bytes(), |x| Ok(format!("0x{:x}", x)))
+ .unwrap(),
+ )
+ } else {
+ format!("value(\"f64\", {})", f64_to_js_value(as_f64))
+ }
+}
+
+fn f32_pattern_to_js_value(pattern: &wast::core::NanPattern<wast::token::Float32>) -> String {
+ use wast::core::NanPattern::*;
+ match pattern {
+ CanonicalNan => format!("`canonical_nan`"),
+ ArithmeticNan => format!("`arithmetic_nan`"),
+ Value(x) => float32_to_js_value(x),
+ }
+}
+
+fn f64_pattern_to_js_value(pattern: &wast::core::NanPattern<wast::token::Float64>) -> String {
+ use wast::core::NanPattern::*;
+ match pattern {
+ CanonicalNan => format!("`canonical_nan`"),
+ ArithmeticNan => format!("`arithmetic_nan`"),
+ Value(x) => float64_to_js_value(x),
+ }
+}
+
+fn return_value_to_js_value(v: &wast::core::WastRetCore<'_>) -> Result<String> {
+ use wast::core::WastRetCore::*;
+ Ok(match v {
+ I32(x) => format!("value(\"i32\", {})", x),
+ I64(x) => format!("value(\"i64\", {}n)", x),
+ F32(x) => f32_pattern_to_js_value(x),
+ F64(x) => f64_pattern_to_js_value(x),
+ RefNull(x) => match x {
+ Some(wast::core::HeapType::Func) => format!("value('anyfunc', null)"),
+ Some(wast::core::HeapType::Extern) => format!("value('externref', null)"),
+ other => bail!(
+ "couldn't convert ref.null {:?} to a js assertion value",
+ other
+ ),
+ },
+ RefExtern(x) => format!("value('externref', externref({}))", x),
+ V128(x) => {
+ use wast::core::V128Pattern::*;
+ match x {
+ I8x16(elements) => format!(
+ "i8x16({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}", x)))?,
+ ),
+ I16x8(elements) => format!(
+ "i16x8({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}", x)))?,
+ ),
+ I32x4(elements) => format!(
+ "i32x4({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}", x)))?,
+ ),
+ I64x2(elements) => format!(
+ "i64x2({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}n", x)))?,
+ ),
+ F32x4(elements) => {
+ let elements: Vec<String> = elements
+ .iter()
+ .map(|x| f32_pattern_to_js_value(x))
+ .collect();
+ let elements = strings_to_raws(elements);
+ JSNode::Call {
+ name: "new F32x4Pattern".to_string(),
+ args: elements,
+ }
+ .output(0)
+ }
+ F64x2(elements) => {
+ let elements: Vec<String> = elements
+ .iter()
+ .map(|x| f64_pattern_to_js_value(x))
+ .collect();
+ let elements = strings_to_raws(elements);
+ JSNode::Call {
+ name: "new F64x2Pattern".to_string(),
+ args: elements,
+ }
+ .output(0)
+ }
+ }
+ }
+ Either(v) => {
+ let args = v
+ .iter()
+ .map(|v| return_value_to_js_value(v).unwrap())
+ .collect();
+ let args = strings_to_raws(args);
+ JSNode::Call {
+ name: "either".to_string(),
+ args: args,
+ }
+ .output(0)
+ }
+ other => bail!("couldn't convert Core({:?}) to a js assertion value", other),
+ })
+}
+
+fn assert_expression_to_js_value(v: &wast::WastRet<'_>) -> Result<String> {
+ use wast::WastRet::*;
+
+ Ok(match &v {
+ Core(x) => return_value_to_js_value(x)?,
+ other => bail!("couldn't convert {:?} to a js assertion value", other),
+ })
+}
+
+fn arg_to_js_value(v: &wast::WastArg<'_>) -> Result<String> {
+ use wast::core::WastArgCore::*;
+ use wast::WastArg::Core;
+
+ Ok(match &v {
+ Core(I32(x)) => format!("{}", *x),
+ Core(I64(x)) => format!("{}n", *x),
+ Core(F32(x)) => float32_to_js_value(x),
+ Core(F64(x)) => float64_to_js_value(x),
+ Core(V128(x)) => {
+ use wast::core::V128Const::*;
+ match x {
+ I8x16(elements) => format!(
+ "i8x16({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}", x)))?,
+ ),
+ I16x8(elements) => format!(
+ "i16x8({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}", x)))?,
+ ),
+ I32x4(elements) => format!(
+ "i32x4({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}", x)))?,
+ ),
+ I64x2(elements) => format!(
+ "i64x2({})",
+ to_js_value_array_string(elements, |x| Ok(format!("0x{:x}n", x)))?,
+ ),
+ F32x4(elements) => {
+ if f32x4_needs_bits(elements) {
+ let bytes =
+ to_js_value_array(&x.to_le_bytes(), |x| Ok(format!("0x{:x}", x)))?;
+ format!("bytes('v128', {})", bytes.output(0))
+ } else {
+ let vals = to_js_value_array(elements, |x| {
+ Ok(f32_to_js_value(f32::from_bits(x.bits)))
+ })?;
+ format!("f32x4({})", vals.output(0))
+ }
+ }
+ F64x2(elements) => {
+ if f64x2_needs_bits(elements) {
+ let bytes =
+ to_js_value_array(&x.to_le_bytes(), |x| Ok(format!("0x{:x}", x)))?;
+ format!("bytes('v128', {})", bytes.output(0))
+ } else {
+ let vals = to_js_value_array(elements, |x| {
+ Ok(f64_to_js_value(f64::from_bits(x.bits)))
+ })?;
+ format!("f64x2({})", vals.output(0))
+ }
+ }
+ }
+ }
+ Core(RefNull(_)) => format!("null"),
+ Core(RefExtern(x)) => format!("externref({})", x),
+ other => bail!("couldn't convert {:?} to a js value", other),
+ })
+}
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/harness.js b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/harness.js
new file mode 100644
index 0000000000..ba615e05dd
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/harness.js
@@ -0,0 +1,360 @@
+"use strict";
+
+/* Copyright 2021 Mozilla Foundation
+ *
+ * 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.
+ */
+
+if (!wasmIsSupported()) {
+ quit();
+}
+
+function bytes(type, bytes) {
+ var typedBuffer = new Uint8Array(bytes);
+ return wasmGlobalFromArrayBuffer(type, typedBuffer.buffer);
+}
+function value(type, value) {
+ return new WebAssembly.Global({
+ value: type,
+ mutable: false,
+ }, value);
+}
+
+function i8x16(elements) {
+ let typedBuffer = new Uint8Array(elements);
+ return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer);
+}
+function i16x8(elements) {
+ let typedBuffer = new Uint16Array(elements);
+ return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer);
+}
+function i32x4(elements) {
+ let typedBuffer = new Uint32Array(elements);
+ return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer);
+}
+function i64x2(elements) {
+ let typedBuffer = new BigUint64Array(elements);
+ return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer);
+}
+function f32x4(elements) {
+ let typedBuffer = new Float32Array(elements);
+ return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer);
+}
+function f64x2(elements) {
+ let typedBuffer = new Float64Array(elements);
+ return wasmGlobalFromArrayBuffer("v128", typedBuffer.buffer);
+}
+
+function either(...arr) {
+ return new EitherVariants(arr);
+}
+
+class F32x4Pattern {
+ constructor(x, y, z, w) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.w = w;
+ }
+}
+
+class F64x2Pattern {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ }
+}
+
+let externrefs = {};
+let externsym = Symbol("externref");
+function externref(s) {
+ if (!(s in externrefs)) externrefs[s] = { [externsym]: s };
+ return externrefs[s];
+}
+function is_externref(x) {
+ return (x !== null && externsym in x) ? 1 : 0;
+}
+function is_funcref(x) {
+ return typeof x === "function" ? 1 : 0;
+}
+function eq_externref(x, y) {
+ return x === y ? 1 : 0;
+}
+function eq_funcref(x, y) {
+ return x === y ? 1 : 0;
+}
+
+let spectest = {
+ externref: externref,
+ is_externref: is_externref,
+ is_funcref: is_funcref,
+ eq_externref: eq_externref,
+ eq_funcref: eq_funcref,
+ print: console.log.bind(console),
+ print_i32: console.log.bind(console),
+ print_i32_f32: console.log.bind(console),
+ print_f64_f64: console.log.bind(console),
+ print_f32: console.log.bind(console),
+ print_f64: console.log.bind(console),
+ global_i32: 666,
+ global_i64: 666n,
+ global_f32: 666,
+ global_f64: 666,
+ table: new WebAssembly.Table({
+ initial: 10,
+ maximum: 20,
+ element: "anyfunc",
+ }),
+ memory: new WebAssembly.Memory({ initial: 1, maximum: 2 }),
+};
+
+let linkage = {
+ spectest,
+};
+
+function getInstance(instanceish) {
+ if (typeof instanceish === "string") {
+ assertEq(
+ instanceish in linkage,
+ true,
+ `'${instanceish}'' must be registered`,
+ );
+ return linkage[instanceish];
+ }
+ return instanceish;
+}
+
+function instantiate(source) {
+ let bytecode = wasmTextToBinary(source);
+ let module = new WebAssembly.Module(bytecode);
+ let instance = new WebAssembly.Instance(module, linkage);
+ return instance.exports;
+}
+
+function register(instanceish, name) {
+ linkage[name] = getInstance(instanceish);
+}
+
+function invoke(instanceish, field, params) {
+ let func = getInstance(instanceish)[field];
+ assertEq(func instanceof Function, true, "expected a function");
+ return wasmLosslessInvoke(func, ...params);
+}
+
+function get(instanceish, field) {
+ let global = getInstance(instanceish)[field];
+ assertEq(
+ global instanceof WebAssembly.Global,
+ true,
+ "expected a WebAssembly.Global",
+ );
+ return global;
+}
+
+function assert_trap(thunk, message) {
+ try {
+ thunk();
+ assertEq("normal return", "trap");
+ } catch (err) {
+ assertEq(
+ err instanceof WebAssembly.RuntimeError,
+ true,
+ "expected trap",
+ );
+ }
+}
+
+let StackOverflow;
+try {
+ (function f() {
+ 1 + f();
+ })();
+} catch (e) {
+ StackOverflow = e.constructor;
+}
+function assert_exhaustion(thunk, message) {
+ try {
+ thunk();
+ assertEq("normal return", "exhaustion");
+ } catch (err) {
+ assertEq(
+ err instanceof StackOverflow,
+ true,
+ "expected exhaustion",
+ );
+ }
+}
+
+function assert_invalid(thunk, message) {
+ try {
+ thunk();
+ assertEq("valid module", "invalid module");
+ } catch (err) {
+ assertEq(
+ err instanceof WebAssembly.LinkError ||
+ err instanceof WebAssembly.CompileError,
+ true,
+ "expected an invalid module",
+ );
+ }
+}
+
+function assert_unlinkable(thunk, message) {
+ try {
+ thunk();
+ assertEq(true, false, "expected an unlinkable module");
+ } catch (err) {
+ assertEq(
+ err instanceof WebAssembly.LinkError ||
+ err instanceof WebAssembly.CompileError,
+ true,
+ "expected an unlinkable module",
+ );
+ }
+}
+
+function assert_malformed(thunk, message) {
+ try {
+ thunk();
+ assertEq("valid module", "malformed module");
+ } catch (err) {
+ assertEq(
+ err instanceof TypeError ||
+ err instanceof SyntaxError ||
+ err instanceof WebAssembly.CompileError ||
+ err instanceof WebAssembly.LinkError,
+ true,
+ `expected a malformed module`,
+ );
+ }
+}
+
+function assert_exception(thunk) {
+ let thrown = false;
+ try {
+ thunk();
+ } catch (err) {
+ thrown = true;
+ }
+ assertEq(thrown, true, "expected an exception to be thrown");
+}
+
+function assert_return(thunk, expected) {
+ let results = thunk();
+
+ if (results === undefined) {
+ results = [];
+ } else if (!Array.isArray(results)) {
+ results = [results];
+ }
+ if (!Array.isArray(expected)) {
+ expected = [expected];
+ }
+
+ if (!compareResults(results, expected)) {
+ let got = results.map((x) => formatResult(x)).join(", ");
+ let wanted = expected.map((x) => formatExpected(x)).join(", ");
+ assertEq(
+ `[${got}]`,
+ `[${wanted}]`,
+ );
+ assertEq(true, false, `${got} !== ${wanted}`);
+ }
+}
+
+function formatResult(result) {
+ if (typeof (result) === "object") {
+ return wasmGlobalToString(result);
+ } else {
+ return `${result}`;
+ }
+}
+
+function formatExpected(expected) {
+ if (
+ expected === `f32_canonical_nan` ||
+ expected === `f32_arithmetic_nan` ||
+ expected === `f64_canonical_nan` ||
+ expected === `f64_arithmetic_nan`
+ ) {
+ return expected;
+ } else if (expected instanceof F32x4Pattern) {
+ return `f32x4(${formatExpected(expected.x)}, ${
+ formatExpected(expected.y)
+ }, ${formatExpected(expected.z)}, ${formatExpected(expected.w)})`;
+ } else if (expected instanceof F64x2Pattern) {
+ return `f64x2(${formatExpected(expected.x)}, ${
+ formatExpected(expected.y)
+ })`;
+ } else if (expected instanceof EitherVariants) {
+ return expected.formatExpected();
+ } else if (typeof (expected) === "object") {
+ return wasmGlobalToString(expected);
+ } else {
+ throw new Error("unknown expected result");
+ }
+}
+
+class EitherVariants {
+ constructor(arr) {
+ this.arr = arr;
+ }
+ matches(v) {
+ return this.arr.some((e) => compareResult(v, e));
+ }
+ formatExpected() {
+ return `either(${this.arr.map(formatExpected).join(", ")})`;
+ }
+}
+
+function compareResults(results, expected) {
+ if (results.length !== expected.length) {
+ return false;
+ }
+ for (let i in results) {
+ if (expected[i] instanceof EitherVariants) {
+ return expected[i].matches(results[i]);
+ }
+ if (!compareResult(results[i], expected[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function compareResult(result, expected) {
+ if (
+ expected === `canonical_nan` ||
+ expected === `arithmetic_nan`
+ ) {
+ return wasmGlobalIsNaN(result, expected);
+ } else if (expected instanceof F32x4Pattern) {
+ return compareResult(
+ wasmGlobalExtractLane(result, "f32x4", 0),
+ expected.x,
+ ) &&
+ compareResult(wasmGlobalExtractLane(result, "f32x4", 1), expected.y) &&
+ compareResult(wasmGlobalExtractLane(result, "f32x4", 2), expected.z) &&
+ compareResult(wasmGlobalExtractLane(result, "f32x4", 3), expected.w);
+ } else if (expected instanceof F64x2Pattern) {
+ return compareResult(
+ wasmGlobalExtractLane(result, "f64x2", 0),
+ expected.x,
+ ) &&
+ compareResult(wasmGlobalExtractLane(result, "f64x2", 1), expected.y);
+ } else if (typeof (expected) === "object") {
+ return wasmGlobalsEqual(result, expected);
+ } else {
+ throw new Error("unknown expected result");
+ }
+}
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/lib.rs b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/lib.rs
new file mode 100644
index 0000000000..b62bcbfaea
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/lib.rs
@@ -0,0 +1,18 @@
+/* Copyright 2021 Mozilla Foundation
+ *
+ * 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.
+ */
+
+mod convert;
+mod out;
+pub use convert::*;
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/license.js b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/license.js
new file mode 100644
index 0000000000..12d51bd702
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/license.js
@@ -0,0 +1,14 @@
+/* Copyright 2021 Mozilla Foundation
+ *
+ * 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.
+ */
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/main.rs b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/main.rs
new file mode 100644
index 0000000000..d5d1118e7e
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/main.rs
@@ -0,0 +1,40 @@
+/* Copyright 2021 Mozilla Foundation
+ *
+ * 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.
+ */
+
+use anyhow::{Context as _, Result};
+use std::env;
+use std::path::Path;
+
+mod convert;
+mod out;
+
+fn main() -> Result<()> {
+ let files = env::args().collect::<Vec<String>>();
+ for path in &files[1..] {
+ let source =
+ std::fs::read_to_string(path).with_context(|| format!("failed to read `{}`", path))?;
+
+ let mut full_script = String::new();
+ full_script.push_str(&convert::harness());
+ full_script.push_str(&convert::convert(path, &source)?);
+
+ std::fs::write(
+ Path::new(path).with_extension("js").file_name().unwrap(),
+ &full_script,
+ )?;
+ }
+
+ Ok(())
+}
diff --git a/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/out.rs b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/out.rs
new file mode 100644
index 0000000000..1d47365f19
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/generate-spectests/wast2js/src/out.rs
@@ -0,0 +1,180 @@
+/* Copyright 2022 Mozilla Foundation
+ *
+ * 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.
+ */
+
+/// This module has a bare-bones "syntax tree" that helps us output more readable JS code in our
+/// tests. This is a lot cheaper and faster than running a full JS formatter on the generated code.
+/// The tree only has node types for things that matter to formatting; most things in the output
+/// use the Raw type, which is just a plain old string.
+
+pub const LINE_WIDTH: usize = 80; // the default line width to try to meet (will not be met perfectly)
+pub const NOWRAP: usize = 1000; // use for line_remaining when you don't want to wrap text
+
+pub enum JSNode {
+ Assert {
+ name: String,
+ exec: Box<JSNode>,
+ expected: Box<JSNode>,
+ },
+ Invoke {
+ instance: String,
+ name: String,
+ body: Box<JSNode>,
+ },
+ Call {
+ name: String,
+ args: Vec<Box<JSNode>>,
+ },
+ Array(Vec<Box<JSNode>>),
+ Raw(String),
+}
+
+impl JSNode {
+ /// Converts a node to JavaScript code. line_remaining is the number of characters remaining on
+ /// the line, used for line-wrapping purposes; if zero, the default LINE_WIDTH will be used.
+ pub fn output(&self, line_remaining: usize) -> String {
+ let line_remaining = if line_remaining == 0 {
+ LINE_WIDTH
+ } else {
+ line_remaining
+ };
+ match self {
+ Self::Assert {
+ name,
+ exec,
+ expected,
+ } => {
+ if self.len() > line_remaining {
+ format!(
+ "{}(\n{}\n)",
+ name,
+ indent(format!(
+ "() => {},\n{},",
+ exec.output(line_remaining - 8),
+ expected.output(line_remaining - 2),
+ )),
+ )
+ } else {
+ format!(
+ "{}(() => {}, {})",
+ name,
+ exec.output(NOWRAP),
+ expected.output(NOWRAP),
+ )
+ }
+ }
+ Self::Invoke {
+ instance,
+ name,
+ body,
+ } => {
+ let body_string =
+ body.output(line_remaining - "invoke".len() - instance.len() - name.len());
+ format!("invoke({}, `{}`, {})", instance, name, body_string)
+ }
+ Self::Call { name, args } => {
+ if self.len() > line_remaining {
+ let arg_strings: Vec<String> = args
+ .iter()
+ .map(|arg| arg.output(line_remaining - 2))
+ .collect();
+ format!("{}(\n{},\n)", name, indent(arg_strings.join(",\n")))
+ } else {
+ let arg_strings: Vec<String> =
+ args.iter().map(|arg| arg.output(NOWRAP)).collect();
+ format!("{}({})", name, arg_strings.join(", "))
+ }
+ }
+ Self::Array(values) => {
+ if self.len() > line_remaining {
+ let value_strings = output_nodes(&values, 70);
+ format!("[\n{},\n]", indent(value_strings.join(",\n")))
+ } else {
+ let value_strings = output_nodes(&values, 80);
+ format!("[{}]", value_strings.join(", "))
+ }
+ }
+ Self::Raw(val) => val.to_string(),
+ }
+ }
+
+ /// A rough estimate of the string length of the node. Used as a heuristic to know when we
+ /// should wrap text.
+ fn len(&self) -> usize {
+ match self {
+ Self::Assert {
+ name,
+ exec,
+ expected,
+ } => name.len() + exec.len() + expected.len(),
+ Self::Invoke {
+ instance,
+ name,
+ body,
+ } => instance.len() + name.len() + body.len(),
+ Self::Call { name, args } => {
+ let mut args_len: usize = 0;
+ for node in args {
+ args_len += node.len() + ", ".len();
+ }
+ name.len() + args_len
+ }
+ Self::Array(nodes) => {
+ let mut content_len: usize = 0;
+ for node in nodes {
+ content_len += node.len() + ", ".len();
+ }
+ content_len
+ }
+ Self::Raw(s) => s.len(),
+ }
+ }
+}
+
+pub fn output_nodes(nodes: &Vec<Box<JSNode>>, line_width_per_node: usize) -> Vec<String> {
+ nodes
+ .iter()
+ .map(|node| node.output(line_width_per_node))
+ .collect()
+}
+
+pub fn strings_to_raws(strs: Vec<String>) -> Vec<Box<JSNode>> {
+ let mut res: Vec<Box<JSNode>> = vec![];
+ for s in strs {
+ res.push(Box::new(JSNode::Raw(s)));
+ }
+ res
+}
+
+fn indent(s: String) -> String {
+ let mut result = String::new();
+ let mut do_indent = true;
+ for (i, line) in s.lines().enumerate() {
+ if i > 0 {
+ result.push('\n');
+ }
+ if line.chars().any(|c| !c.is_whitespace()) {
+ if do_indent {
+ result.push_str(" ");
+ }
+ result.push_str(line);
+
+ // An odd number of backticks in the line means we are entering or exiting a raw string.
+ if line.matches("`").count() % 2 == 1 {
+ do_indent = !do_indent
+ }
+ }
+ }
+ result
+}
diff --git a/js/src/jit-test/etc/wasm/spec-tests.patch b/js/src/jit-test/etc/wasm/spec-tests.patch
new file mode 100644
index 0000000000..d0de76b1ab
--- /dev/null
+++ b/js/src/jit-test/etc/wasm/spec-tests.patch
@@ -0,0 +1,784 @@
+# HG changeset patch
+# User Ryan Hunt <rhunt@eqrion.net>
+# Date 1634962581 0
+# Sat Oct 23 04:16:21 2021 +0000
+# Node ID 40a38b62293c892ef4e397c43179321edccea2c3
+# Parent bb4199affde55df21fd07f2fbf65155bc60a0943
+Spec test patches rollup.
+
+1. Bug 1737225 - Disable some tests on arm. r=yury
+
+Disable a partial-oob test on arm/arm64 because some hardware will
+perform byte-at-a-time writes at the end of the heap, and we have
+not fixed that yet.
+
+user: Lars Hansen
+Differential Revision: https://phabricator.services.mozilla.com/D129248
+
+2. Bug 1747450 - simd has moved to spec and needs per-test skips.
+user: Ryan Hunt
+
+diff --git a/js/src/jit-test/tests/wasm/spec/memory64/align64.wast.js b/js/src/jit-test/tests/wasm/spec/memory64/align64.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/memory64/align64.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/memory64/align64.wast.js
+@@ -1076,8 +1076,15 @@ let $24 = instantiate(`(module
+ )
+ )`);
+
+-// ./test/core/align64.wast:864
+-assert_trap(() => invoke($24, `store`, [65532n, -1n]), `out of bounds memory access`);
+-
+-// ./test/core/align64.wast:866
+-assert_return(() => invoke($24, `load`, [65532n]), [value("i32", 0)]);
++// Bug 1737225 - do not observe the partial store caused by bug 1666747 on
++// some native platforms.
++if (!partialOobWriteMayWritePartialData()) {
++ // ./test/core/align64.wast:864
++ assert_trap(
++ () => invoke($24, `store`, [65532n, -1n]),
++ `out of bounds memory access`,
++ );
++
++ // ./test/core/align64.wast:866
++ assert_return(() => invoke($24, `load`, [65532n]), [value("i32", 0)]);
++}
+diff --git a/js/src/jit-test/tests/wasm/spec/memory64/harness/harness.js b/js/src/jit-test/tests/wasm/spec/memory64/harness/harness.js
+--- a/js/src/jit-test/tests/wasm/spec/memory64/harness/harness.js
++++ b/js/src/jit-test/tests/wasm/spec/memory64/harness/harness.js
+@@ -19,6 +19,17 @@ if (!wasmIsSupported()) {
+ quit();
+ }
+
++function partialOobWriteMayWritePartialData() {
++ let cfg = getBuildConfiguration();
++ let arm_native = cfg["arm"] && !cfg["arm-simulator"];
++ let arm64_native = cfg["arm64"] && !cfg["arm64-simulator"];
++ return arm_native || arm64_native;
++}
++
++let cfg = getBuildConfiguration();
++let native_arm = cfg["arm"] && !cfg["arm-simulator"];
++let native_arm64 = cfg["arm64"] && !cfg["arm64-simulator"];
++
+ function bytes(type, bytes) {
+ var typedBuffer = new Uint8Array(bytes);
+ return wasmGlobalFromArrayBuffer(type, typedBuffer.buffer);
+diff --git a/js/src/jit-test/tests/wasm/spec/memory64/memory_trap64.wast.js b/js/src/jit-test/tests/wasm/spec/memory64/memory_trap64.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/memory64/memory_trap64.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/memory64/memory_trap64.wast.js
+@@ -617,8 +617,16 @@ assert_trap(() => invoke($1, `i64.load32_u`, [-3n]), `out of bounds memory acces
+ // ./test/core/memory_trap64.wast:265
+ assert_trap(() => invoke($1, `i64.load32_u`, [-4n]), `out of bounds memory access`);
+
+-// ./test/core/memory_trap64.wast:268
+-assert_return(() => invoke($1, `i64.load`, [65528n]), [value("i64", 7523094288207667809n)]);
+-
+-// ./test/core/memory_trap64.wast:269
+-assert_return(() => invoke($1, `i64.load`, [0n]), [value("i64", 7523094288207667809n)]);
++// Bug 1737225 - do not observe the partial store caused by bug 1666747 on
++// some native platforms.
++if (!partialOobWriteMayWritePartialData()) {
++ // ./test/core/memory_trap64.wast:268
++ assert_return(() => invoke($1, `i64.load`, [65528n]), [
++ value("i64", 7523094288207667809n),
++ ]);
++
++ // ./test/core/memory_trap64.wast:269
++ assert_return(() => invoke($1, `i64.load`, [0n]), [
++ value("i64", 7523094288207667809n),
++ ]);
++}
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_address.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_bit_shift.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_bit_shift.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_bit_shift.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_bit_shift.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_bitwise.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_bitwise.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_bitwise.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_bitwise.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_boolean.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_boolean.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_boolean.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_boolean.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_const.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_const.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_const.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_const.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_conversions.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_conversions.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_conversions.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_conversions.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_cmp.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_cmp.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_cmp.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_cmp.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_pmin_pmax.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_pmin_pmax.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_pmin_pmax.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_pmin_pmax.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_rounding.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_rounding.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_rounding.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f32x4_rounding.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_cmp.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_cmp.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_cmp.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_cmp.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_pmin_pmax.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_pmin_pmax.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_pmin_pmax.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_pmin_pmax.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_rounding.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_rounding.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_rounding.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_f64x2_rounding.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith2.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith2.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith2.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_arith2.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_cmp.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_cmp.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_cmp.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_cmp.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extadd_pairwise_i8x16.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extadd_pairwise_i8x16.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extadd_pairwise_i8x16.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extadd_pairwise_i8x16.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extmul_i8x16.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extmul_i8x16.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extmul_i8x16.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_extmul_i8x16.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_q15mulr_sat_s.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_q15mulr_sat_s.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_q15mulr_sat_s.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_q15mulr_sat_s.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_sat_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_sat_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_sat_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i16x8_sat_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith2.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith2.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith2.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_arith2.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_cmp.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_cmp.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_cmp.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_cmp.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_dot_i16x8.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_dot_i16x8.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_dot_i16x8.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_dot_i16x8.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extadd_pairwise_i16x8.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extadd_pairwise_i16x8.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extadd_pairwise_i16x8.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extadd_pairwise_i16x8.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extmul_i16x8.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extmul_i16x8.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extmul_i16x8.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_extmul_i16x8.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f32x4.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f32x4.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f32x4.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f32x4.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f64x2.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f64x2.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f64x2.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i32x4_trunc_sat_f64x2.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith2.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith2.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith2.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_arith2.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_cmp.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_cmp.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_cmp.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_cmp.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_extmul_i32x4.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_extmul_i32x4.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_extmul_i32x4.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i64x2_extmul_i32x4.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith2.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith2.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith2.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_arith2.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_cmp.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_cmp.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_cmp.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_cmp.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_sat_arith.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_sat_arith.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_sat_arith.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_i8x16_sat_arith.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_int_to_int_extend.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_int_to_int_extend.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_int_to_int_extend.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_int_to_int_extend.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load16_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load16_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load16_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load16_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load32_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load32_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load32_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load32_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load64_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load64_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load64_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load64_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load8_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load8_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load8_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load8_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load_extend.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load_extend.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load_extend.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load_extend.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load_splat.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load_splat.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load_splat.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load_splat.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_load_zero.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_load_zero.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_load_zero.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_load_zero.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_splat.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_splat.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_splat.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_splat.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_store.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_store.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_store.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_store.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_store16_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_store16_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_store16_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_store16_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_store32_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_store32_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_store32_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_store32_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_store64_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_store64_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_store64_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_store64_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/spec/simd_store8_lane.wast.js b/js/src/jit-test/tests/wasm/spec/spec/simd_store8_lane.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/spec/simd_store8_lane.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/spec/simd_store8_lane.wast.js
+@@ -1,3 +1,5 @@
++// |jit-test| skip-if: !wasmSimdEnabled()
++
+ /* Copyright 2021 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+diff --git a/js/src/jit-test/tests/wasm/spec/function-references/call_ref.wast.js b/js/src/jit-test/tests/wasm/spec/function-references/call_ref.wast.js
+TODO non-nullable references not supported in globals
+--- a/js/src/jit-test/tests/wasm/spec/function-references/call_ref.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/function-references/call_ref.wast.js
+@@ -39,74 +39,6 @@ let $0 = instantiate(`(module
+ (func (export "null") (result i32)
+ (call_ref $$ii (i32.const 1) (ref.null $$ii))
+ )
+-
+- ;; Recursion
+-
+- (type $$ll (func (param i64) (result i64)))
+- (type $$lll (func (param i64 i64) (result i64)))
+-
+- (elem declare func $$fac)
+- (global $$fac (ref $$ll) (ref.func $$fac))
+-
+- (func $$fac (export "fac") (type $$ll)
+- (if (result i64) (i64.eqz (local.get 0))
+- (then (i64.const 1))
+- (else
+- (i64.mul
+- (local.get 0)
+- (call_ref $$ll (i64.sub (local.get 0) (i64.const 1)) (global.get $$fac))
+- )
+- )
+- )
+- )
+-
+- (elem declare func $$fac-acc)
+- (global $$fac-acc (ref $$lll) (ref.func $$fac-acc))
+-
+- (func $$fac-acc (export "fac-acc") (type $$lll)
+- (if (result i64) (i64.eqz (local.get 0))
+- (then (local.get 1))
+- (else
+- (call_ref $$lll
+- (i64.sub (local.get 0) (i64.const 1))
+- (i64.mul (local.get 0) (local.get 1))
+- (global.get $$fac-acc)
+- )
+- )
+- )
+- )
+-
+- (elem declare func $$fib)
+- (global $$fib (ref $$ll) (ref.func $$fib))
+-
+- (func $$fib (export "fib") (type $$ll)
+- (if (result i64) (i64.le_u (local.get 0) (i64.const 1))
+- (then (i64.const 1))
+- (else
+- (i64.add
+- (call_ref $$ll (i64.sub (local.get 0) (i64.const 2)) (global.get $$fib))
+- (call_ref $$ll (i64.sub (local.get 0) (i64.const 1)) (global.get $$fib))
+- )
+- )
+- )
+- )
+-
+- (elem declare func $$even $$odd)
+- (global $$even (ref $$ll) (ref.func $$even))
+- (global $$odd (ref $$ll) (ref.func $$odd))
+-
+- (func $$even (export "even") (type $$ll)
+- (if (result i64) (i64.eqz (local.get 0))
+- (then (i64.const 44))
+- (else (call_ref $$ll (i64.sub (local.get 0) (i64.const 1)) (global.get $$odd)))
+- )
+- )
+- (func $$odd (export "odd") (type $$ll)
+- (if (result i64) (i64.eqz (local.get 0))
+- (then (i64.const 99))
+- (else (call_ref $$ll (i64.sub (local.get 0) (i64.const 1)) (global.get $$even)))
+- )
+- )
+ )`);
+
+ // ./test/core/call_ref.wast:94
+@@ -118,69 +50,6 @@ assert_return(() => invoke($0, `run`, [3
+ // ./test/core/call_ref.wast:97
+ assert_trap(() => invoke($0, `null`, []), `null function`);
+
+-// ./test/core/call_ref.wast:99
+-assert_return(() => invoke($0, `fac`, [0n]), [value("i64", 1n)]);
+-
+-// ./test/core/call_ref.wast:100
+-assert_return(() => invoke($0, `fac`, [1n]), [value("i64", 1n)]);
+-
+-// ./test/core/call_ref.wast:101
+-assert_return(() => invoke($0, `fac`, [5n]), [value("i64", 120n)]);
+-
+-// ./test/core/call_ref.wast:102
+-assert_return(() => invoke($0, `fac`, [25n]), [value("i64", 7034535277573963776n)]);
+-
+-// ./test/core/call_ref.wast:103
+-assert_return(() => invoke($0, `fac-acc`, [0n, 1n]), [value("i64", 1n)]);
+-
+-// ./test/core/call_ref.wast:104
+-assert_return(() => invoke($0, `fac-acc`, [1n, 1n]), [value("i64", 1n)]);
+-
+-// ./test/core/call_ref.wast:105
+-assert_return(() => invoke($0, `fac-acc`, [5n, 1n]), [value("i64", 120n)]);
+-
+-// ./test/core/call_ref.wast:106
+-assert_return(() => invoke($0, `fac-acc`, [25n, 1n]), [value("i64", 7034535277573963776n)]);
+-
+-// ./test/core/call_ref.wast:111
+-assert_return(() => invoke($0, `fib`, [0n]), [value("i64", 1n)]);
+-
+-// ./test/core/call_ref.wast:112
+-assert_return(() => invoke($0, `fib`, [1n]), [value("i64", 1n)]);
+-
+-// ./test/core/call_ref.wast:113
+-assert_return(() => invoke($0, `fib`, [2n]), [value("i64", 2n)]);
+-
+-// ./test/core/call_ref.wast:114
+-assert_return(() => invoke($0, `fib`, [5n]), [value("i64", 8n)]);
+-
+-// ./test/core/call_ref.wast:115
+-assert_return(() => invoke($0, `fib`, [20n]), [value("i64", 10946n)]);
+-
+-// ./test/core/call_ref.wast:117
+-assert_return(() => invoke($0, `even`, [0n]), [value("i64", 44n)]);
+-
+-// ./test/core/call_ref.wast:118
+-assert_return(() => invoke($0, `even`, [1n]), [value("i64", 99n)]);
+-
+-// ./test/core/call_ref.wast:119
+-assert_return(() => invoke($0, `even`, [100n]), [value("i64", 44n)]);
+-
+-// ./test/core/call_ref.wast:120
+-assert_return(() => invoke($0, `even`, [77n]), [value("i64", 99n)]);
+-
+-// ./test/core/call_ref.wast:121
+-assert_return(() => invoke($0, `odd`, [0n]), [value("i64", 99n)]);
+-
+-// ./test/core/call_ref.wast:122
+-assert_return(() => invoke($0, `odd`, [1n]), [value("i64", 44n)]);
+-
+-// ./test/core/call_ref.wast:123
+-assert_return(() => invoke($0, `odd`, [200n]), [value("i64", 99n)]);
+-
+-// ./test/core/call_ref.wast:124
+-assert_return(() => invoke($0, `odd`, [77n]), [value("i64", 44n)]);
+-
+ // ./test/core/call_ref.wast:129
+ let $1 = instantiate(`(module
+ (type $$t (func))
+diff --git a/js/src/jit-test/tests/wasm/spec/function-references/local_get.wast.js b/js/src/jit-test/tests/wasm/spec/function-references/local_get.wast.js
+TODO Error: globals are of different type
+--- a/js/src/jit-test/tests/wasm/spec/function-references/local_get.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/function-references/local_get.wast.js
+@@ -297,18 +297,6 @@ let $1 = instantiate(`(module
+ )
+ )`);
+
+-// ./test/core/local_get.wast:248
+-assert_return(() => invoke($1, `get-after-set`, [externref(1)]), [value('externref', externref(1))]);
+-
+-// ./test/core/local_get.wast:249
+-assert_return(() => invoke($1, `get-after-tee`, [externref(2)]), [value('externref', externref(2))]);
+-
+-// ./test/core/local_get.wast:250
+-assert_return(
+- () => invoke($1, `get-in-block-after-set`, [externref(3)]),
+- [value('externref', externref(3))],
+-);
+-
+ // ./test/core/local_get.wast:252
+ assert_invalid(
+ () => instantiate(`(module (func $$uninit (local $$x (ref extern)) (drop (local.get $$x))))`),
+diff --git a/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js b/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js
+--- a/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js
++++ b/js/src/jit-test/tests/wasm/spec/relaxed-simd/i32x4_relaxed_trunc.wast.js
+@@ -92,6 +92,7 @@ assert_return(
+ either(
+ i32x4([0x0, 0x0, 0xffffff00, 0xffffffff]),
+ i32x4([0x0, 0xffffffff, 0xffffff00, 0xffffffff]),
++ i32x4([0x0, 0xffffffff, 0xffffff00, 0x0]),
+ ),
+ ],
+ );
+@@ -122,6 +123,7 @@ assert_return(
+ either(
+ i32x4([0x0, 0x0, 0x0, 0x0]),
+ i32x4([0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff]),
++ i32x4([0x80000000, 0x80000000, 0x80000000, 0x80000000]),
+ ),
+ ],
+ );
+@@ -173,6 +175,7 @@ assert_return(
+ either(
+ i32x4([0x0, 0xffffffff, 0x0, 0x0]),
+ i32x4([0xffffffff, 0xffffffff, 0x0, 0x0]),
++ i32x4([0xfffffffe, 0x0, 0x0, 0x0]),
+ ),
+ ],
+ );