tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

litmus16.js (3361B)


      1 // This alternately grows and then shrinks the stack frame across tail call boundaries.  
      2 // Here we go cross-module as well.  There is enough ballast that the stack frame will
      3 // alternately have to grow and shrink across some of the calls.
      4 //
      5 // We generate one module+instance per function so that every call is cross-instance.
      6 // Each module has an "up" function (calling the next higher index) and a "down" function
      7 // (calling the next lower index).  The last "up" function decrements the global counter
      8 // and optionally returns a result $down0 just calls $up0.
      9 //
     10 // TODO: Test that the proper instance is being restored?  Or have we done that elsewhere?
     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,_,n) => `(i32.or ${p} (local.get ${n}))`,
     24                                `(i32.const ${1 << ballast})`)
     25 }
     26 
     27 function code(n, ballast) {
     28    switch (n) {
     29    case 0:
     30        return `
     31 (func $up0 (export "f") (result i32)
     32  (return_call_indirect (type $ty1) (i32.const ${1 << n}) (i32.const 1)))
     33 (func $down0 (result i32)
     34  (return_call $up0))`;
     35    case ballast:
     36        return `
     37 (func $up${ballast} (param ${ntimes(ballast, 'i32')}) (result i32)
     38    (if (result i32) (i32.eqz (global.get $glob))
     39        (then (return ${compute(ballast)}))
     40        (else
     41            (block (result i32)
     42                (global.set $glob (i32.sub (global.get $glob) (i32.const 1)))
     43                (return_call_indirect (type $ty${ballast-1}) ${ntimes(ballast-1,get_local)} (i32.const ${ballast+1}))))))`;
     44    default:
     45        return `
     46 (func $up${n} (param ${ntimes(n, 'i32')}) (result i32)
     47  (return_call_indirect (type $ty${n+1}) (i32.const ${1 << n}) ${ntimes(n, get_local)} (i32.const ${n+1})))
     48 (func $down${n} (param ${ntimes(n, 'i32')}) (result i32)
     49  (return_call_indirect (type $ty${n-1}) ${ntimes(n-1, get_local)} (i32.const ${2*ballast-n+1})))`;
     50    }
     51 }
     52 
     53 function types(n, ballast) {
     54    var tys = '';
     55    if (n > 0) 
     56        tys += `
     57 (type $ty${n-1} (func (param ${ntimes(n-1, 'i32')}) (result i32)))`;
     58    if (n < ballast)
     59        tys += `
     60 (type $ty${n+1} (func (param ${ntimes(n+1, 'i32')}) (result i32)))`
     61    return tys;
     62 }
     63 
     64 function inits(n, ballast) {
     65    var inits = `
     66 (elem (i32.const ${n}) $up${n})`
     67    if (n < ballast) 
     68        inits += `
     69 (elem (i32.const ${2*ballast-n}) $down${n})`;
     70    return inits
     71 }
     72 
     73 for (let ballast = 1; ballast < TailCallBallast; ballast++) {
     74    let counter = new WebAssembly.Global({ mutable: true, value: "i32" }, TailCallIterations/10);
     75    let table = new WebAssembly.Table({ initial: ballast * 2 + 1, maximum: ballast * 2 + 1, element: "anyfunc" });
     76    let tys = ntimes(ballast + 1, n => types(n));
     77    let vals = iota(ballast + 1).map(v => 1 << v);
     78    let sumv = vals.reduce((p, c) => p | c);
     79    let ins = [];
     80    let imp = { "": { table, counter } };
     81    for (let i = 0; i <= ballast; i++) {
     82        let txt = `
     83 (module
     84    ${types(i, ballast)}
     85    (import "" "table" (table $t ${ballast * 2 - 1} funcref))
     86    (import "" "counter" (global $glob (mut i32)))
     87    ${inits(i, ballast)}
     88    ${code(i, ballast)})`;
     89        ins[i] = wasmEvalText(txt, imp);
     90    }
     91    assertEq(ins[0].exports.f(), sumv)
     92 }