Bug 1659667 - expect less specific NaN values. r=bbouvier
authorLars T Hansen <lhansen@mozilla.com>
Thu, 20 Aug 2020 14:08:40 +0000
changeset 610149 bd8b53cf8058f0f5d1e3f7030731160a50925dec
parent 610148 8b1447160ba44970ffa7de621b6abbbc33d8815d
child 610150 4d5c637b367ec1787bbe5e20efbc169d4da81851
push id13553
push userffxbld-merge
push dateMon, 24 Aug 2020 12:51:36 +0000
treeherdermozilla-beta@a54f8b5d0977 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1659667
milestone81.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1659667 - expect less specific NaN values. r=bbouvier These test cases expected specific payloads for some NaN results but the wasm spec does not guarantee that, and some implementations of some architectures will not return the same payloads as x64. So adjust the tests. Differential Revision: https://phabricator.services.mozilla.com/D87455
js/src/jit-test/lib/wasm.js
js/src/jit-test/tests/wasm/conversion.js
js/src/jit-test/tests/wasm/nan-semantics.js
--- a/js/src/jit-test/lib/wasm.js
+++ b/js/src/jit-test/lib/wasm.js
@@ -88,22 +88,36 @@ function _augmentSrc(src, assertions) {
          ${ args ? args.join('\n') : '' }
          call ${func}`;
 
         if (typeof expected !== 'undefined') {
             switch (type) {
                 case 'f32':
                     newSrc += `
          i32.reinterpret/f32
+         ${(function () {
+             if (expected == 'nan:arithmetic') {
+               expected = '0x7FC00000';
+               return '(i32.const 0x7FC00000) i32.and';
+             }
+             return '';
+         })()}
          i32.const ${expected}
          i32.eq`;
                     break;
                 case 'f64':
                     newSrc += `
          i64.reinterpret/f64
+         ${(function () {
+             if (expected == 'nan:arithmetic') {
+               expected = '0x7FF8000000000000';
+               return '(i64.const 0x7FF8000000000000) i64.and';
+             }
+             return '';
+         })()}
          i64.const ${expected}
          i64.eq`;
                     break;
                 case 'i32':
                     newSrc += `
          i32.const ${expected}
          i32.eq`;
                     break;
--- a/js/src/jit-test/tests/wasm/conversion.js
+++ b/js/src/jit-test/tests/wasm/conversion.js
@@ -361,10 +361,36 @@ testConversion('f32', 'convert_u', 'i32'
 
 testConversion('f64', 'convert_s', 'i32', 40, 40);
 testConversion('f64', 'convert_u', 'i32', 40, 40);
 
 testConversion('f32', 'demote', 'f64', 40.1, 40.099998474121094);
 testConversion('f64', 'promote', 'f32', 40.1, 40.099998474121094);
 
 // Non-canonical NaNs.
-wasmFullPass('(module (func (result i32) (i32.reinterpret/f32 (f32.demote/f64 (f64.const -nan:0x4444444444444)))) (export "run" (func 0)))', -0x1dddde);
-wasmFullPass('(module (func (result i32) (local i64) (local.set 0 (i64.reinterpret/f64 (f64.promote/f32 (f32.const -nan:0x222222)))) (i32.xor (i32.wrap/i64 (local.get 0)) (i32.wrap/i64 (i64.shr_u (local.get 0) (i64.const 32))))) (export "run" (func 0)))', -0x4003bbbc);
+
+// Wasm v1.1 spec 4.3.4 "Conversions", subsection demote(z): if z is a NaN but
+// not canonical, return any NaN.  That is, the sign is arbitrary and the
+// payload is arbitrary, except that the most significant bit of the payload
+// must be set.  We check simply that a quiet NaN is returned and ignore the
+// sign and payload.
+
+wasmFullPass(`
+(module
+ (func (result i32)
+  (i32.and
+    (i32.const 0x7FC00000)
+    (i32.reinterpret/f32
+      (f32.demote/f64 (f64.const -nan:0x4444444444444)))))
+ (export "run" (func 0)))`,
+             0x7FC00000);
+
+// Wasm v1.1 spec 4.3.4 "Conversions", subsection promote(z): if z is a NaN but
+// not canonical, return any NaN.  See above.
+
+wasmFullPass(`
+(module
+ (func (result i64)
+   (i64.and
+     (i64.const 0x7FF8000000000000)
+     (i64.reinterpret/f64 (f64.promote/f32 (f32.const -nan:0x222222)))))
+ (export "run" (func 0)))`,
+             0x7FF8_0000_0000_0000n);
--- a/js/src/jit-test/tests/wasm/nan-semantics.js
+++ b/js/src/jit-test/tests/wasm/nan-semantics.js
@@ -6,40 +6,62 @@ function assertSameBitPattern(from, to, 
     for (let i = from; i < to; i++)
         assertEq(u8[i], u8[i + offset], 'non equality in assertSameBitPattern');
 }
 
 // Check that custom NaN can't escape to normal JS, in non-testing mode.
 f32[0] = NaN;
 f32[0] = f32[0]; // Force canonicalization.
 
-f32[1] = wasmEvalText('(module (func (result f32) (f32.const nan:0x123456)) (export "" (func 0)))').exports[""]();
+f32[1] = wasmEvalText(`
+(module
+  (func (result f32)
+    (f32.const nan:0x123456))
+  (export "" (func 0)))
+`).exports[""]();
 assertSameBitPattern(0, 4, 4);
 
 var checkBitPatterns = {
     "": {
         float32(x) {
             f32[1] = x;
             assertSameBitPattern(0, 4, 4);
         },
         float64(x) {
             f64[1] = x;
             assertSameBitPattern(0, 8, 8);
         }
     }
 }
 
-wasmEvalText('(module (import "" "float32" (func (param f32))) (func (call 0 (f32.const nan:0x123456))) (export "" (func 0)))', checkBitPatterns).exports[""]();
+wasmEvalText(`
+(module
+  (import "" "float32" (func (param f32)))
+  (func
+    (call 0 (f32.const nan:0x123456)))
+  (export "" (func 0)))
+`, checkBitPatterns).exports[""]();
 
 f64[0] = NaN;
 f64[0] = f64[0]; // Force canonicalization.
-f64[1] = wasmEvalText('(module (func (result f64) (f64.const nan:0x123456)) (export "" (func 0)))').exports[""]();
+f64[1] = wasmEvalText(`
+(module
+  (func (result f64)
+    (f64.const nan:0x123456))
+  (export "" (func 0)))
+`).exports[""]();
 assertSameBitPattern(0, 8, 8);
 
-wasmEvalText('(module (import "" "float64" (func (param f64))) (func (call 0 (f64.const nan:0x123456))) (export "" (func 0)))', checkBitPatterns).exports[""]();
+wasmEvalText(`
+(module
+  (import "" "float64" (func (param f64)))
+  (func
+    (call 0 (f64.const nan:0x123456)))
+  (export "" (func 0)))
+`, checkBitPatterns).exports[""]();
 
 // SANITY CHECKS
 
 // There are two kinds of NaNs: signaling and quiet. Usually, the first bit of
 // the payload is used to indicate whether it is quiet (1 for quiet, 0 for
 // signaling). Most operations have to transform a signaling NaN into a quiet
 // NaN, which prevents some optimizations in WebAssembly.
 
@@ -70,16 +92,20 @@ wasmAssert(`(module
     { type: 'f32', func: '$f32_snan', expected: f32_snan },
     { type: 'f32', func: '$f32_qnan', expected: f32_qnan },
     { type: 'f64', func: '$f64_snan', expected: f64_snan },
     { type: 'f64', func: '$f64_qnan', expected: f64_qnan },
 ]);
 
 // Actual tests.
 
+// Wasm spec v1.1 section 4.3.3, sections "fadd" et seq and "NaN propagation":
+// If the input NaN is not canonical then the output may be any arithmetic NaN,
+// ie a quiet NaN with arbitrary payload.
+
 wasmAssert(`(module
     (global (mut f32) (f32.const 0))
     (global (mut f64) (f64.const 0))
 
     ;; An example where a signaling nan gets transformed into a quiet nan:
     ;; snan + 0.0 = qnan
     (func $add (result f32) (f32.add ${f32_snan_code} (f32.const 0)))
 
@@ -92,17 +118,17 @@ wasmAssert(`(module
 
     ;; Shouldn't affect NaNess.
     (func $global.set.get_f64 (result f64)
         ${f64_snan_code}
         global.set 1
         global.get 1
     )
 )`, [
-    { type: 'f32', func: '$add', expected: f32_qnan },
+    { type: 'f32', func: '$add', expected: 'nan:arithmetic' },
     { type: 'f32', func: '$global.set.get_f32', expected: f32_snan },
     { type: 'f64', func: '$global.set.get_f64', expected: f64_snan },
 ]);
 
 // NaN propagation behavior.
 function test(type, opcode, lhs_code, rhs_code) {
     let qnan_code = type === 'f32' ? f32_qnan : f64_qnan;
 
@@ -115,20 +141,20 @@ function test(type, opcode, lhs_code, rh
     // - (variable, constant),
     // - (variable, variable)
     wasmAssert(`(module
         (func $1 (result ${t}) (${t}.${op} ${lhs_code} ${rhs_code}))
         (func $2 (param ${t}) (result ${t}) (${t}.${op} (local.get 0) ${rhs_code}))
         (func $3 (param ${t}) (result ${t}) (${t}.${op} ${lhs_code} (local.get 0)))
         (func $4 (param ${t}) (param ${t}) (result ${t}) (${t}.${op} (local.get 0) (local.get 1)))
     )`, [
-        { type, func: '$1', expected: qnan_code },
-        { type, func: '$2', args: [lhs_code], expected: qnan_code },
-        { type, func: '$3', args: [rhs_code], expected: qnan_code },
-        { type, func: '$4', args: [lhs_code, rhs_code], expected: qnan_code },
+        { type, func: '$1', expected: 'nan:arithmetic' },
+        { type, func: '$2', args: [lhs_code], expected: 'nan:arithmetic' },
+        { type, func: '$3', args: [rhs_code], expected: 'nan:arithmetic' },
+        { type, func: '$4', args: [lhs_code, rhs_code], expected: 'nan:arithmetic' },
     ]);
 }
 
 var f32_zero = '(f32.const 0)';
 var f64_zero = '(f64.const 0)';
 
 var f32_one = '(f32.const 1)';
 var f64_one = '(f64.const 1)';