tor-browser

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

nan-semantics.js (6083B)


      1 var f64 = new Float64Array(2);
      2 var f32 = new Float32Array(f64.buffer);
      3 var u8 = new Uint8Array(f64.buffer);
      4 
      5 function assertSameBitPattern(from, to, offset) {
      6    for (let i = from; i < to; i++)
      7        assertEq(u8[i], u8[i + offset], 'non equality in assertSameBitPattern');
      8 }
      9 
     10 // Check that custom NaN can't escape to normal JS, in non-testing mode.
     11 f32[0] = NaN;
     12 f32[0] = f32[0]; // Force canonicalization.
     13 
     14 f32[1] = wasmEvalText(`
     15 (module
     16  (func (result f32)
     17    (f32.const nan:0x123456))
     18  (export "" (func 0)))
     19 `).exports[""]();
     20 assertSameBitPattern(0, 4, 4);
     21 
     22 var checkBitPatterns = {
     23    "": {
     24        float32(x) {
     25            f32[1] = x;
     26            assertSameBitPattern(0, 4, 4);
     27        },
     28        float64(x) {
     29            f64[1] = x;
     30            assertSameBitPattern(0, 8, 8);
     31        }
     32    }
     33 }
     34 
     35 wasmEvalText(`
     36 (module
     37  (import "" "float32" (func (param f32)))
     38  (func
     39    (call 0 (f32.const nan:0x123456)))
     40  (export "" (func 0)))
     41 `, checkBitPatterns).exports[""]();
     42 
     43 f64[0] = NaN;
     44 f64[0] = f64[0]; // Force canonicalization.
     45 f64[1] = wasmEvalText(`
     46 (module
     47  (func (result f64)
     48    (f64.const nan:0x123456))
     49  (export "" (func 0)))
     50 `).exports[""]();
     51 assertSameBitPattern(0, 8, 8);
     52 
     53 wasmEvalText(`
     54 (module
     55  (import "" "float64" (func (param f64)))
     56  (func
     57    (call 0 (f64.const nan:0x123456)))
     58  (export "" (func 0)))
     59 `, checkBitPatterns).exports[""]();
     60 
     61 // SANITY CHECKS
     62 
     63 // There are two kinds of NaNs: signaling and quiet. Usually, the first bit of
     64 // the payload is used to indicate whether it is quiet (1 for quiet, 0 for
     65 // signaling). Most operations have to transform a signaling NaN into a quiet
     66 // NaN, which prevents some optimizations in WebAssembly.
     67 
     68 // A float32 has 32 bits, 23 bits of them being reserved for the mantissa
     69 // (= NaN payload).
     70 var f32_qnan_code = '(f32.const nan:0x600000)';
     71 var f32_snan_code = '(f32.const nan:0x200000)';
     72 
     73 var f32_snan = '0x7fa00000';
     74 var f32_qnan = '0x7fe00000';
     75 
     76 // A float64 has 64 bits, 1 for the sign, 11 for the exponent, the rest for the
     77 // mantissa (payload).
     78 var f64_nan_base_high = 0x7ff00000;
     79 
     80 var f64_snan_code = '(f64.const nan:0x4000000000000)';
     81 var f64_qnan_code = '(f64.const nan:0xc000000000000)';
     82 
     83 var f64_snan = '0x7ff4000000000000';
     84 var f64_qnan = '0x7ffc000000000000';
     85 
     86 wasmAssert(`(module
     87    (func $f32_snan (result f32) ${f32_snan_code})
     88    (func $f32_qnan (result f32) ${f32_qnan_code})
     89    (func $f64_snan (result f64) ${f64_snan_code})
     90    (func $f64_qnan (result f64) ${f64_qnan_code})
     91 )`, [
     92    { type: 'f32', func: '$f32_snan', expected: f32_snan },
     93    { type: 'f32', func: '$f32_qnan', expected: f32_qnan },
     94    { type: 'f64', func: '$f64_snan', expected: f64_snan },
     95    { type: 'f64', func: '$f64_qnan', expected: f64_qnan },
     96 ]);
     97 
     98 // Actual tests.
     99 
    100 // Wasm spec v1.1 section 4.3.3, sections "fadd" et seq and "NaN propagation":
    101 // If the input NaN is not canonical then the output may be any arithmetic NaN,
    102 // ie a quiet NaN with arbitrary payload.
    103 
    104 wasmAssert(`(module
    105    (global (mut f32) (f32.const 0))
    106    (global (mut f64) (f64.const 0))
    107 
    108    ;; An example where a signaling nan gets transformed into a quiet nan:
    109    ;; snan + 0.0 = qnan
    110    (func $add (result f32) (f32.add ${f32_snan_code} (f32.const 0)))
    111 
    112    ;; Shouldn't affect NaNess.
    113    (func $global.set.get_f32 (result f32)
    114        ${f32_snan_code}
    115        global.set 0
    116        global.get 0
    117    )
    118 
    119    ;; Shouldn't affect NaNess.
    120    (func $global.set.get_f64 (result f64)
    121        ${f64_snan_code}
    122        global.set 1
    123        global.get 1
    124    )
    125 )`, [
    126    { type: 'f32', func: '$add', expected: 'nan:arithmetic' },
    127    { type: 'f32', func: '$global.set.get_f32', expected: f32_snan },
    128    { type: 'f64', func: '$global.set.get_f64', expected: f64_snan },
    129 ]);
    130 
    131 // NaN propagation behavior.
    132 function test(type, opcode, lhs_code, rhs_code) {
    133    let qnan_code = type === 'f32' ? f32_qnan : f64_qnan;
    134 
    135    let t = type;
    136    let op = opcode;
    137 
    138    // Test all forms:
    139    // - (constant, constant),
    140    // - (constant, variable),
    141    // - (variable, constant),
    142    // - (variable, variable)
    143    wasmAssert(`(module
    144        (func $1 (result ${t}) (${t}.${op} ${lhs_code} ${rhs_code}))
    145        (func $2 (param ${t}) (result ${t}) (${t}.${op} (local.get 0) ${rhs_code}))
    146        (func $3 (param ${t}) (result ${t}) (${t}.${op} ${lhs_code} (local.get 0)))
    147        (func $4 (param ${t}) (param ${t}) (result ${t}) (${t}.${op} (local.get 0) (local.get 1)))
    148    )`, [
    149        { type, func: '$1', expected: 'nan:arithmetic' },
    150        { type, func: '$2', args: [lhs_code], expected: 'nan:arithmetic' },
    151        { type, func: '$3', args: [rhs_code], expected: 'nan:arithmetic' },
    152        { type, func: '$4', args: [lhs_code, rhs_code], expected: 'nan:arithmetic' },
    153    ]);
    154 }
    155 
    156 var f32_zero = '(f32.const 0)';
    157 var f64_zero = '(f64.const 0)';
    158 
    159 var f32_one = '(f32.const 1)';
    160 var f64_one = '(f64.const 1)';
    161 
    162 var f32_negone = '(f32.const -1)';
    163 var f64_negone = '(f64.const -1)';
    164 
    165 // x - 0.0 doesn't get folded into x:
    166 test('f32', 'sub', f32_snan_code, f32_zero);
    167 test('f64', 'sub', f64_snan_code, f64_zero);
    168 
    169 // x * 1.0 doesn't get folded into x:
    170 test('f32', 'mul', f32_snan_code, f32_one);
    171 test('f32', 'mul', f32_one, f32_snan_code);
    172 
    173 test('f64', 'mul', f64_snan_code, f64_one);
    174 test('f64', 'mul', f64_one, f64_snan_code);
    175 
    176 // x * -1.0 doesn't get folded into -x:
    177 test('f32', 'mul', f32_snan_code, f32_negone);
    178 test('f32', 'mul', f32_negone, f32_snan_code);
    179 
    180 test('f64', 'mul', f64_snan_code, f64_negone);
    181 test('f64', 'mul', f64_negone, f64_snan_code);
    182 
    183 // x / -1.0 doesn't get folded into -1 * x:
    184 test('f32', 'div', f32_snan_code, f32_negone);
    185 test('f64', 'div', f64_snan_code, f64_negone);
    186 
    187 // min doesn't get folded when one of the operands is a NaN
    188 test('f32', 'min', f32_snan_code, f32_zero);
    189 test('f32', 'min', f32_zero, f32_snan_code);
    190 
    191 test('f64', 'min', f64_snan_code, f64_zero);
    192 test('f64', 'min', f64_zero, f64_snan_code);
    193 
    194 // ditto for max
    195 test('f32', 'max', f32_snan_code, f32_zero);
    196 test('f32', 'max', f32_zero, f32_snan_code);
    197 
    198 test('f64', 'max', f64_snan_code, f64_zero);
    199 test('f64', 'max', f64_zero, f64_snan_code);