summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/tail-calls/return_call.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/tail-calls/return_call.js')
-rw-r--r--js/src/jit-test/tests/wasm/tail-calls/return_call.js305
1 files changed, 305 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/tail-calls/return_call.js b/js/src/jit-test/tests/wasm/tail-calls/return_call.js
new file mode 100644
index 0000000000..ddaa933aaf
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/tail-calls/return_call.js
@@ -0,0 +1,305 @@
+var ins = wasmEvalText(`(module
+ (func $fac-acc (export "fac-acc") (param i64 i64) (result i64)
+ (if (result i64) (i64.eqz (local.get 0))
+ (then (local.get 1))
+ (else
+ (return_call $fac-acc
+ (i64.sub (local.get 0) (i64.const 1))
+ (i64.mul (local.get 0) (local.get 1))
+ )
+ )
+ )
+ )
+ (func (export "main") (param i64) (result i64)
+ (call $fac-acc (local.get 0) (i64.const 1))
+ )
+)`);
+
+// Check return call via wasm function
+assertEq(ins.exports.main(5n), 120n);
+
+// Check return call directly via interpreter stub
+const fac = ins.exports["fac-acc"];
+assertEq(fac(4n, 1n), 24n);
+
+// Check return call directly via jit stub
+check_stub1: {
+ let options = getJitCompilerOptions();
+ if (!options["baseline.enable"]) break check_stub1;
+ const check = function() {
+ fac(4n, 1n);
+ };
+ for (let i = options["baseline.warmup.trigger"] + 1; i--;)
+ check();
+}
+
+// Return call of an import
+var ins0 = wasmEvalText(`(module
+ (func $fac-acc (export "fac-acc") (param i64 i64) (result i64)
+ (if (result i64) (i64.eqz (local.get 0))
+ (then (local.get 1))
+ (else
+ (return_call $fac-acc
+ (i64.sub (local.get 0) (i64.const 1))
+ (i64.mul (local.get 0) (local.get 1))
+ )
+ )
+ )
+ )
+)`);
+
+var ins = wasmEvalText(`(module
+ (import "" "fac-acc" (func $fac-acc (param i64 i64) (result i64)))
+ (func (export "fac") (param i64) (result i64)
+ local.get 0
+ i64.const 1
+ return_call $fac-acc
+ )
+ (func (export "main") (result i64)
+ i64.const 4
+ call 1
+ )
+)`, {"": {"fac-acc": ins0.exports["fac-acc"],}});
+
+assertEq(ins.exports.main(), 24n);
+assertEq(ins.exports.fac(3n, 1n), 6n);
+check_stub2: {
+ let options = getJitCompilerOptions();
+ if (!options["baseline.enable"]) break check_stub2;
+ const check = function() {
+ ins.exports.fac(3n, 1n)
+ };
+ for (let i = options["baseline.warmup.trigger"] + 1; i--;)
+ check();
+}
+
+// Check with parameters area growth
+var ins0 = wasmEvalText(`(module
+ (func $fac-acc (export "fac-acc") (param i64 i64 i64 i64 i64 i64 i64 i64) (result i64)
+ (if (result i64) (i64.eqz (local.get 0))
+ (then (local.get 1))
+ (else
+ (return_call $fac-acc
+ (i64.sub (local.get 0) (i64.const 1))
+ (i64.mul (local.get 0) (local.get 1))
+ i64.const 1 i64.const 2 i64.const 3 i64.const 4 i64.const 5 i64.const 6
+ )
+ )
+ )
+ )
+)`);
+
+var ins = wasmEvalText(`(module
+ (import "" "fac-acc" (func $fac-acc (param i64 i64 i64 i64 i64 i64 i64 i64) (result i64)))
+ (func (export "fac") (param i64) (result i64)
+ local.get 0
+ i64.const 1
+ i64.const 1 i64.const 2 i64.const 3 i64.const 4 i64.const 5 i64.const 6
+ return_call $fac-acc
+ )
+ (func (export "main") (result i64)
+ i64.const 5
+ call 1
+ )
+)`, {"": {"fac-acc": ins0.exports["fac-acc"],}});
+
+assertEq(ins.exports.main(), 120n);
+assertEq(ins.exports.fac(3n, 1n), 6n);
+check_stub3: {
+ let options = getJitCompilerOptions();
+ if (!options["baseline.enable"]) break check_stub3;
+ const check = function() {
+ ins.exports.fac(4n, 1n)
+ };
+ for (let i = options["baseline.warmup.trigger"] + 1; i--;)
+ check();
+}
+
+// Test multi-value returns.
+var ins = wasmEvalText(`(module
+ (memory (export "memory") 1 1)
+ (func $rec (export "rec") (param i32 i32 i32 i32 i32 i32 i32) (result i32 i32 f32 f32)
+ (local f32 i32)
+ (if (result i32 i32 f32 f32) (i32.ge_u (local.get 0) (local.get 1))
+ (then
+ (local.get 5)
+ (local.get 6)
+ (local.tee 7 (f32.div (f32.convert_i32_u (local.get 3)) (f32.convert_i32_u (local.get 2))))
+ (f32.sqrt
+ (f32.sub
+ (f32.div (f32.convert_i32_u (local.get 4)) (f32.convert_i32_u (local.get 2)))
+ (f32.mul (local.get 7) (local.get 7))
+ )
+ )
+ )
+ (else
+ (return_call $rec
+ (i32.add (local.get 0) (i32.const 1))
+ (local.get 1)
+ (i32.add (local.get 2) (i32.const 1))
+ (i32.add (local.get 3) (local.tee 8 (i32.load8_u (local.get 0))))
+ (i32.add (local.get 4) (i32.mul (local.get 8) (local.get 8)))
+ (if (result i32) (i32.gt_s (local.get 5) (local.get 8))
+ (then (local.get 8)) (else (local.get 5))
+ )
+ (if (result i32) (i32.lt_s (local.get 6) (local.get 8))
+ (then (local.get 8)) (else (local.get 6))
+ )
+ )
+ )
+ )
+ )
+ (func $main (export "main") (result i32 i32 f32 f32)
+ (call $rec
+ (i32.const 0)
+ (i32.const 6)
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 1000)
+ (i32.const -1000)
+ )
+ )
+ (data (i32.const 0) "\\02\\13\\22\\04\\08\\30")
+)`);
+
+const main = ins.exports["main"];
+assertEq(""+ main(), "2,48,19.16666603088379,16.836633682250977");
+assertEq("" + ins.exports.rec(1, 5, 0, 0, 0, 1000, -1000), "4,34,16.25,11.627016067504883");
+check_stub3: {
+ let options = getJitCompilerOptions();
+ if (!options["baseline.enable"]) break check_stub3;
+ const check = function() {
+ ins.exports.rec(1, 5, 0, 0, 0, 1000, -1000);
+ };
+ for (let i = options["baseline.warmup.trigger"] + 1; i--;)
+ check();
+}
+
+// Handling trap.
+var ins = wasmEvalText(`(module
+ (func $fac-acc (export "fac") (param i64 i64) (result i64)
+ (if (result i64) (i64.eqz (local.get 0))
+ (then (unreachable))
+ (else
+ (return_call $fac-acc
+ (i64.sub (local.get 0) (i64.const 1))
+ (i64.mul (local.get 0) (local.get 1))
+ )
+ )
+ )
+ )
+ (func (export "main") (param i64) (result i64)
+ (call $fac-acc (local.get 0) (i64.const 1))
+ )
+)`);
+
+assertErrorMessage(() => ins.exports.main(4n), WebAssembly.RuntimeError, /unreachable executed/);
+assertErrorMessage(() => ins.exports.fac(3n, 1n), WebAssembly.RuntimeError, /unreachable executed/);
+
+// Performance and stack growth: calculating sum of numbers 1..40000000
+var ins = wasmEvalText(`(module
+ (func $sum (param i32 i64) (result i64)
+ local.get 0
+ i32.eqz
+ if
+ local.get 1
+ return
+ else
+ local.get 0
+ i32.const 1
+ i32.sub
+ local.get 1
+ local.get 0
+ i64.extend_i32_s
+ i64.add
+ return_call $sum
+ end
+ unreachable
+ )
+
+ (func (export "main") (param i32) (result i64)
+ local.get 0
+ i64.const 0
+ call $sum
+ )
+)`);
+
+if (getBuildConfiguration("simulator")) {
+ assertEq(ins.exports.main(400000), 80000200000n);
+} else {
+ assertEq(ins.exports.main(40000000), 800000020000000n);
+}
+
+// GC/externref shall not cling to the trampoline frame.
+// The `return_call` caller will create a trampoline because the callee is
+// an import. The caller will create a GC object and will hold in its frame
+// and a WeakMap.
+// Test if the created object is in the WeakMap even after gc().
+var wm = new WeakMap();
+var ins = wasmEvalText(`(module
+ (import "" "test" (func $test))
+ (func $sum (param i32 i64) (result i64)
+ local.get 0
+ i32.eqz
+ if
+ call $test
+ local.get 1
+ return
+ else
+ local.get 0
+ i32.const 1
+ i32.sub
+ local.get 1
+ local.get 0
+ i64.extend_i32_s
+ i64.add
+ return_call $sum
+ end
+ unreachable
+ )
+ (export "sum" (func $sum))
+)`, {"": {
+ test() {
+ gc();
+ assertEq(nondeterministicGetWeakMapKeys(wm).length, 0);
+ }
+}});
+
+var ins2 = wasmEvalText(`(module
+ (import "" "add_ref" (func $add_ref (result externref)))
+ (import "" "use_ref" (func $use_ref (param externref)))
+ (import "" "sum" (func $sum (param i32 i64) (result i64)))
+ (global $g1 (mut i32) (i32.const 0))
+ (func (export "main_gc") (param i32) (result i64)
+ (local $ref externref)
+ call $add_ref
+ local.set $ref
+
+ local.get $ref
+ call $use_ref
+
+ block
+ global.get $g1
+ br_if 0
+ local.get 0
+ i64.const 0
+ return_call $sum
+ end
+
+ local.get $ref
+ call $use_ref
+ i64.const -1
+ )
+)`, {"": {
+ sum: ins.exports.sum,
+ add_ref() {
+ const obj = {}; wm.set(obj, 'foo'); return obj;
+ },
+ use_ref(obj) {
+ assertEq(nondeterministicGetWeakMapKeys(wm).length, 1);
+ },
+}});
+
+assertEq(ins2.exports.main_gc(400000), 80000200000n);
+assertEq(nondeterministicGetWeakMapKeys(wm).length, 0);