Bug 202002 - Consider ensuring that NaNs are always represented by pureNaN before boxing.
Summary: Consider ensuring that NaNs are always represented by pureNaN before boxing.
Status: ASSIGNED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Mark Lam
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-09-19 13:36 PDT by Mark Lam
Modified: 2019-09-19 19:30 PDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Mark Lam 2019-09-19 13:36:01 PDT
Most double operations will not produce NaNs.  For those that can e.g. division, we can purifyNaN the value before we box it.

If we do this, then we can reduce the JSValue tag to only 14 bits.  The key insight is as follows:

The highest double value we can have will now be negative infinity i.e. starts with 0xfff0.
The "negative" pureNaN will now no longer be possible because we ensure that it gets purified into the "positive" form that starts with 0x7ff8.

This means if we add a DoubleEncodeOffset of 1 << 50 (starts with 0x0040), negative infinity becomes 0xfff4.  That leaves us 0xfffc for ints, and 0x0000 for pointers.
Comment 1 Mark Lam 2019-09-19 13:51:00 PDT
(In reply to Mark Lam from comment #0)
> Most double operations will not produce NaNs.  For those that can e.g.
> division, we can purifyNaN the value before we box it.
> 
> If we do this, then we can reduce the JSValue tag to only 14 bits.  The key
> insight is as follows:
> 
> The highest double value we can have will now be negative infinity i.e.
> starts with 0xfff0.
> The "negative" pureNaN will now no longer be possible because we ensure that
> it gets purified into the "positive" form that starts with 0x7ff8.
> 
> This means if we add a DoubleEncodeOffset of 1 << 50 (starts with 0x0040),
> negative infinity becomes 0xfff4.  That leaves us 0xfffc for ints, and
> 0x0000 for pointers.

Correction: 1 << 50 starts with 0x0004, not 0x0040.
Comment 2 Geoffrey Garen 2019-09-19 16:27:03 PDT
I don't think it's valid to convert negative NaN to positive NaN. My memory is that the sign bit is observable to the JavaScript programmer.
Comment 3 Mark Lam 2019-09-19 16:36:15 PDT
(In reply to Geoffrey Garen from comment #2)
> I don't think it's valid to convert negative NaN to positive NaN. My memory
> is that the sign bit is observable to the JavaScript programmer.

That's interesting, but would not be spec compliant.  See https://www.ecma-international.org/ecma-262/9.0/index.html#sec-ecmascript-language-types-number-type

"the 9007199254740990 (that is, 253-2) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special NaN value. (Note that the NaN value is produced by the program expression NaN.) In some implementations, external code might be able to detect a difference between various Not-a-Number values, but such behaviour is implementation-dependent; to ECMAScript code, all NaN values are indistinguishable from each other."

Do we need to keep the non-compliant behavior?
Comment 4 Yusuke Suzuki 2019-09-19 16:41:26 PDT
(In reply to Geoffrey Garen from comment #2)
> I don't think it's valid to convert negative NaN to positive NaN. My memory
> is that the sign bit is observable to the JavaScript programmer.

I think this is positive-zero and negative-zero, right?

var z = -0;
print(1 / z) // => -Infinity
var z = 0;
print(1 / z) // => Infinity

But for NaN, all the calculation with NaN will produce NaN in JS world. And Math.sign(NaN) returns NaN. So I think, in JS world (not saying about wasm world), we do not have a way to distinguish negative and positive NaN.
Comment 5 Geoffrey Garen 2019-09-19 16:47:01 PDT
> Do we need to keep the non-compliant behavior?

I wasn't aware of that spec text. I think it's new.

I don't know of any need to stick to our old behavior, given that the spec now mandates a single NaN in JavaScript.