trailers-gc-stress.js (7091B)
1 // |jit-test| skip-if: getBuildConfiguration("simulator") 2 3 // This test is intended to test what was committed in 4 // 5 // Bug 1817385 - wasm-gc: reduce cost of allocation and GC paths 6 // and 7 // Bug 1820120 - Manage Wasm{Array,Struct}Object OOL-storage-blocks 8 // using a thread-private cache 9 // 10 // and in particular the latter. The patches in these bugs reduce the cost of 11 // wasm-gc struct/array allocation and collection, in part by better 12 // integrating those objects with our generational GC facility. 13 // 14 // Existing tests do not cover all of those paths. In particular they do not 15 // exercise both set-subtraction algorithms in Nursery::freeTrailerBlocks. 16 // This test does, though. 17 // 18 // The test first creates an "primary" array of 1500 elements. Each element 19 // is a reference to a secondary array of between 1 and 50 int32s. These 20 // secondary arrays have size chosen randomly, and the elements are also 21 // random. 22 // 23 // Then, elements of the primary array are replaced. An index in the range 0 24 // .. N - 1 is randomly chosen, and the element there is replaced by a 25 // randomly-created secondary array. This is repeated 500,000 times with 26 // N = 800. 27 // 28 // Finally, all of the above is repeated, but with N = 1200. 29 // 30 // As a result just over a million arrays and their trailer blocks, of various 31 // sizes, are allocated and deallocated. With N = 800, in 32 // js::Nursery::freeTrailerBlocks, we end up with trailersRemovedUsed_ of 33 // around 800, so one of the set-subtraction algorithms is exercised. 34 // With N = 1200, the other is exercised. It's not entirely clear why changing 35 // N causes trailersRemovedUsed_ to have more or less the same value during 36 // nursery collection, but the correlation does seem fairly robust. 37 // 38 // The test is skipped on the simulator because it takes too long to run, and 39 // triggers timeouts. 40 41 let t = 42 `(module 43 44 ;; A simple pseudo-random number generator. 45 ;; Produces numbers in the range 0 .. 2^16-1. 46 (global $rngState 47 (mut i32) (i32.const 1) 48 ) 49 (func $rand (export "rand") (result i32) 50 (local $t i32) 51 ;; update $rngState 52 (local.set $t (global.get $rngState)) 53 (local.set $t (i32.mul (local.get $t) (i32.const 1103515245))) 54 (local.set $t (i32.add (local.get $t) (i32.const 12345))) 55 (global.set $rngState (local.get $t)) 56 ;; pull 16 random bits out of it 57 (local.set $t (i32.shr_u (local.get $t) (i32.const 15))) 58 (local.set $t (i32.and (local.get $t) (i32.const 0xFFFF))) 59 (local.get $t) 60 ) 61 62 ;; Array types 63 (type $tArrayI32 (array (mut i32))) ;; "secondary array" above 64 (type $tArrayArrayI32 (array (mut (ref null $tArrayI32)))) ;; "primary array" 65 66 ;; Create an array ("secondary array") containing random numbers, with a 67 ;; size between 1 and 50, also randomly chosen. 68 (func $createSecondaryArray (export "createSecondaryArray") 69 (result (ref $tArrayI32)) 70 (local $i i32) 71 (local $nElems i32) 72 (local $arr (ref $tArrayI32)) 73 (local.set $nElems (call $rand)) 74 (local.set $nElems (i32.rem_u (local.get $nElems) (i32.const 50))) 75 (local.set $nElems (i32.add (local.get $nElems) (i32.const 1))) 76 (local.set $arr (array.new $tArrayI32 (i32.const 0) (local.get $nElems))) 77 (loop $cont 78 (array.set $tArrayI32 (local.get $arr) (local.get $i) (call $rand)) 79 (local.set $i (i32.add (local.get $i) (i32.const 1))) 80 (br_if $cont (i32.lt_u (local.get $i) (local.get $nElems))) 81 ) 82 (local.get $arr) 83 ) 84 85 ;; Create an array (the "primary array") of 1500 elements of 86 ;; type ref-of-tArrayI32. 87 (func $createPrimaryArray (export "createPrimaryArray") 88 (result (ref $tArrayArrayI32)) 89 (local $i i32) 90 (local $arrarr (ref $tArrayArrayI32)) 91 (local.set $arrarr (array.new $tArrayArrayI32 (ref.null $tArrayI32) 92 (i32.const 1500))) 93 (loop $cont 94 (array.set $tArrayArrayI32 (local.get $arrarr) 95 (local.get $i) (call $createSecondaryArray)) 96 (local.set $i (i32.add (local.get $i) (i32.const 1))) 97 (br_if $cont (i32.lt_u (local.get $i) (i32.const 1500))) 98 ) 99 (local.get $arrarr) 100 ) 101 102 ;; Use $createPrimaryArray to create an initial array. Then randomly replace 103 ;; elements for a while. 104 (func $churn (export "churn") (param $thresh i32) (result i32) 105 (local $i i32) 106 (local $j i32) 107 (local $finalSum i32) 108 (local $arrarr (ref $tArrayArrayI32)) 109 (local $arr (ref null $tArrayI32)) 110 (local $arrLen i32) 111 (local.set $arrarr (call $createPrimaryArray)) 112 ;; This loop iterates 500,000 times. Each iteration, it chooses 113 ;; a randomly element in $arrarr and replaces it with a new 114 ;; random array of 32-bit ints. 115 (loop $cont 116 ;; make $j be a random number in 0 .. $thresh-1. 117 ;; Then replace that index in $arrarr with a new random arrayI32. 118 (local.set $j (i32.rem_u (call $rand) (local.get $thresh))) 119 (array.set $tArrayArrayI32 (local.get $arrarr) 120 (local.get $j) (call $createSecondaryArray)) 121 (local.set $i (i32.add (local.get $i) (i32.const 1))) 122 (br_if $cont (i32.lt_u (local.get $i) (i32.const 500000))) 123 ) 124 125 ;; Finally, compute a checksum by summing all the numbers 126 ;; in all secondary arrays. This simply assumes that all of the refs to 127 ;; secondary arrays are non-null, which isn't per-se guaranteed by the 128 ;; previous loop, but it works in this case because the RNG 129 ;; produces each index value to overwrite at least once. 130 (local.set $finalSum (i32.const 0)) 131 (local.set $i (i32.const 0)) ;; loop var for the outer loop 132 (loop $outer 133 ;; body of outer loop 134 ;; $arr = $arrarr[i] 135 (local.set $arr (array.get $tArrayArrayI32 (local.get $arrarr) 136 (local.get $i))) 137 ;; iterate over $arr 138 (local.set $arrLen (array.len (local.get $arr))) 139 (local.set $j (i32.const 0)) ;; loop var for the inner loop 140 (loop $inner 141 ;; body of inner loop 142 (local.set $finalSum 143 (i32.rotl (local.get $finalSum) (i32.const 1))) 144 (local.set $finalSum 145 (i32.xor (local.get $finalSum) 146 (array.get $tArrayI32 (local.get $arr) 147 (local.get $j)))) 148 ;; loop control for the inner loop 149 (local.set $j (i32.add (local.get $j) (i32.const 1))) 150 (br_if $inner (i32.lt_u (local.get $j) (local.get $arrLen))) 151 ) 152 ;; loop control for the outer loop 153 (local.set $i (i32.add (local.get $i) (i32.const 1))) 154 (br_if $outer (i32.lt_u (local.get $i) (i32.const 1500))) 155 ) 156 157 ;; finally, roll in the final value of the RNG state 158 (i32.xor (local.get $finalSum) (global.get $rngState)) 159 ) 160 )`; 161 162 let i = wasmEvalText(t); 163 let fns = i.exports; 164 165 assertEq(fns.churn(800), -575895114); 166 assertEq(fns.churn(1200), -1164697516);