Bug 162544

Summary: [DOMJIT] Introducing DOM binding JIT
Product: WebKit Reporter: Yusuke Suzuki <ysuzuki>
Component: BindingsAssignee: Yusuke Suzuki <ysuzuki>
Status: NEW ---    
Severity: Normal CC: cdumez, fpizlo, ggaren, ossy, saam, sam
Priority: P2    
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Bug Depends on: 163226, 163315, 163458, 163841, 164074, 164115, 164362, 164408, 162916, 162941, 162978, 162980, 163005, 163223, 163224, 163245, 163457, 163657, 163864, 163900, 164004, 164113, 164356, 164627    
Bug Blocks:    
Attachments:
Description Flags
Prototype
none
Prototype rev2
none
Prototype rev3
none
Prototype rev4
none
Prototype rev5
none
Prototype rev6
none
Prototype rev7
none
Prototype rev8
none
Patch
none
Patch
none
Prototype rev9
none
Prototype rev10, CheckDOM becomes very effective
none
Prototype rev11, more clean up none

Description Yusuke Suzuki 2016-09-26 00:10:29 PDT
Some DOM operations are very tiny operation. For example, firstChild just looks up the stored member from Node*.
In these cases, DOM binding code itself occupies large part of execution time.

We would like to optimize DOM binding further by implementing DOM binding code in special IR.
And this IR will be handled in JSC DFG, and they should be correctly inlined.
Comment 1 Yusuke Suzuki 2016-09-26 00:18:59 PDT
Currently I'm planning to introduce binding JIT for DOM accessors first.
In particular, first, I'm planning to implement firstChild etc. (DOM traversal) operations by hand. And later, generalizing them and producing framework to handle DOM accessors.

Why I would like to focus on DOM accessors first is,

1. DOM accessors are typically tiny.

Some DOM accessors are very tiny operations. For example, firstChild just looks up the member field of Node* (Of course, it also has type checking of `this`).
As a result, large part of operation is occupied by DOM binding code.
If DOM operation itself is heavy, DOM binding does not occupy large part of it, and benefit of DOM binding JIT becomes small.

2. We can implement whole the DOM binding logic in JIT

If a DOM operation is complex, while we can implement DOM binding code in JIT, in the middle of JIT code, we still need to call C runtime function to invoke an actual operation. This limits the benefit of DOM binding JIT.

If we can implement whole the logic in JIT code, we can inline DOM operations. And DOM accessors are good example we can easily implement it in JIT.
Comment 2 Yusuke Suzuki 2016-09-26 00:52:12 PDT
To check the benefit, I'll prototype firstChild in DFG intrinsic first.
And after checking performance benefit, I'll consider the way to generalize it.
The target benchmark is Dromaeo dom-traversal.
Comment 3 Yusuke Suzuki 2016-09-27 12:21:25 PDT
OK, initial plan is,

1. Introducing some intrinsic system for CustomAccessorGetter
2. Introduce some DFG node, like, DOMAttributeGet
3. Inline DOM getter into several DFG nodes. If OSRExit occurs, we just fall back to a simple custom getter function call (First, we target such a getter, no side effect).

In (3) case, maybe, we will emit nodes for firstChild such as,

1: node
2: Check(node@1, JSElementType)
3: DOMNodeFlagCheck(node@1, ContainerNode) (Or, we can introduce SpecContainerNode)
4: DOMAttributeGet(node@1, offset to firstChild) (Here, we would like to use heap prediction)
Comment 4 Yusuke Suzuki 2016-09-27 14:29:07 PDT
Created attachment 290008 [details]
Prototype

WIP: Moving WebCore DOM types into JSC
Comment 5 Yusuke Suzuki 2016-09-27 22:48:04 PDT
Created attachment 290058 [details]
Prototype rev2

WIP: Introduce naive DOMJIT generation. Not using it actually yet. Not considering about layering violation since this is a prototype.
Comment 6 Yusuke Suzuki 2016-09-27 23:51:15 PDT
Created attachment 290063 [details]
Prototype rev3

WIP: Adding DFG path.
Comment 7 Yusuke Suzuki 2016-09-27 23:56:07 PDT
Figure out the necessary operation in firstChild.

1: node
2: Check DOMNodeUse, @1
3: GetDOMWrapped @1 => OpaquePointer (We should support such a type in DFG / FTL)
4: CheckDOMNodeFlags @3, IsContainerFlag
5: GetDOMAttributeValue @3, offset, sizeof(Node*) => OpaquePointer
6: GetGlobalObject @1
7: ToDOMWrapper @6, @5, Node => JSNode
Comment 8 Yusuke Suzuki 2016-09-28 00:01:30 PDT
Currently, we take super naive approach since it is a prototype. We directly use DFG::SpeculativeJIT from WebCore (That's bad).
After measuring the performance, we will introduce DOMJIT IR, like the following.

1: node
2: Check DOMNodeUse, @1
3: GetDOMWrapped @1 => OpaquePointer (We should support such a type in DFG / FTL)
4: CheckDOMNodeFlags @3, IsContainerFlag
5: GetDOMAttributeValue @3, offset, sizeof(Node*) => OpaquePointer
6: GetGlobalObject @1
7: ToDOMWrapper @6, @5, Node => JSNode

And pass the data to DFG. And DFG converts this IR to DFG Nodes.

In our first implementation, we leave C++ binding code for LLInt and Baseline. Along with it, DOMJIT attributes has the above IR data.
And when handling these attributes accesses in DFG, we will use the IR to inline it.
Comment 9 Yusuke Suzuki 2016-09-28 23:26:33 PDT
Created attachment 290181 [details]
Prototype rev4

WIP: Add DOMJIT IR
Comment 10 Yusuke Suzuki 2016-09-28 23:42:24 PDT
Created attachment 290184 [details]
Prototype rev5

WIP: Fix more things
Comment 11 Yusuke Suzuki 2016-09-29 10:31:08 PDT
Created attachment 290215 [details]
Prototype rev6

WIP: Fix more things
Comment 12 Yusuke Suzuki 2016-09-29 14:40:15 PDT
Created attachment 290245 [details]
Prototype rev7

WIP: Fix more things
Comment 13 Yusuke Suzuki 2016-09-30 11:37:21 PDT
Created attachment 290357 [details]
Prototype rev8

WIP: Fix more things!
Comment 14 Yusuke Suzuki 2016-09-30 13:01:07 PDT
Anyway, moving JSElementTypes into JSC would us to use these edge filters easily.
It should help if we would like to recognize type signatures in DFG and inline type checks.
Comment 15 Yusuke Suzuki 2016-10-03 02:41:58 PDT
Created attachment 290482 [details]
Patch

WIP: Updating
Comment 16 Yusuke Suzuki 2016-10-03 02:53:45 PDT
Created attachment 290483 [details]
Patch

WIP: Updating
Comment 17 Yusuke Suzuki 2016-10-03 11:22:13 PDT
Created attachment 290496 [details]
Prototype rev9

WIP: Introduce DOMJIT::Patchpoint
Comment 18 Yusuke Suzuki 2016-10-03 22:17:21 PDT
Created attachment 290567 [details]
Prototype rev10, CheckDOM becomes very effective

WIP: Introduce DOMJIT::Patchpoint
Comment 19 Yusuke Suzuki 2016-10-03 23:32:28 PDT
Created attachment 290571 [details]
Prototype rev11, more clean up

WIP: Introduce DOMJIT::Patchpoint
Comment 20 Yusuke Suzuki 2016-10-07 00:23:48 PDT
We will offer 2 types of DOMJIT

1. Hand written DOM inlining.

We inject DOMJIT::Patchpoint compiler into JSC. And JSC uses this to inline DOM operation, and drop type checks.
Since the operation is fully inlined, potentially it has large performance boost.
Some super stupid tiny micro benchmark (that repeatedly accesses document.body.firstChild) shows 40% improvement.
On the other hand, since we wrote it by hand, this way does not scale well.
We will apply this method to only super hot accessors like parentNode etc.

Note that CSS Selector JIT compiler already does the similar things: accessing parentNode etc. directly by using offsets.

2. Exposing signature information.

We will offer function type signature by some representation and pass it to JSC.
JSC will use to drop type checks. Since IDL code generator already knows this,
we can automatically generate such a information. Since we don't perform any inlining,
the performance boost may be limited. But it's worth doing.
For example, if you write like,

document.getElementById("...");

DFG knows,

1. argument count is 1
2. argument is definitely String
3. |this| is document object. We already emit CheckStructure when performing `document.getElementById` GetById ops!

Above 3 things effectively drop many checks of binding code.