Bug 201633 - [JSC] Remove CheckAdd in JetStream2/async-fs's Math.random function
Summary: [JSC] Remove CheckAdd in JetStream2/async-fs's Math.random function
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Yusuke Suzuki
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2019-09-09 23:18 PDT by Yusuke Suzuki
Modified: 2019-09-22 02:08 PDT (History)
7 users (show)

See Also:


Attachments
Patch (10.32 KB, patch)
2019-09-20 22:09 PDT, Yusuke Suzuki
no flags Details | Formatted Diff | Diff
Patch (10.32 KB, patch)
2019-09-20 22:17 PDT, Yusuke Suzuki
mark.lam: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Yusuke Suzuki 2019-09-09 23:18:37 PDT
Math.random function implementation in async-fs repeatedly emits CheckAdd.
And this makes PutGlobalVariable un-sunkable, and bloats many code that is not inherently necessary.

 35     return function() {
 36         // Robert Jenkins' 32 bit integer hash function.
 37         seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
 38         seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
 39         seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
 40         seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
 41         seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
 42         seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
 43         return (seed & 0xfffffff) / 0x10000000;
 44     };

But seems that `& 0xffffffff` is saying that we only care lower 32bit. I would like to check how to remove the above check-add and make this efficient.
Comment 1 Yusuke Suzuki 2019-09-19 01:35:57 PDT
(In reply to Yusuke Suzuki from comment #0)
> Math.random function implementation in async-fs repeatedly emits CheckAdd.
> And this makes PutGlobalVariable un-sunkable, and bloats many code that is
> not inherently necessary.
> 
>  35     return function() {
>  36         // Robert Jenkins' 32 bit integer hash function.
>  37         seed = ((seed + 0x7ed55d16) + (seed << 12))  & 0xffffffff;
>  38         seed = ((seed ^ 0xc761c23c) ^ (seed >>> 19)) & 0xffffffff;
>  39         seed = ((seed + 0x165667b1) + (seed << 5))   & 0xffffffff;
>  40         seed = ((seed + 0xd3a2646c) ^ (seed << 9))   & 0xffffffff;
>  41         seed = ((seed + 0xfd7046c5) + (seed << 3))   & 0xffffffff;
>  42         seed = ((seed ^ 0xb55a4f09) ^ (seed >>> 16)) & 0xffffffff;
>  43         return (seed & 0xfffffff) / 0x10000000;
>  44     };
> 
> But seems that `& 0xffffffff` is saying that we only care lower 32bit. I
> would like to check how to remove the above check-add and make this
> efficient.

Maybe, we should do integer-range-optimization in B3 to convert CheckAdd to Add. Then, CSE should work.
Comment 2 Yusuke Suzuki 2019-09-19 01:42:11 PDT
Or, extend existing DFG IntegerRangeOptimization Phase.
Comment 3 Yusuke Suzuki 2019-09-19 02:02:29 PDT
After looking through the code, I think,

1. We can improve things in B3 by analyzing integer-range.
2. But in this case, we can easily improve it in DFG since DFG knows Int52Rep(Int32) semantics while it is encoded into shift in B3

And the most common case in general JS code with Int52 is `ArithAdd(Int52Rep(Int32), Int52Constant)`, which will be shown when we decide upgrading Int32 to Int52. After considering about it, I'm guessing this is soooooooooo common than I expected (maybe, the most common pattern for Int52 calculation?). If we can remove CheckOverflow from this, we could improve many programs (even in non-FTL) very easily.
While (1) is attractive, just adding (2) adhocly also looks nice to me. (1) looks nice to me too, it could potentially improve bound-checked Wasm code...? I'm not sure.
Comment 4 Yusuke Suzuki 2019-09-20 18:30:01 PDT
Or, we can do this easily in B3, checking.
Comment 5 Yusuke Suzuki 2019-09-20 19:09:32 PDT
Do it in B3 first. I found that this is super easy.
Comment 6 Yusuke Suzuki 2019-09-20 22:09:28 PDT
Created attachment 379305 [details]
Patch
Comment 7 Yusuke Suzuki 2019-09-20 22:17:47 PDT
Created attachment 379306 [details]
Patch
Comment 8 Radar WebKit Bug Importer 2019-09-20 22:55:55 PDT
<rdar://problem/55583016>
Comment 9 Mark Lam 2019-09-21 20:56:48 PDT
Comment on attachment 379306 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=379306&action=review

Nice.  r=me

> Source/JavaScriptCore/ChangeLog:25
> +        This converts many CheckAdd in JetStream2/async-fs's hot function simple Add, removes bunch of unnecessary instructions which exist because of this OSR exit.

/function simple Add, removes bunch/function to simple Add, and removes a bunch/.

> Source/JavaScriptCore/b3/testb3_5.cpp:950
> +    CHECK(invoke<int64_t>(*code, value) == 2ll * static_cast<int32_t>(value));

Why is this static_cast necessary?
Comment 10 Yusuke Suzuki 2019-09-22 00:18:53 PDT
Comment on attachment 379306 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=379306&action=review

>> Source/JavaScriptCore/b3/testb3_5.cpp:950
>> +    CHECK(invoke<int64_t>(*code, value) == 2ll * static_cast<int32_t>(value));
> 
> Why is this static_cast necessary?

This code is precisely simulating the B3's code, like, first performing SExt8, just for readability.
Comment 11 Yusuke Suzuki 2019-09-22 00:19:47 PDT
Committed r250198: <https://trac.webkit.org/changeset/250198>
Comment 12 Mark Lam 2019-09-22 00:38:03 PDT
Comment on attachment 379306 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=379306&action=review

>>> Source/JavaScriptCore/b3/testb3_5.cpp:950
>>> +    CHECK(invoke<int64_t>(*code, value) == 2ll * static_cast<int32_t>(value));
>> 
>> Why is this static_cast necessary?
> 
> This code is precisely simulating the B3's code, like, first performing SExt8, just for readability.

Yeah, but you don't do this for the SExt16 case below.  I noticed the inconsistency and wondered why.  It's not a big deal as the code behaves the same either way.
Comment 13 Yusuke Suzuki 2019-09-22 02:02:58 PDT
Comment on attachment 379306 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=379306&action=review

>>>> Source/JavaScriptCore/b3/testb3_5.cpp:950
>>>> +    CHECK(invoke<int64_t>(*code, value) == 2ll * static_cast<int32_t>(value));
>>> 
>>> Why is this static_cast necessary?
>> 
>> This code is precisely simulating the B3's code, like, first performing SExt8, just for readability.
> 
> Yeah, but you don't do this for the SExt16 case below.  I noticed the inconsistency and wondered why.  It's not a big deal as the code behaves the same either way.

I see, I'll add the static_cast to SExt16 case too!
Comment 14 Yusuke Suzuki 2019-09-22 02:08:32 PDT
Committed r250199: <https://trac.webkit.org/changeset/250199>