summaryrefslogtreecommitdiffstats
path: root/js/src/jit-test/tests/wasm/tail-calls/gc.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit-test/tests/wasm/tail-calls/gc.js')
-rw-r--r--js/src/jit-test/tests/wasm/tail-calls/gc.js220
1 files changed, 220 insertions, 0 deletions
diff --git a/js/src/jit-test/tests/wasm/tail-calls/gc.js b/js/src/jit-test/tests/wasm/tail-calls/gc.js
new file mode 100644
index 0000000000..10e5971e6d
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/tail-calls/gc.js
@@ -0,0 +1,220 @@
+// |jit-test| --wasm-function-references; --wasm-gc; skip-if: !wasmGcEnabled() || getBuildConfiguration("simulator")
+
+// Tests GC references passed as arguments during return calls.
+// Similar to js/src/jit-test/tests/wasm/gc/trailers-gc-stress.js
+
+let base = wasmEvalText(`(module
+ ;; A simple pseudo-random number generator.
+ ;; Produces numbers in the range 0 .. 2^16-1.
+ (global $rngState (export "rngState")
+ (mut i32) (i32.const 1)
+ )
+ (func $rand (export "rand") (result i32)
+ (local $t i32)
+ ;; update $rngState
+ (local.set $t (global.get $rngState))
+ (local.set $t (i32.mul (local.get $t) (i32.const 1103515245)))
+ (local.set $t (i32.add (local.get $t) (i32.const 12345)))
+ (global.set $rngState (local.get $t))
+ ;; pull 16 random bits out of it
+ (local.set $t (i32.shr_u (local.get $t) (i32.const 15)))
+ (local.set $t (i32.and (local.get $t) (i32.const 0xFFFF)))
+ (local.get $t)
+ )
+
+ ;; Array types
+ (type $tArrayI32 (array (mut i32))) ;; "secondary array" above
+ (type $tArrayArrayI32 (array (mut (ref null $tArrayI32)))) ;; "primary array"
+
+ (func $createSecondaryArrayLoop (export "createSecondaryArrayLoop")
+ (param $i i32) (param $arr (ref $tArrayI32))
+ (result (ref $tArrayI32))
+ (block $cont
+ (br_if $cont (i32.ge_u (local.get $i) (array.len (local.get $arr))))
+ (array.set $tArrayI32 (local.get $arr) (local.get $i) (call $rand))
+ (return_call $createSecondaryArrayLoop
+ (i32.add (local.get $i) (i32.const 1))
+ (local.get $arr))
+ )
+ (local.get $arr)
+ )
+ ;; Create an array ("secondary array") containing random numbers, with a
+ ;; size between 1 and 50, also randomly chosen.
+ (func $createSecondaryArray (export "createSecondaryArray")
+ (result (ref $tArrayI32))
+ (return_call $createSecondaryArrayLoop
+ (i32.const 0)
+ (array.new $tArrayI32
+ (i32.const 0)
+ (i32.add (i32.rem_u (call $rand) (i32.const 50)) (i32.const 1)))
+ )
+ )
+
+ (func $createPrimaryArrayLoop (export "createPrimaryArrayLoop")
+ (param $i i32) (param $arrarr (ref $tArrayArrayI32))
+ (result (ref $tArrayArrayI32))
+ (block $cont
+ (br_if $cont (i32.ge_u (local.get $i) (array.len (local.get $arrarr))))
+ (array.set $tArrayArrayI32 (local.get $arrarr)
+ (local.get $i) (call $createSecondaryArray))
+ (return_call $createPrimaryArrayLoop
+ (i32.add (local.get $i) (i32.const 1))
+ (local.get $arrarr))
+ )
+ (local.get $arrarr)
+ )
+)`);
+let t =
+`(module
+ ;; Array types (the same as in the base)
+ (type $tArrayI32 (array (mut i32))) ;; "secondary array" above
+ (type $tArrayArrayI32 (array (mut (ref null $tArrayI32)))) ;; "primary array"
+
+ (import "" "rngState" (global $rngState (mut i32)))
+ (import "" "rand" (func $rand (result i32)))
+ (import "" "createSecondaryArrayLoop"
+ (func $createSecondaryArrayLoop
+ (param $i i32) (param $arr (ref $tArrayI32))
+ (result (ref $tArrayI32))))
+ (import "" "createPrimaryArrayLoop"
+ (func $createPrimaryArrayLoop
+ (param $i i32) (param $arrarr (ref $tArrayArrayI32))
+ (result (ref $tArrayArrayI32))))
+
+ ;; Create an array ("secondary array") containing random numbers, with a
+ ;; size between 1 and 50, also randomly chosen.
+ ;; (Copy of the base one to create trampoline)
+ (func $createSecondaryArray (export "createSecondaryArray")
+ (result (ref $tArrayI32))
+ (return_call $createSecondaryArrayLoop
+ (i32.const 0)
+ (array.new $tArrayI32
+ (i32.const 0)
+ (i32.add (i32.rem_u (call $rand) (i32.const 50)) (i32.const 1)))
+ )
+ )
+
+ ;; Create an array (the "primary array") of 1500 elements of
+ ;; type ref-of-tArrayI32.
+ (func $createPrimaryArray (export "createPrimaryArray")
+ (result (ref $tArrayArrayI32))
+ (return_call $createPrimaryArrayLoop
+ (i32.const 0)
+ (array.new $tArrayArrayI32 (ref.null $tArrayI32) (i32.const 1500)))
+ )
+
+ ;; Use $createPrimaryArray to create an initial array. Then randomly replace
+ ;; elements for a while.
+ (func $churn (export "churn") (param $thresh i32) (result i32)
+ (local $i i32)
+ (local $j i32)
+ (local $finalSum i32)
+ (local $arrarr (ref $tArrayArrayI32))
+ (local $arr (ref null $tArrayI32))
+ (local $arrLen i32)
+ (local.set $arrarr (call $createPrimaryArray))
+ ;; This loop iterates 500,000 times. Each iteration, it chooses
+ ;; a randomly element in $arrarr and replaces it with a new
+ ;; random array of 32-bit ints.
+ (loop $cont
+ ;; make $j be a random number in 0 .. $thresh-1.
+ ;; Then replace that index in $arrarr with a new random arrayI32.
+ (local.set $j (i32.rem_u (call $rand) (local.get $thresh)))
+ (array.set $tArrayArrayI32 (local.get $arrarr)
+ (local.get $j) (call $createSecondaryArray))
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
+ (br_if $cont (i32.lt_u (local.get $i) (i32.const 500000)))
+ )
+
+ ;; Finally, compute a checksum by summing all the numbers
+ ;; in all secondary arrays. This simply assumes that all of the refs to
+ ;; secondary arrays are non-null, which isn't per-se guaranteed by the
+ ;; previous loop, but it works in this case because the RNG
+ ;; produces each index value to overwrite at least once.
+ (local.set $finalSum (i32.const 0))
+ (local.set $i (i32.const 0)) ;; loop var for the outer loop
+ (loop $outer
+ ;; body of outer loop
+ ;; $arr = $arrarr[i]
+ (local.set $arr (array.get $tArrayArrayI32 (local.get $arrarr)
+ (local.get $i)))
+ ;; iterate over $arr
+ (local.set $arrLen (array.len (local.get $arr)))
+ (local.set $j (i32.const 0)) ;; loop var for the inner loop
+ (loop $inner
+ ;; body of inner loop
+ (local.set $finalSum
+ (i32.rotl (local.get $finalSum) (i32.const 1)))
+ (local.set $finalSum
+ (i32.xor (local.get $finalSum)
+ (array.get $tArrayI32 (local.get $arr)
+ (local.get $j))))
+ ;; loop control for the inner loop
+ (local.set $j (i32.add (local.get $j) (i32.const 1)))
+ (br_if $inner (i32.lt_u (local.get $j) (local.get $arrLen)))
+ )
+ ;; loop control for the outer loop
+ (local.set $i (i32.add (local.get $i) (i32.const 1)))
+ (br_if $outer (i32.lt_u (local.get $i) (i32.const 1500)))
+ )
+
+ ;; finally, roll in the final value of the RNG state
+ (i32.xor (local.get $finalSum) (global.get $rngState))
+ )
+)`;
+
+let i = wasmEvalText(t, {"": base.exports,});
+let fns = i.exports;
+
+assertEq(fns.churn(800), -575895114);
+assertEq(fns.churn(1200), -1164697516);
+
+wasmValidateText(`(module
+ (rec
+ (type $s1 (sub (struct (field i32))))
+ (type $s2 (sub $s1 (struct (field i32 f32))))
+ )
+ (func (result (ref $s2))
+ struct.new_default $s2
+ )
+ (func (export "f") (result (ref $s1))
+ return_call 0
+ )
+)`);
+
+wasmFailValidateText(`(module
+ (rec
+ (type $s1 (sub (struct (field i32))))
+ (type $s2 (sub $s1 (struct (field i32 f32))))
+ )
+ (func (result (ref $s1))
+ struct.new_default $s1
+ )
+ (func (export "f") (result (ref $s2))
+ return_call 0
+ )
+)`, /type mismatch/);
+
+wasmValidateText(`(module
+ (rec
+ (type $s1 (sub (struct (field i32))))
+ (type $s2 (sub $s1 (struct (field i32 f32))))
+ )
+ (type $t (func (result (ref $s2))))
+ (func (export "f") (param (ref $t)) (result (ref $s1))
+ local.get 0
+ return_call_ref $t
+ )
+)`);
+
+wasmFailValidateText(`(module
+ (rec
+ (type $s1 (sub (struct (field i32))))
+ (type $s2 (sub $s1 (struct (field i32 f32))))
+ )
+ (type $t (func (result (ref $s1))))
+ (func (export "f") (param (ref $t)) (result (ref $s2))
+ local.get 0
+ return_call_ref $t
+ )
+)`, /type mismatch/);