summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/bigint
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/bigint')
-rw-r--r--js/src/jit-test/tests/wasm/bigint/bigint.js468
-rw-r--r--js/src/jit-test/tests/wasm/bigint/bug1633740.js25
-rw-r--r--js/src/jit-test/tests/wasm/bigint/directives.txt1
-rw-r--r--js/src/jit-test/tests/wasm/bigint/stubs.js142
4 files changed, 636 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/bigint/bigint.js b/js/src/jit-test/tests/wasm/bigint/bigint.js
new file mode 100644
index 0000000000..79e7e416b1
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/bigint/bigint.js
@@ -0,0 +1,468 @@
+// Used to ensure tests will trigger Wasm Jit stub code.
+var threshold = 2 * getJitCompilerOptions()["ion.warmup.trigger"] + 10;
+function testWithJit(f) {
+ for (var i = 0; i < threshold; i++) {
+ f();
+ }
+}
+
+function testRet() {
+ var f = wasmEvalText(`(module
+ (func (export "f") (result i64) (i64.const 66))
+ )`).exports.f;
+
+ testWithJit(() => {
+ assertEq(typeof f(), "bigint", "should return a bigint");
+ assertEq(f(), 66n, "should return the correct value");
+ });
+}
+
+function testId() {
+ var exports = wasmEvalText(`(module
+ (func (export "f") (param i64 i64) (result i64)
+ (local.get 0)
+ )
+ (func (export "f2") (param i64 i64) (result i64)
+ (local.get 1)
+ )
+ )`).exports;
+ var f = exports.f;
+ var f2 = exports.f2;
+
+ testWithJit(() => {
+ assertEq(f(0n, 1n), 0n);
+ assertEq(f(-0n, 1n), -0n);
+ assertEq(f(123n, 1n), 123n);
+ assertEq(f(-123n, 1n), -123n);
+ assertEq(f(2n ** 63n, 1n), -(2n ** 63n));
+ assertEq(f(2n ** 64n + 123n, 1n), 123n);
+ assertEq(f("5", 1n), 5n);
+ assertEq(f(true, 1n), 1n);
+ assertEq(f(false, 1n), 0n);
+ assertEq(f({ toString() { return "5"; }, }, 1n), 5n);
+ assertEq(f({ valueOf() { return 5n; }, }, 1n), 5n);
+
+ assertEq(f2(1n, 0n), 0n);
+ assertEq(f2(1n, -0n), -0n);
+ assertEq(f2(1n, 123n), 123n);
+ assertEq(f2(1n, -123n), -123n);
+ assertEq(f2(1n, 2n ** 63n), -(2n ** 63n));
+ assertEq(f2(1n, 2n ** 64n + 123n), 123n);
+ assertEq(f2(1n, "5"), 5n);
+ assertEq(f2(1n, true), 1n);
+ assertEq(f2(1n, false), 0n);
+ assertEq(f2(1n, { toString() { return "5"; }, }), 5n);
+ assertEq(f2(1n, { valueOf() { return 5n; }, }), 5n);
+ });
+}
+
+function testNonBigIntArgs() {
+ var f = wasmEvalText(`(module
+ (func (export "f") (param i64) (result i64)
+ (local.get 0)
+ )
+ )`).exports.f;
+
+ assertErrorMessage(() => f(5), TypeError, "can't convert 5 to BigInt");
+ assertErrorMessage(() => f({ valueOf() { return 5; }, }),
+ TypeError,
+ "can't convert 5 to BigInt");
+ assertErrorMessage(() => f(5.3), TypeError, "can't convert 5.3 to BigInt");
+ assertErrorMessage(() => f(), TypeError, "can't convert undefined to BigInt");
+ assertErrorMessage(
+ () => f(undefined),
+ TypeError,
+ "can't convert undefined to BigInt"
+ );
+ assertErrorMessage(() => f(null), TypeError, "can't convert null to BigInt");
+ assertErrorMessage(
+ () => f(Symbol("foo")),
+ TypeError,
+ 'can\'t convert Symbol("foo") to BigInt'
+ );
+ assertErrorMessage(() => f({}), SyntaxError, "invalid BigInt syntax");
+ assertErrorMessage(() => f({ valueof() { return "foo"; }, }),
+ SyntaxError,
+ "invalid BigInt syntax");
+ assertErrorMessage(() => f("x"), SyntaxError, "invalid BigInt syntax");
+}
+
+function testIdPlus() {
+ var f = wasmEvalText(`(module
+ (func (export "f") (param i64) (result i64)
+ (i64.const 8)
+ (get_local 0)
+ (i64.add)
+ )
+ )`).exports.f;
+
+ testWithJit(() => {
+ assertEq(f(0n), 0n + 8n);
+ assertEq(f(147n), 147n + 8n);
+ });
+}
+
+// Test functions with many parameters to stress ABI cases. We want to test
+// spilled arguments both under and over the Ion call inlining limit.
+function testManyArgs() {
+ var f1 = wasmEvalText(`(module
+ (func (export "f")
+ (param i64 i64 i64 i64 i64 i64 i64 i64)
+ (result i64)
+ (get_local 0)
+ (get_local 1)
+ (get_local 2)
+ (get_local 3)
+ (get_local 4)
+ (get_local 5)
+ (get_local 6)
+ (get_local 7)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ )
+ )`).exports.f;
+
+ var f2 = wasmEvalText(`(module
+ (func (export "f")
+ (param i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64 i64)
+ (result i64)
+ (get_local 0)
+ (get_local 1)
+ (get_local 2)
+ (get_local 3)
+ (get_local 4)
+ (get_local 5)
+ (get_local 6)
+ (get_local 7)
+ (get_local 8)
+ (get_local 9)
+ (get_local 10)
+ (get_local 11)
+ (get_local 12)
+ (get_local 13)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ (i64.add)
+ )
+ )`).exports.f;
+
+ testWithJit(() => {
+ assertEq(f1(1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n), 8n);
+ assertEq(f2(1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n, 1n), 14n);
+ });
+}
+
+// Test import and re-export.
+function testImportExport() {
+ var f1 = wasmEvalText(
+ `(module
+ (import "i64" "func" (func (param i64)))
+ (export "f" (func 0))
+ )`,
+ {
+ i64: {
+ func(b) {
+ assertEq(b, 42n);
+ },
+ },
+ }
+ ).exports.f;
+
+ var f2 = wasmEvalText(
+ `(module
+ (import "i64" "func" (func (param i64) (result i64)))
+ (export "f" (func 0))
+ )`,
+ {
+ i64: {
+ func(n) {
+ return n + 1n;
+ },
+ },
+ }
+ ).exports.f;
+
+ var f3 = wasmEvalText(
+ `(module
+ (import "" "i64" (func $i64 (param i64) (result i64)))
+ (func (export "f") (param i64) (result i64)
+ (get_local 0)
+ (call $i64))
+ )`,
+ {
+ "": {
+ i64: n => {
+ return n + 1n;
+ },
+ },
+ }
+ ).exports.f;
+
+ var f4 = wasmEvalText(
+ `(module
+ (import "i64" "func" (func (result i64)))
+ (export "f" (func 0))
+ )`,
+ { i64: { func() {} } }
+ ).exports.f;
+
+ testWithJit(() => {
+ assertEq(f1(42n), undefined);
+ assertEq(f2(42n), 43n);
+ assertEq(f3(42n), 43n);
+ assertErrorMessage(() => f4(42), TypeError, "can't convert undefined to BigInt");
+ });
+}
+
+// Test that a mixture of I64 and other argument types works.
+function testMixedArgs() {
+ var f = wasmEvalText(`(module
+ (func (export "f")
+ (param i64 f32 f64 i32 i64)
+ (result i64)
+ (get_local 1)
+ (i64.trunc_s/f32)
+ (get_local 2)
+ (i64.trunc_s/f64)
+ (i64.add)
+ (get_local 3)
+ (i64.extend_i32_u)
+ (i64.add)
+ (get_local 0)
+ (i64.add)
+ (get_local 4)
+ (i64.add)
+ )
+ )`).exports.f;
+
+ testWithJit(() => {
+ assertEq(f(1n, 1.3, 1.7, 1, 1n), 5n);
+ });
+}
+
+function testGlobalImport() {
+ var exports = wasmEvalText(
+ `(module
+ (import "g" "a" (global $a i64))
+ (import "g" "b" (global $b i64))
+ (import "g" "c" (global $c i64))
+
+ (export "a" (global $a))
+ (export "b" (global $b))
+ (export "c" (global $c))
+ )`,
+ { g: { a: 1n, b: 2n ** 63n, c: -100n } }
+ ).exports;
+
+ testWithJit(() => {
+ assertEq(exports.a.value, 1n);
+ assertEq(exports.b.value, -(2n ** 63n));
+ assertEq(exports.c.value, -100n);
+ });
+}
+
+function testMutableGlobalImport() {
+ var exports = wasmEvalText(
+ `(module
+ (import "g" "a" (global $a (mut i64)))
+ (import "g" "b" (global $b (mut i64)))
+
+ (export "a" (global $a))
+ (export "b" (global $b))
+ )`,
+ {
+ g: {
+ a: new WebAssembly.Global({ value: "i64", mutable: true }, 1n),
+ b: new WebAssembly.Global({ value: "i64", mutable: true }, "2"),
+ },
+ }
+ ).exports;
+
+ testWithJit(() => {
+ assertEq(exports.a.value, 1n);
+ assertEq(exports.b.value, 2n);
+ });
+}
+
+function testMutableGlobalImportLiteral() {
+ assertErrorMessage(
+ () =>
+ wasmEvalText(
+ `(module
+ (import "g" "a" (global $a (mut i64)))
+ )`,
+ { g: { a: 1n } }
+ ),
+ WebAssembly.LinkError,
+ "imported global mutability mismatch"
+ );
+}
+
+function testGlobalBadImportLiteral() {
+ assertErrorMessage(
+ () =>
+ wasmEvalText(
+ `(module
+ (import "g" "a" (global $a i64))
+ (export "a" (global $a))
+ )`,
+ { g: { a: 1 } }
+ ),
+ WebAssembly.LinkError,
+ "import object field 'a' is not a BigInt"
+ );
+
+ assertErrorMessage(
+ () =>
+ wasmEvalText(
+ `(module
+ (import "g" "a" (global $a i64))
+ (export "a" (global $a))
+ )`,
+ { g: { a: "foo" } }
+ ),
+ WebAssembly.LinkError,
+ "import object field 'a' is not a BigInt"
+ );
+}
+
+// This exercises error code paths that were added due to BigInt/I64
+// conversion, though the test does not directly deal with I64 types.
+function testGlobalBadImportNumber() {
+ assertErrorMessage(
+ () =>
+ wasmEvalText(
+ `(module
+ (import "g" "a" (global $a i32))
+ (export "a" (global $a))
+ )`,
+ { g: { a: 1n } }
+ ),
+ WebAssembly.LinkError,
+ "import object field 'a' is not a Number"
+ );
+
+ assertErrorMessage(
+ () =>
+ wasmEvalText(
+ `(module
+ (import "g" "a" (global $a i32))
+ (export "a" (global $a))
+ )`,
+ { g: { a: "foo" } }
+ ),
+ WebAssembly.LinkError,
+ "import object field 'a' is not a Number"
+ );
+}
+
+function testI64Global() {
+ var global = new WebAssembly.Global({ value: "i64", mutable: true });
+
+ assertEq(global.value, 0n); // initial value
+
+ global.value = 123n;
+ assertEq(global.value, 123n);
+
+ global.value = 2n ** 63n;
+ assertEq(global.value, -(2n ** 63n));
+
+ global.value = "123";
+ assertEq(global.value, 123n);
+}
+
+function testI64GlobalValueOf() {
+ var argument = { value: "i64" };
+
+ // as literal
+ var global = new WebAssembly.Global(argument, {
+ valueOf() {
+ return 123n;
+ },
+ });
+ assertEq(global.value, 123n);
+
+ // as string
+ var global2 = new WebAssembly.Global(argument, {
+ valueOf() {
+ return "123";
+ },
+ });
+ assertEq(global.value, 123n);
+}
+
+function testGlobalI64ValueWrongType() {
+ var argument = { value: "i64" };
+ assertErrorMessage(
+ () => new WebAssembly.Global(argument, 666),
+ TypeError,
+ "can't convert 666 to BigInt"
+ );
+ assertErrorMessage(
+ () => new WebAssembly.Global(argument, "foo"),
+ SyntaxError,
+ "invalid BigInt syntax"
+ );
+ assertErrorMessage(
+ () =>
+ new WebAssembly.Global(argument, {
+ valueOf() {
+ return 5;
+ },
+ }),
+ TypeError,
+ "can't convert 5 to BigInt"
+ );
+}
+
+function testGlobalI64SetWrongType() {
+ var global = new WebAssembly.Global({ value: "i64", mutable: true });
+ assertErrorMessage(() => (global.value = 1), TypeError, "can't convert 1 to BigInt");
+ assertErrorMessage(
+ () => (global.value = "foo"),
+ SyntaxError,
+ "invalid BigInt syntax"
+ );
+ assertErrorMessage(
+ () =>
+ (global.value = {
+ valueOf() {
+ return 5;
+ },
+ }),
+ TypeError,
+ "can't convert 5 to BigInt"
+ );
+}
+
+testRet();
+testId();
+testIdPlus();
+testNonBigIntArgs();
+testManyArgs();
+testImportExport();
+testMixedArgs();
+testGlobalImport();
+testMutableGlobalImport();
+testMutableGlobalImportLiteral();
+testGlobalBadImportLiteral();
+testGlobalBadImportNumber();
+testI64Global();
+testI64GlobalValueOf();
+testGlobalI64ValueWrongType();
+testGlobalI64SetWrongType();
diff --git a/js/src/jit-test/tests/wasm/bigint/bug1633740.js b/js/src/jit-test/tests/wasm/bigint/bug1633740.js
new file mode 100644
index 0000000000..773e8932d9
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/bigint/bug1633740.js
@@ -0,0 +1,25 @@
+// Test for bug 1633740, an intermittent GC-related crash caused by
+// the bigint/i64 conversion in inlined Ion to Wasm calls.
+
+// Used to help ensure this will trigger the Ion inlined call path.
+var threshold = 2 * getJitCompilerOptions()["ion.warmup.trigger"] + 10;
+function testWithJit(f) {
+ for (var i = 0; i < threshold; i++) {
+ f();
+ }
+}
+
+function test() {
+ var exports = wasmEvalText(`(module
+ (func (export "f") (param i64) (result i64)
+ (local.get 0)
+ ))`).exports;
+ var f = exports.f;
+
+ testWithJit(() => {
+ assertEq(f("5"), 5n);
+ });
+}
+
+gczeal(7, 1); // Collect nursery on every allocation.
+test();
diff --git a/js/src/jit-test/tests/wasm/bigint/directives.txt b/js/src/jit-test/tests/wasm/bigint/directives.txt
new file mode 100644
index 0000000000..6fb4fc4a49
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/bigint/directives.txt
@@ -0,0 +1 @@
+|jit-test| test-also=--wasm-compiler=optimizing; test-also=--wasm-compiler=baseline; test-also=--test-wasm-await-tier2; include:wasm.js
diff --git a/js/src/jit-test/tests/wasm/bigint/stubs.js b/js/src/jit-test/tests/wasm/bigint/stubs.js
new file mode 100644
index 0000000000..89f7d78413
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/bigint/stubs.js
@@ -0,0 +1,142 @@
+// This is similar to the corresponding JIT exit stub test in the
+// wasm/import-export.js file, but it also tests BigInt cases.
+(function testImportJitExit() {
+ let options = getJitCompilerOptions();
+ if (!options["baseline.enable"]) return;
+
+ let baselineTrigger = options["baseline.warmup.trigger"];
+
+ let valueToConvert = 0;
+ function ffi(n) {
+ if (n == 1337n) {
+ return BigInt(valueToConvert);
+ }
+ return 42n;
+ }
+
+ // This case is intended to test that the I64 to BigInt argument
+ // filling for the JIT exit stub does not interfere with the other
+ // argument registers. It's important that the BigInt argument comes
+ // first (to test that it doesn't clobber the subsequent ones).
+ function ffi2(n, x1, x2, x3, x4, x5, x6) {
+ return (
+ 42n +
+ BigInt(x1) +
+ BigInt(x2) +
+ BigInt(x3) +
+ BigInt(x4) +
+ BigInt(x5) +
+ BigInt(x6)
+ );
+ }
+
+ // This case is for testing issues with potential GC.
+ function ffi3(n1, n2, n3, n4) {
+ return n3;
+ }
+
+ // Baseline compile ffis.
+ for (let i = baselineTrigger + 1; i-- > 0; ) {
+ ffi(BigInt(i));
+ ffi2(BigInt(i), i, i, i, i, i, i);
+ ffi3(BigInt(i), BigInt(i), BigInt(i), BigInt(i));
+ }
+
+ let imports = {
+ a: {
+ ffi,
+ ffi2,
+ ffi3,
+ },
+ };
+
+ ex = wasmEvalText(
+ `(module
+ (import "a" "ffi" (func $ffi (param i64) (result i64)))
+ (import "a" "ffi2" (func $ffi2
+ (param i64 i32 i32 i32 i32 i32 i32)
+ (result i64)))
+ (import "a" "ffi3" (func $ffi3 (param i64 i64 i64 i64) (result i64)))
+
+ (func (export "callffi") (param i64) (result i64)
+ local.get 0
+ call $ffi
+ )
+
+ (func (export "callffi2") (param i32 i64) (result i64)
+ local.get 1
+ local.get 0
+ local.get 0
+ local.get 0
+ local.get 0
+ local.get 0
+ local.get 0
+ call $ffi2
+ )
+
+ (func (export "callffi3") (param i64 i64 i64 i64) (result i64)
+ local.get 0
+ local.get 1
+ local.get 2
+ local.get 3
+ call $ffi3
+ )
+ )`,
+ imports
+ ).exports;
+
+ // Enable the jit exit for each JS callee.
+ assertEq(ex.callffi(0n), 42n);
+ assertEq(ex.callffi2(2, 0n), 54n);
+ assertEq(ex.callffi3(0n, 1n, 2n, 3n), 2n);
+
+ // Test the jit exit under normal conditions.
+ assertEq(ex.callffi(0n), 42n);
+ assertEq(ex.callffi(1337n), 0n);
+ assertEq(ex.callffi2(2, 0n), 54n);
+ assertEq(ex.callffi3(0n, 1n, 2n, 3n), 2n);
+
+ // Test the jit exit with GC stress in order to ensure that
+ // any trigger of GC in the stub do not cause errors.
+ if (this.gczeal) {
+ this.gczeal(2, 1); // Collect on every allocation
+ }
+ for (let i = 0; i < 1000; i++) {
+ assertEq(ex.callffi3(0n, 1n, 2n, 3n), 2n);
+ }
+})();
+
+// Test JIT entry stub
+(function testJitEntry() {
+ let options = getJitCompilerOptions();
+ if (!options["baseline.enable"]) return;
+
+ let baselineTrigger = options["baseline.warmup.trigger"];
+
+ i = wasmEvalText(
+ `(module
+ (func (export "foo") (param i64) (result i64)
+ local.get 0
+ i64.const 1
+ i64.add
+ return
+ )
+ )`,
+ {}
+ ).exports;
+
+ function caller(n) {
+ return i.foo(42n);
+ }
+
+ // Baseline compile ffis.
+ for (let i = baselineTrigger + 1; i-- > 0; ) {
+ caller(i);
+ }
+
+ // Enable the jit entry.
+ assertEq(caller(0), 43n);
+
+ // Test the jit exit under normal conditions.
+ assertEq(caller(0), 43n);
+})();