Loading js/src/jit-test/lib/wasm.js +43 −26 Original line number Diff line number Diff line Loading @@ -50,6 +50,49 @@ function jsify(wasmVal) { return wasmVal; } function makeF32Assert(i, func, expected, params=[]) { return ` (func (export "assert_${i}") (result i32) ${ params.join('\n') } call ${func} i32.reinterpret/f32 i32.const ${expected} i32.eq )`; } function makeF64Assert(i, func, expected, params=[]) { return ` (func (export "assert_${i}") (result i32) ${ params.join('\n') } call ${func} i64.reinterpret/f64 i64.const ${expected} i64.eq )`; } function wasmAssert(src, assertions) { let newSrc = src.substr(0, src.lastIndexOf(')')); let i = 0; for (let a of assertions) { if (a.type === 'f32') newSrc += makeF32Assert(i++, a.func, a.expected, a.params); if (a.type === 'f64') newSrc += makeF64Assert(i++, a.func, a.expected, a.params); } newSrc += ')'; let { exports } = wasmEvalText(newSrc); for (let j = 0; j < i; j++) { let { func, expected, params } = assertions[j]; let paramText = params ? params.join(', ') : ''; assertEq(exports['assert_' + j](), 1, `Unexpected value when running ${func}(${paramText}), expecting ${expected}.`); } } // Assert that the expected value is equal to the int64 value, as passed by // Baldr: {low: int32, high: int32}. // - if the expected value is in the int32 range, it can be just a number. Loading @@ -72,32 +115,6 @@ function assertEqI64(observed, expect) { } } // Asserts in Baldr test mode that NaN payloads match. function assertEqNaN(x, y) { if (typeof x === 'number') { assertEq(Number.isNaN(x), Number.isNaN(y)); return; } assertEq(typeof x === 'object' && typeof x.nan_low === 'number', true, "assertEqNaN args must have shape {nan_high, nan_low}"); assertEq(typeof y === 'object' && typeof y.nan_low === 'number', true, "assertEqNaN args must have shape {nan_high, nan_low}"); assertEq(typeof x.nan_high, typeof y.nan_high, "both args must have nan_high, or none"); assertEq(x.nan_high, y.nan_high, "assertEqNaN nan_high don't match"); if (typeof x.nan_low !== 'undefined') assertEq(x.nan_low, y.nan_low, "assertEqNaN nan_low don't match"); } function createI64(val) { let ret; if (typeof val === 'number') { Loading js/src/jit-test/tests/wasm/nan-semantics.js +79 −79 Original line number Diff line number Diff line Loading @@ -36,9 +36,6 @@ assertSameBitPattern(0, 8, 8); wasmEvalText('(module (import "" "float64" (param f64)) (func (call 0 (f64.const nan:0x123456))) (export "" 0))', checkBitPatterns).exports[""](); // Enable test mode. setJitCompilerOption('wasm.test-mode', 1); // SANITY CHECKS // There are two kinds of NaNs: signaling and quiet. Usually, the first bit of Loading @@ -48,83 +45,86 @@ setJitCompilerOption('wasm.test-mode', 1); // A float32 has 32 bits, 23 bits of them being reserved for the mantissa // (= NaN payload). var f32_nan_base = 0x7f800000; var f32_qnan_code = '(f32.const nan:0x600000)'; var f32_snan_code = '(f32.const nan:0x200000)'; var f32_snan = wasmEvalText(`(module (func (result f32) ${f32_snan_code}) (export "" 0))`).exports[""](); assertEqNaN(f32_snan, { nan_low: f32_nan_base | 0x200000 }); var f32_qnan_code = '(f32.const nan:0x600000)'; var f32_qnan = wasmEvalText(`(module (func (result f32) ${f32_qnan_code}) (export "" 0))`).exports[""](); assertEqNaN(f32_qnan, { nan_low: f32_nan_base | 0x600000 }); var f32_snan = '0x7fa00000'; var f32_qnan = '0x7fe00000'; // A float64 has 64 bits, 1 for the sign, 11 for the exponent, the rest for the // mantissa (payload). var f64_nan_base_high = 0x7ff00000; var f64_snan_code = '(f64.const nan:0x4000000000000)'; var f64_snan = wasmEvalText(`(module (func (result f64) ${f64_snan_code}) (export "" 0))`).exports[""](); assertEqNaN(f64_snan, { nan_low: 0x0, nan_high: f64_nan_base_high | 0x40000 }); var f64_qnan_code = '(f64.const nan:0xc000000000000)'; var f64_qnan = wasmEvalText(`(module (func (result f64) ${f64_qnan_code}) (export "" 0))`).exports[""](); assertEqNaN(f64_qnan, { nan_low: 0x0, nan_high: f64_nan_base_high | 0xc0000 }); // Actual tests. // An example where a signaling nan gets transformed into a quiet nan: // snan + 0.0 = qnan var nan = wasmEvalText(`(module (func (result f32) (f32.add ${f32_snan_code} (f32.const 0))) (export "" 0))`).exports[""](); assertEqNaN(nan, f32_qnan); // Globals. var m = wasmEvalText(`(module (import "globals" "x" (global f32)) (func (result f32) (get_global 0)) (export "global" global 0) (export "test" 0)) `, { globals: { x: f32_snan } }).exports; assertEqNaN(m.test(), f32_snan); assertEqNaN(m.global, f32_snan); var f64_snan = '0x7ff4000000000000'; var f64_qnan = '0x7ffc000000000000'; wasmAssert(`(module (func $f32_snan (result f32) ${f32_snan_code}) (func $f32_qnan (result f32) ${f32_qnan_code}) (func $f64_snan (result f64) ${f64_snan_code}) (func $f64_qnan (result f64) ${f64_qnan_code}) )`, [ { 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 }, ]); var m = wasmEvalText(`(module (import "globals" "x" (global f64)) (func (result f64) (get_global 0)) (export "global" global 0) (export "test" 0)) `, { globals: { x: f64_snan } }).exports; // Actual tests. assertEqNaN(m.test(), f64_snan); assertEqNaN(m.global, f64_snan); 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))) ;; Shouldn't affect NaNess. (func $set_get_global_f32 (result f32) ${f32_snan_code} set_global 0 get_global 0 ) ;; Shouldn't affect NaNess. (func $set_get_global_f64 (result f64) ${f64_snan_code} set_global 1 get_global 1 ) )`, [ { type: 'f32', func: '$add', expected: f32_qnan }, { type: 'f32', func: '$set_get_global_f32', expected: f32_snan }, { type: 'f64', func: '$set_get_global_f64', expected: f64_snan }, ]); // NaN propagation behavior. var constantCache = new Map; function getConstant(code) { if (typeof code === 'number') return code; if (constantCache.has(code)) { return constantCache.get(code); } let type = code.indexOf('f32') >= 0 ? 'f32' : 'f64'; let val = wasmEvalText(`(module (func (result ${type}) ${code}) (export "" 0))`).exports[""](); constantCache.set(code, val); return val; } function test(type, opcode, lhs_code, rhs_code) { let qnan_code = type === 'f32' ? f32_qnan : f64_qnan; function test(type, opcode, snan_code, rhs_code, qnan_val) { var snan_val = getConstant(snan_code); var rhs = getConstant(rhs_code); let t = type; let op = opcode; // Test all forms: // - (constant, constant), // - (constant, variable), // - (variable, constant), // - (variable, variable) assertEqNaN(wasmEvalText(`(module (func (result ${type}) (${type}.${opcode} ${snan_code} ${rhs_code})) (export "" 0))`).exports[""](), qnan_val); assertEqNaN(wasmEvalText(`(module (func (param ${type}) (result ${type}) (${type}.${opcode} (get_local 0) ${rhs_code})) (export "" 0))`).exports[""](snan_val), qnan_val); assertEqNaN(wasmEvalText(`(module (func (param ${type}) (result ${type}) (${type}.${opcode} ${snan_code} (get_local 0))) (export "" 0))`).exports[""](rhs), qnan_val); assertEqNaN(wasmEvalText(`(module (func (param ${type}) (param ${type}) (result ${type}) (${type}.${opcode} (get_local 0) (get_local 1))) (export "" 0))`).exports[""](snan_val, rhs), qnan_val); wasmAssert(`(module (func $1 (result ${t}) (${t}.${op} ${lhs_code} ${rhs_code})) (func $2 (param ${t}) (result ${t}) (${t}.${op} (get_local 0) ${rhs_code})) (func $3 (param ${t}) (result ${t}) (${t}.${op} ${lhs_code} (get_local 0))) (func $4 (param ${t}) (param ${t}) (result ${t}) (${t}.${op} (get_local 0) (get_local 1))) )`, [ { type, func: '$1', expected: qnan_code }, { type, func: '$2', params: [lhs_code], expected: qnan_code }, { type, func: '$3', params: [rhs_code], expected: qnan_code }, { type, func: '$4', params: [lhs_code, rhs_code], expected: qnan_code }, ]); } var f32_zero = '(f32.const 0)'; Loading @@ -137,39 +137,39 @@ var f32_negone = '(f32.const -1)'; var f64_negone = '(f64.const -1)'; // x - 0.0 doesn't get folded into x: test('f32', 'sub', f32_snan_code, f32_zero, f32_qnan); test('f64', 'sub', f64_snan_code, f64_zero, f64_qnan); test('f32', 'sub', f32_snan_code, f32_zero); test('f64', 'sub', f64_snan_code, f64_zero); // x * 1.0 doesn't get folded into x: test('f32', 'mul', f32_snan_code, f32_one, f32_qnan); test('f32', 'mul', f32_one, f32_snan_code, f32_qnan); test('f32', 'mul', f32_snan_code, f32_one); test('f32', 'mul', f32_one, f32_snan_code); test('f64', 'mul', f64_snan_code, f64_one, f64_qnan); test('f64', 'mul', f64_one, f64_snan_code, f64_qnan); test('f64', 'mul', f64_snan_code, f64_one); test('f64', 'mul', f64_one, f64_snan_code); // x * -1.0 doesn't get folded into -x: test('f32', 'mul', f32_snan_code, f32_negone, f32_qnan); test('f32', 'mul', f32_negone, f32_snan_code, f32_qnan); test('f32', 'mul', f32_snan_code, f32_negone); test('f32', 'mul', f32_negone, f32_snan_code); test('f64', 'mul', f64_snan_code, f64_negone, f64_qnan); test('f64', 'mul', f64_negone, f64_snan_code, f64_qnan); test('f64', 'mul', f64_snan_code, f64_negone); test('f64', 'mul', f64_negone, f64_snan_code); // x / -1.0 doesn't get folded into -1 * x: test('f32', 'div', f32_snan_code, f32_negone, f32_qnan); test('f64', 'div', f64_snan_code, f64_negone, f64_qnan); test('f32', 'div', f32_snan_code, f32_negone); test('f64', 'div', f64_snan_code, f64_negone); // min doesn't get folded when one of the operands is a NaN test('f32', 'min', f32_snan_code, f32_zero, f32_qnan); test('f32', 'min', f32_zero, f32_snan_code, f32_qnan); test('f32', 'min', f32_snan_code, f32_zero); test('f32', 'min', f32_zero, f32_snan_code); test('f64', 'min', f64_snan_code, f64_zero, f64_qnan); test('f64', 'min', f64_zero, f64_snan_code, f64_qnan); test('f64', 'min', f64_snan_code, f64_zero); test('f64', 'min', f64_zero, f64_snan_code); // ditto for max test('f32', 'max', f32_snan_code, f32_zero, f32_qnan); test('f32', 'max', f32_zero, f32_snan_code, f32_qnan); test('f32', 'max', f32_snan_code, f32_zero); test('f32', 'max', f32_zero, f32_snan_code); test('f64', 'max', f64_snan_code, f64_zero, f64_qnan); test('f64', 'max', f64_zero, f64_snan_code, f64_qnan); test('f64', 'max', f64_snan_code, f64_zero); test('f64', 'max', f64_zero, f64_snan_code); setJitCompilerOption('wasm.test-mode', 0); js/src/jit/arm/Simulator-arm.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -2874,14 +2874,14 @@ Simulator::softwareInterrupt(SimInstruction* instr) void Simulator::canonicalizeNaN(double* value) { if (!JitOptions.wasmTestMode && FPSCR_default_NaN_mode_) if (!wasm::CodeExists && !wasm::LookupCodeSegment(get_pc_as<void*>()) && FPSCR_default_NaN_mode_) *value = JS::CanonicalizeNaN(*value); } void Simulator::canonicalizeNaN(float* value) { if (!JitOptions.wasmTestMode && FPSCR_default_NaN_mode_) if (!wasm::CodeExists && !wasm::LookupCodeSegment(get_pc_as<void*>()) && FPSCR_default_NaN_mode_) *value = JS::CanonicalizeNaN(*value); } Loading js/src/wasm/WasmInstance.cpp +0 −22 Original line number Diff line number Diff line Loading @@ -615,20 +615,10 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) return false; break; case ValType::F32: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomFloat32NaNObject(cx, v, (uint32_t*)&exportArgs[i])) return false; break; } if (!RoundFloat32(cx, v, (float*)&exportArgs[i])) return false; break; case ValType::F64: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomDoubleNaNObject(cx, v, (uint64_t*)&exportArgs[i])) return false; break; } if (!ToNumber(cx, v, (double*)&exportArgs[i])) return false; break; Loading Loading @@ -724,21 +714,9 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) return false; break; case ExprType::F32: if (JitOptions.wasmTestMode && IsNaN(*(float*)retAddr)) { retObj = CreateCustomNaNObject(cx, (float*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(float*)retAddr)); break; case ExprType::F64: if (JitOptions.wasmTestMode && IsNaN(*(double*)retAddr)) { retObj = CreateCustomNaNObject(cx, (double*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(double*)retAddr)); break; case ExprType::I8x16: Loading js/src/wasm/WasmJS.cpp +0 −84 Original line number Diff line number Diff line Loading @@ -81,72 +81,6 @@ wasm::HasSupport(JSContext* cx) // ============================================================================ // Imports template<typename T> JSObject* wasm::CreateCustomNaNObject(JSContext* cx, T* addr) { MOZ_ASSERT(IsNaN(*addr)); RootedObject obj(cx, JS_NewPlainObject(cx)); if (!obj) return nullptr; int32_t* i32 = (int32_t*)addr; RootedValue intVal(cx, Int32Value(i32[0])); if (!JS_DefineProperty(cx, obj, "nan_low", intVal, JSPROP_ENUMERATE)) return nullptr; if (IsSame<double, T>::value) { intVal = Int32Value(i32[1]); if (!JS_DefineProperty(cx, obj, "nan_high", intVal, JSPROP_ENUMERATE)) return nullptr; } return obj; } template JSObject* wasm::CreateCustomNaNObject(JSContext* cx, float* addr); template JSObject* wasm::CreateCustomNaNObject(JSContext* cx, double* addr); bool wasm::ReadCustomFloat32NaNObject(JSContext* cx, HandleValue v, uint32_t* ret) { RootedObject obj(cx, &v.toObject()); RootedValue val(cx); int32_t i32; if (!JS_GetProperty(cx, obj, "nan_low", &val)) return false; if (!ToInt32(cx, val, &i32)) return false; *ret = i32; return true; } bool wasm::ReadCustomDoubleNaNObject(JSContext* cx, HandleValue v, uint64_t* ret) { RootedObject obj(cx, &v.toObject()); RootedValue val(cx); int32_t i32; if (!JS_GetProperty(cx, obj, "nan_high", &val)) return false; if (!ToInt32(cx, val, &i32)) return false; *ret = uint32_t(i32); *ret <<= 32; if (!JS_GetProperty(cx, obj, "nan_low", &val)) return false; if (!ToInt32(cx, val, &i32)) return false; *ret |= uint32_t(i32); return true; } JSObject* wasm::CreateI64Object(JSContext* cx, int64_t i64) { Loading Loading @@ -299,15 +233,6 @@ GetImports(JSContext* cx, break; } case ValType::F32: { if (JitOptions.wasmTestMode && v.isObject()) { uint32_t bits; if (!ReadCustomFloat32NaNObject(cx, v, &bits)) return false; float f; BitwiseCast(bits, &f); val = Val(f); break; } if (!v.isNumber()) return ThrowBadImportType(cx, import.field.get(), "Number"); double d; Loading @@ -317,15 +242,6 @@ GetImports(JSContext* cx, break; } case ValType::F64: { if (JitOptions.wasmTestMode && v.isObject()) { uint64_t bits; if (!ReadCustomDoubleNaNObject(cx, v, &bits)) return false; double d; BitwiseCast(bits, &d); val = Val(d); break; } if (!v.isNumber()) return ThrowBadImportType(cx, import.field.get(), "Number"); double d; Loading Loading
js/src/jit-test/lib/wasm.js +43 −26 Original line number Diff line number Diff line Loading @@ -50,6 +50,49 @@ function jsify(wasmVal) { return wasmVal; } function makeF32Assert(i, func, expected, params=[]) { return ` (func (export "assert_${i}") (result i32) ${ params.join('\n') } call ${func} i32.reinterpret/f32 i32.const ${expected} i32.eq )`; } function makeF64Assert(i, func, expected, params=[]) { return ` (func (export "assert_${i}") (result i32) ${ params.join('\n') } call ${func} i64.reinterpret/f64 i64.const ${expected} i64.eq )`; } function wasmAssert(src, assertions) { let newSrc = src.substr(0, src.lastIndexOf(')')); let i = 0; for (let a of assertions) { if (a.type === 'f32') newSrc += makeF32Assert(i++, a.func, a.expected, a.params); if (a.type === 'f64') newSrc += makeF64Assert(i++, a.func, a.expected, a.params); } newSrc += ')'; let { exports } = wasmEvalText(newSrc); for (let j = 0; j < i; j++) { let { func, expected, params } = assertions[j]; let paramText = params ? params.join(', ') : ''; assertEq(exports['assert_' + j](), 1, `Unexpected value when running ${func}(${paramText}), expecting ${expected}.`); } } // Assert that the expected value is equal to the int64 value, as passed by // Baldr: {low: int32, high: int32}. // - if the expected value is in the int32 range, it can be just a number. Loading @@ -72,32 +115,6 @@ function assertEqI64(observed, expect) { } } // Asserts in Baldr test mode that NaN payloads match. function assertEqNaN(x, y) { if (typeof x === 'number') { assertEq(Number.isNaN(x), Number.isNaN(y)); return; } assertEq(typeof x === 'object' && typeof x.nan_low === 'number', true, "assertEqNaN args must have shape {nan_high, nan_low}"); assertEq(typeof y === 'object' && typeof y.nan_low === 'number', true, "assertEqNaN args must have shape {nan_high, nan_low}"); assertEq(typeof x.nan_high, typeof y.nan_high, "both args must have nan_high, or none"); assertEq(x.nan_high, y.nan_high, "assertEqNaN nan_high don't match"); if (typeof x.nan_low !== 'undefined') assertEq(x.nan_low, y.nan_low, "assertEqNaN nan_low don't match"); } function createI64(val) { let ret; if (typeof val === 'number') { Loading
js/src/jit-test/tests/wasm/nan-semantics.js +79 −79 Original line number Diff line number Diff line Loading @@ -36,9 +36,6 @@ assertSameBitPattern(0, 8, 8); wasmEvalText('(module (import "" "float64" (param f64)) (func (call 0 (f64.const nan:0x123456))) (export "" 0))', checkBitPatterns).exports[""](); // Enable test mode. setJitCompilerOption('wasm.test-mode', 1); // SANITY CHECKS // There are two kinds of NaNs: signaling and quiet. Usually, the first bit of Loading @@ -48,83 +45,86 @@ setJitCompilerOption('wasm.test-mode', 1); // A float32 has 32 bits, 23 bits of them being reserved for the mantissa // (= NaN payload). var f32_nan_base = 0x7f800000; var f32_qnan_code = '(f32.const nan:0x600000)'; var f32_snan_code = '(f32.const nan:0x200000)'; var f32_snan = wasmEvalText(`(module (func (result f32) ${f32_snan_code}) (export "" 0))`).exports[""](); assertEqNaN(f32_snan, { nan_low: f32_nan_base | 0x200000 }); var f32_qnan_code = '(f32.const nan:0x600000)'; var f32_qnan = wasmEvalText(`(module (func (result f32) ${f32_qnan_code}) (export "" 0))`).exports[""](); assertEqNaN(f32_qnan, { nan_low: f32_nan_base | 0x600000 }); var f32_snan = '0x7fa00000'; var f32_qnan = '0x7fe00000'; // A float64 has 64 bits, 1 for the sign, 11 for the exponent, the rest for the // mantissa (payload). var f64_nan_base_high = 0x7ff00000; var f64_snan_code = '(f64.const nan:0x4000000000000)'; var f64_snan = wasmEvalText(`(module (func (result f64) ${f64_snan_code}) (export "" 0))`).exports[""](); assertEqNaN(f64_snan, { nan_low: 0x0, nan_high: f64_nan_base_high | 0x40000 }); var f64_qnan_code = '(f64.const nan:0xc000000000000)'; var f64_qnan = wasmEvalText(`(module (func (result f64) ${f64_qnan_code}) (export "" 0))`).exports[""](); assertEqNaN(f64_qnan, { nan_low: 0x0, nan_high: f64_nan_base_high | 0xc0000 }); // Actual tests. // An example where a signaling nan gets transformed into a quiet nan: // snan + 0.0 = qnan var nan = wasmEvalText(`(module (func (result f32) (f32.add ${f32_snan_code} (f32.const 0))) (export "" 0))`).exports[""](); assertEqNaN(nan, f32_qnan); // Globals. var m = wasmEvalText(`(module (import "globals" "x" (global f32)) (func (result f32) (get_global 0)) (export "global" global 0) (export "test" 0)) `, { globals: { x: f32_snan } }).exports; assertEqNaN(m.test(), f32_snan); assertEqNaN(m.global, f32_snan); var f64_snan = '0x7ff4000000000000'; var f64_qnan = '0x7ffc000000000000'; wasmAssert(`(module (func $f32_snan (result f32) ${f32_snan_code}) (func $f32_qnan (result f32) ${f32_qnan_code}) (func $f64_snan (result f64) ${f64_snan_code}) (func $f64_qnan (result f64) ${f64_qnan_code}) )`, [ { 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 }, ]); var m = wasmEvalText(`(module (import "globals" "x" (global f64)) (func (result f64) (get_global 0)) (export "global" global 0) (export "test" 0)) `, { globals: { x: f64_snan } }).exports; // Actual tests. assertEqNaN(m.test(), f64_snan); assertEqNaN(m.global, f64_snan); 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))) ;; Shouldn't affect NaNess. (func $set_get_global_f32 (result f32) ${f32_snan_code} set_global 0 get_global 0 ) ;; Shouldn't affect NaNess. (func $set_get_global_f64 (result f64) ${f64_snan_code} set_global 1 get_global 1 ) )`, [ { type: 'f32', func: '$add', expected: f32_qnan }, { type: 'f32', func: '$set_get_global_f32', expected: f32_snan }, { type: 'f64', func: '$set_get_global_f64', expected: f64_snan }, ]); // NaN propagation behavior. var constantCache = new Map; function getConstant(code) { if (typeof code === 'number') return code; if (constantCache.has(code)) { return constantCache.get(code); } let type = code.indexOf('f32') >= 0 ? 'f32' : 'f64'; let val = wasmEvalText(`(module (func (result ${type}) ${code}) (export "" 0))`).exports[""](); constantCache.set(code, val); return val; } function test(type, opcode, lhs_code, rhs_code) { let qnan_code = type === 'f32' ? f32_qnan : f64_qnan; function test(type, opcode, snan_code, rhs_code, qnan_val) { var snan_val = getConstant(snan_code); var rhs = getConstant(rhs_code); let t = type; let op = opcode; // Test all forms: // - (constant, constant), // - (constant, variable), // - (variable, constant), // - (variable, variable) assertEqNaN(wasmEvalText(`(module (func (result ${type}) (${type}.${opcode} ${snan_code} ${rhs_code})) (export "" 0))`).exports[""](), qnan_val); assertEqNaN(wasmEvalText(`(module (func (param ${type}) (result ${type}) (${type}.${opcode} (get_local 0) ${rhs_code})) (export "" 0))`).exports[""](snan_val), qnan_val); assertEqNaN(wasmEvalText(`(module (func (param ${type}) (result ${type}) (${type}.${opcode} ${snan_code} (get_local 0))) (export "" 0))`).exports[""](rhs), qnan_val); assertEqNaN(wasmEvalText(`(module (func (param ${type}) (param ${type}) (result ${type}) (${type}.${opcode} (get_local 0) (get_local 1))) (export "" 0))`).exports[""](snan_val, rhs), qnan_val); wasmAssert(`(module (func $1 (result ${t}) (${t}.${op} ${lhs_code} ${rhs_code})) (func $2 (param ${t}) (result ${t}) (${t}.${op} (get_local 0) ${rhs_code})) (func $3 (param ${t}) (result ${t}) (${t}.${op} ${lhs_code} (get_local 0))) (func $4 (param ${t}) (param ${t}) (result ${t}) (${t}.${op} (get_local 0) (get_local 1))) )`, [ { type, func: '$1', expected: qnan_code }, { type, func: '$2', params: [lhs_code], expected: qnan_code }, { type, func: '$3', params: [rhs_code], expected: qnan_code }, { type, func: '$4', params: [lhs_code, rhs_code], expected: qnan_code }, ]); } var f32_zero = '(f32.const 0)'; Loading @@ -137,39 +137,39 @@ var f32_negone = '(f32.const -1)'; var f64_negone = '(f64.const -1)'; // x - 0.0 doesn't get folded into x: test('f32', 'sub', f32_snan_code, f32_zero, f32_qnan); test('f64', 'sub', f64_snan_code, f64_zero, f64_qnan); test('f32', 'sub', f32_snan_code, f32_zero); test('f64', 'sub', f64_snan_code, f64_zero); // x * 1.0 doesn't get folded into x: test('f32', 'mul', f32_snan_code, f32_one, f32_qnan); test('f32', 'mul', f32_one, f32_snan_code, f32_qnan); test('f32', 'mul', f32_snan_code, f32_one); test('f32', 'mul', f32_one, f32_snan_code); test('f64', 'mul', f64_snan_code, f64_one, f64_qnan); test('f64', 'mul', f64_one, f64_snan_code, f64_qnan); test('f64', 'mul', f64_snan_code, f64_one); test('f64', 'mul', f64_one, f64_snan_code); // x * -1.0 doesn't get folded into -x: test('f32', 'mul', f32_snan_code, f32_negone, f32_qnan); test('f32', 'mul', f32_negone, f32_snan_code, f32_qnan); test('f32', 'mul', f32_snan_code, f32_negone); test('f32', 'mul', f32_negone, f32_snan_code); test('f64', 'mul', f64_snan_code, f64_negone, f64_qnan); test('f64', 'mul', f64_negone, f64_snan_code, f64_qnan); test('f64', 'mul', f64_snan_code, f64_negone); test('f64', 'mul', f64_negone, f64_snan_code); // x / -1.0 doesn't get folded into -1 * x: test('f32', 'div', f32_snan_code, f32_negone, f32_qnan); test('f64', 'div', f64_snan_code, f64_negone, f64_qnan); test('f32', 'div', f32_snan_code, f32_negone); test('f64', 'div', f64_snan_code, f64_negone); // min doesn't get folded when one of the operands is a NaN test('f32', 'min', f32_snan_code, f32_zero, f32_qnan); test('f32', 'min', f32_zero, f32_snan_code, f32_qnan); test('f32', 'min', f32_snan_code, f32_zero); test('f32', 'min', f32_zero, f32_snan_code); test('f64', 'min', f64_snan_code, f64_zero, f64_qnan); test('f64', 'min', f64_zero, f64_snan_code, f64_qnan); test('f64', 'min', f64_snan_code, f64_zero); test('f64', 'min', f64_zero, f64_snan_code); // ditto for max test('f32', 'max', f32_snan_code, f32_zero, f32_qnan); test('f32', 'max', f32_zero, f32_snan_code, f32_qnan); test('f32', 'max', f32_snan_code, f32_zero); test('f32', 'max', f32_zero, f32_snan_code); test('f64', 'max', f64_snan_code, f64_zero, f64_qnan); test('f64', 'max', f64_zero, f64_snan_code, f64_qnan); test('f64', 'max', f64_snan_code, f64_zero); test('f64', 'max', f64_zero, f64_snan_code); setJitCompilerOption('wasm.test-mode', 0);
js/src/jit/arm/Simulator-arm.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -2874,14 +2874,14 @@ Simulator::softwareInterrupt(SimInstruction* instr) void Simulator::canonicalizeNaN(double* value) { if (!JitOptions.wasmTestMode && FPSCR_default_NaN_mode_) if (!wasm::CodeExists && !wasm::LookupCodeSegment(get_pc_as<void*>()) && FPSCR_default_NaN_mode_) *value = JS::CanonicalizeNaN(*value); } void Simulator::canonicalizeNaN(float* value) { if (!JitOptions.wasmTestMode && FPSCR_default_NaN_mode_) if (!wasm::CodeExists && !wasm::LookupCodeSegment(get_pc_as<void*>()) && FPSCR_default_NaN_mode_) *value = JS::CanonicalizeNaN(*value); } Loading
js/src/wasm/WasmInstance.cpp +0 −22 Original line number Diff line number Diff line Loading @@ -615,20 +615,10 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) return false; break; case ValType::F32: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomFloat32NaNObject(cx, v, (uint32_t*)&exportArgs[i])) return false; break; } if (!RoundFloat32(cx, v, (float*)&exportArgs[i])) return false; break; case ValType::F64: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomDoubleNaNObject(cx, v, (uint64_t*)&exportArgs[i])) return false; break; } if (!ToNumber(cx, v, (double*)&exportArgs[i])) return false; break; Loading Loading @@ -724,21 +714,9 @@ Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) return false; break; case ExprType::F32: if (JitOptions.wasmTestMode && IsNaN(*(float*)retAddr)) { retObj = CreateCustomNaNObject(cx, (float*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(float*)retAddr)); break; case ExprType::F64: if (JitOptions.wasmTestMode && IsNaN(*(double*)retAddr)) { retObj = CreateCustomNaNObject(cx, (double*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(double*)retAddr)); break; case ExprType::I8x16: Loading
js/src/wasm/WasmJS.cpp +0 −84 Original line number Diff line number Diff line Loading @@ -81,72 +81,6 @@ wasm::HasSupport(JSContext* cx) // ============================================================================ // Imports template<typename T> JSObject* wasm::CreateCustomNaNObject(JSContext* cx, T* addr) { MOZ_ASSERT(IsNaN(*addr)); RootedObject obj(cx, JS_NewPlainObject(cx)); if (!obj) return nullptr; int32_t* i32 = (int32_t*)addr; RootedValue intVal(cx, Int32Value(i32[0])); if (!JS_DefineProperty(cx, obj, "nan_low", intVal, JSPROP_ENUMERATE)) return nullptr; if (IsSame<double, T>::value) { intVal = Int32Value(i32[1]); if (!JS_DefineProperty(cx, obj, "nan_high", intVal, JSPROP_ENUMERATE)) return nullptr; } return obj; } template JSObject* wasm::CreateCustomNaNObject(JSContext* cx, float* addr); template JSObject* wasm::CreateCustomNaNObject(JSContext* cx, double* addr); bool wasm::ReadCustomFloat32NaNObject(JSContext* cx, HandleValue v, uint32_t* ret) { RootedObject obj(cx, &v.toObject()); RootedValue val(cx); int32_t i32; if (!JS_GetProperty(cx, obj, "nan_low", &val)) return false; if (!ToInt32(cx, val, &i32)) return false; *ret = i32; return true; } bool wasm::ReadCustomDoubleNaNObject(JSContext* cx, HandleValue v, uint64_t* ret) { RootedObject obj(cx, &v.toObject()); RootedValue val(cx); int32_t i32; if (!JS_GetProperty(cx, obj, "nan_high", &val)) return false; if (!ToInt32(cx, val, &i32)) return false; *ret = uint32_t(i32); *ret <<= 32; if (!JS_GetProperty(cx, obj, "nan_low", &val)) return false; if (!ToInt32(cx, val, &i32)) return false; *ret |= uint32_t(i32); return true; } JSObject* wasm::CreateI64Object(JSContext* cx, int64_t i64) { Loading Loading @@ -299,15 +233,6 @@ GetImports(JSContext* cx, break; } case ValType::F32: { if (JitOptions.wasmTestMode && v.isObject()) { uint32_t bits; if (!ReadCustomFloat32NaNObject(cx, v, &bits)) return false; float f; BitwiseCast(bits, &f); val = Val(f); break; } if (!v.isNumber()) return ThrowBadImportType(cx, import.field.get(), "Number"); double d; Loading @@ -317,15 +242,6 @@ GetImports(JSContext* cx, break; } case ValType::F64: { if (JitOptions.wasmTestMode && v.isObject()) { uint64_t bits; if (!ReadCustomDoubleNaNObject(cx, v, &bits)) return false; double d; BitwiseCast(bits, &d); val = Val(d); break; } if (!v.isNumber()) return ThrowBadImportType(cx, import.field.get(), "Number"); double d; Loading