Last Comment Bug 584168 - consider canonicalizing nans passed to the JSAPI
: consider canonicalizing nans passed to the JSAPI
Status: RESOLVED FIXED
fixed-in-tracemonkey
:
Product: Core
Classification: Components
Component: JavaScript Engine (show other bugs)
: Trunk
: All All
: -- normal (vote)
: ---
Assigned To: Luke Wagner [:luke]
:
Mentors:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2010-08-03 13:05 PDT by Luke Wagner [:luke]
Modified: 2010-08-07 11:03 PDT (History)
9 users (show)
See Also:
Crash Signature:
(edit)
QA Whiteboard:
Iteration: ---
Points: ---
Has Regression Range: ---
Has STR: ---
beta4+


Attachments
patch (1.75 KB, patch)
2010-08-03 20:07 PDT, Luke Wagner [:luke]
brendan: review+
Details | Diff | Review
dynamic test (1.74 KB, text/x-c++src)
2010-08-05 09:42 PDT, Luke Wagner [:luke]
no flags Details

Description Luke Wagner [:luke] 2010-08-03 13:05:32 PDT
Right now, DOUBLE_TO_JSVAL/JS_NewNumberValue only assert that the given double is a canonical nan.  It seems like, with js-ctypes or other places where code can do scary things outside of our control, that non-canonical nans may arise and break the JS engine.  Since DOUBLE_TO_JSVAL/JS_NewNumberValue are only called from outside the engine (js::Value::setDouble is used inside), adding this check shouldn't affect perf too much.  Does this sound right?
Comment 1 Luke Wagner [:luke] 2010-08-03 20:07:56 PDT
Created attachment 462653 [details] [diff] [review]
patch

Yeah, I think this is a very good idea.  It's a crazy world.
Comment 2 Luke Wagner [:luke] 2010-08-04 04:15:36 PDT
http://hg.mozilla.org/tracemonkey/rev/cb31ec3a3325

Sayre: I think this should be pulled into the beta as well.
Comment 3 Igor Bukanov 2010-08-04 04:34:38 PDT
Comment on attachment 462653 [details] [diff] [review]
patch

> JS_PUBLIC_API(JSBool)
> JS_NewNumberValue(JSContext *cx, jsdouble d, jsval *rval)
> {
>+    d = JS_CANONICALIZE_NAN(d);
>     Valueify(rval)->setNumber(d);
>     return JS_TRUE;
> }

Why this is not done at setNumber level? For example, how do we know that the Math implementation generates only canonical nans?
Comment 4 Luke Wagner [:luke] 2010-08-04 11:05:25 PDT
(In reply to comment #3)

> Why this is not done at setNumber level? For example, how do we know that the
> Math implementation generates only canonical nans?

Because we usually know that a given double is not a non-canonical nan.  A non-canonical nan is a qnan with a non-zero payload, and since a qnan's payload is intended to mean something to the user (like explain where the nan came from), it seems that cos/sin/+/- etc take care to not generate qnans with random payloads.  This is not fixed in the standard, but it looks like JSC depends on it as well.

It might be worth adding it to all the math library calls and measuring whether it even makes a measurable difference.  If its cheap, it might be worth it just for peace of mind.
Comment 5 Igor Bukanov 2010-08-04 12:02:48 PDT
(In reply to comment #4)
> It might be worth adding it to all the math library calls and measuring whether
> it even makes a measurable difference.  If its cheap, it might be worth it just
> for peace of mind.

I still think that it would be better to do that at setNumber level while adding something like setCanonicalNumber and convert the codebase to the latter at places where we know for sure.
Comment 6 Brendan Eich [:brendan] 2010-08-04 12:32:50 PDT
Add assertions to start. We can dial in if any botch.

/be
Comment 7 Luke Wagner [:luke] 2010-08-04 13:42:20 PDT
We already have assertions in DOUBLE_TO_JSVAL_IMPL.
Comment 8 Luke Wagner [:luke] 2010-08-04 14:07:08 PDT
Adding JS_CANONICALIZE_NAN to setDouble (which includes setNumber):

Sunspider:    *1.010x as slow*  538.0ms +/- 0.4%   543.5ms +/- 0.3%
V8:           *1.014x as slow*  5193.9ms +/- 0.3%  5267.9ms +/- 0.5%

highlights:
cube:         *1.108x as slow*   32.0ms +/- 1.9%    35.5ms +/- 3.1%
fannkuch:     *1.031x as slow*   40.9ms +/- 0.9%    42.1ms +/- 1.5%
earley-boyer: *1.023x as slow*  1755.3ms +/- 0.7%   1795.0ms +/- 0.5%
Comment 9 Igor Bukanov 2010-08-04 14:12:43 PDT
(In reply to comment #6)
> Add assertions to start. We can dial in if any botch.

Assertions are not very useful here. They will be hit quite likely only when one, for example, tries to exploit a bug in libc math functions. At that point we will need to rush with a patch.
Comment 10 Igor Bukanov 2010-08-04 14:16:14 PDT
(In reply to comment #8)
> highlights:
> cube:         *1.108x as slow*   32.0ms +/- 1.9%    35.5ms +/- 3.1%
> fannkuch:     *1.031x as slow*   40.9ms +/- 0.9%    42.1ms +/- 1.5%
> earley-boyer: *1.023x as slow*  1755.3ms +/- 0.7%   1795.0ms +/- 0.5%

This does not indicate if the slowdown comes from Math.sin etc. implementation or from other places where setDouble is used and where one can guarantee that nan is canonical.
Comment 11 Luke Wagner [:luke] 2010-08-04 14:20:48 PDT
Clearly, I was arguing the point that just changing setNumber to canonicalize is a slowdown.  I did a second measurement that *only* canonicalized for libc math calls in jsmath.cpp and the entire slowdown remained.
Comment 12 Paul Wright 2010-08-05 06:14:54 PDT
Was this potential issue already in the code, or was it introduced during the recent development cycle(s)?  If the problem you are fixing with this bug has always been there, what historical data is available to indicate a real-world problem?  The point is, why take a 1 - 1.5% performance hit to fix a problem that isn't really a problem?
Comment 13 Igor Bukanov 2010-08-05 06:42:12 PDT
(In reply to comment #12)
> Was this potential issue already in the code, or was it introduced during the
> recent development cycle(s)?

It was introduced with recent changes in SM so we have no historical data.

> The point is, why take a 1 - 1.5% performance hit to fix a problem
> that isn't really a problem?

It is not known if there is a problem. The current code assumption is that libc math functions never return non-canonical NaNs and the current observations confirm this. But that does not mean that libc of maths functions implementations on all supported platforms do not have bugs that, with a specific input, would return a bad exploitable NaN. Such bugs especially potent since C/C++ standards do not insist on canonical NaNs and bad NaNs were never problematic until recently so there were never a pressure to fix such bugs.

Given that my opinion is to take a performance hit until we have reasonable assurances that math behaves properly.
Comment 14 Luke Wagner [:luke] 2010-08-05 09:42:06 PDT
Created attachment 463187 [details]
dynamic test

How about the less pessimistic solution:

While sin(x) could return a non-canonical nan for any of the 2^64 inputs, for most of those inputs, that would be wrong according to the spec: these libc math functions only have a choice in the matter for a select few input values, mostly infinities, zeroes, (canonical) nans.  Thus, we could be reasonably confident by testing just these weird double values as part of, say, jsapi-tests.  Incidentally, I wrote such a test two days ago (when I started getting paranoid), although its just an ad hoc test, not a jsapi-test.
Comment 15 Paul Wright 2010-08-05 10:37:33 PDT
I am in agreement with Luke.  If no data is available, do the needed testing to acquire it.  You are all doing a tremendous amount of work (and are doing a great job, btw) to improve the overall JS performance.  It seems very counter-productive to be chipping away at perf improvements on the backend without doing the due diligence Luke is suggesting.

Note You need to log in before you can comment on or make changes to this bug.