litmus13.js (2375B)
1 // Once we exhaust the register arguments this will alternately grow and then 2 // shrink the stack frame across tail call boundaries because the increment of 3 // stack allocation is 16 bytes and our variability exceeds that. 4 // 5 // (This is not redundant with eg litmus2, because in that case all the 6 // functions have the same ballast. Here we have different ballast, so we get 7 // growing and shrinking.) 8 // 9 // See litmus11 for the direct-call case. 10 // See litmus16 for the cross-module call_indirect case. 11 12 function ntimes(n, v) { 13 if (typeof v == "function") 14 return iota(n).map(v).join(' '); 15 return iota(n).map(_ => v).join(' '); 16 } 17 18 function get_local(n) { 19 return `(local.get ${n})` 20 } 21 22 function compute(ballast) { 23 return iota(ballast).reduce((p,g,n) => `(i32.or ${p} (local.get ${n}))`, 24 `(i32.const ${1 << ballast})`) 25 } 26 27 function build(n, ballast) { 28 switch (n) { 29 case 0: 30 return ` 31 (func $f0 (export "f") (result i32) 32 (return_call_indirect (type $ty1) (i32.const ${1 << n}) (i32.const 1))) 33 `; 34 case ballast: 35 return ` 36 (func $f${ballast} (param ${ntimes(ballast, 'i32')}) (result i32) 37 (if (result i32) (i32.eqz (global.get $glob)) 38 (then (return ${compute(ballast)})) 39 (else (block (result i32) 40 (global.set $glob (i32.sub (global.get $glob) (i32.const 1))) 41 (return_call_indirect (type $ty0) (i32.const 0)))))) 42 `; 43 default: 44 return ` 45 (func $f${n} (param ${ntimes(n, 'i32')}) (result i32) 46 (return_call_indirect (type $ty${n+1}) (i32.const ${1 << n}) ${ntimes(n, get_local)} (i32.const ${n+1}))) 47 ` 48 } 49 } 50 51 function types(n) { 52 var ps = n == 0 ? '' : `(param ${ntimes(n, 'i32')})`; 53 return ` 54 (type $ty${n} (func ${ps} (result i32)))`; 55 } 56 57 function funcnames(n) { 58 return ntimes(n, n => `$f${n}`) 59 } 60 61 for ( let ballast=1; ballast < TailCallBallast; ballast++ ) { 62 63 let vals = iota(ballast+1).map(v => 1 << v); 64 let sumv = vals.reduce((p,c) => p|c); 65 let text = ` 66 (module 67 ${ntimes(ballast+1, n => types(n))} 68 (table $t ${ballast+1} ${ballast+1} funcref) 69 (elem (table $t) (i32.const 0) func ${funcnames(ballast+1)}) 70 (global $glob (mut i32) (i32.const ${TailCallIterations})) 71 ${ntimes(ballast, n => build(n, ballast))} 72 ${build(ballast, ballast)}) 73 `; 74 75 let ins = wasmEvalText(text); 76 assertEq(ins.exports.f(), sumv); 77 }