[DOMJIT] Implement Node accessors in DOMJIT
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 10 Oct 2016 19:57:55 +0000 (19:57 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 10 Oct 2016 19:57:55 +0000 (19:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=163005

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Add some helper methods and offsetOfXXX for JSC::Weak since it is used
for DOM wrapper caching.

And make DOMJIT::Patchpoint in FTL closer to one in DFG. We add resultConstraint
to avoid the situation that the same register is allocated to child and result.

We also extend DOMJIT::Patchpoint to tell useTagTypeNumberRegister / useTagMaskRegister.

* dfg/DFGSpeculativeJIT.h:
(JSC::DFG::SpeculativeJIT::callOperation):
* domjit/DOMJITSlowPathCalls.h:
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileCheckDOM):
(JSC::FTL::DFG::LowerDFGToB3::compileCallDOM):
* heap/WeakImpl.h:
(JSC::WeakImpl::offsetOfJSValue):
(JSC::WeakImpl::offsetOfWeakHandleOwner):
* jit/AssemblyHelpers.h:
(JSC::AssemblyHelpers::boxCell):
(JSC::AssemblyHelpers::boxInt32): Deleted.
* jit/JITOperations.h:

Source/WebCore:

This patch implements DOMJIT accessors in WebCore. We plan to offer 2 things in 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. 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.

This patch implements the first one, hand written DOM inlining facility. We add a new IDL attribute,
"DOMJIT". This means that "This readonly attribute have a DOMJIT patchpoint compiler".
We annotate several accessors at first. "firstChild", "lastChild", "nextSibling", "previousSibling",
and "parentNode". And we implement DOMJIT::Patchpoint for that in JSNodeDOMJIT.cpp.

This patchpoint will be integrated into JSC's DFG and FTL. And these tiers can drop type checks and
inline the entire code of these accessors. JSC compiler still does not know much about DOM. And WebCore
does not know much about each tier of JSC. WebCore just offers the generic patchpoints and they are
used in both DFG and FTL tiers. The layer separation is still kept.

While very small microbenchmark[1] shows performance benefit, still we cannot improve DOM
benchmarks due to the lack of following implementations. Once the following implementations
are implemented, we will get performance boost.

1. Super polymorphic sites.

    This inlining is super effective if we run some microbenchmarks. However, Dromaeo does not
    show so much performance benefit. This is because Dromaeo's dom-traverse.html is super
    polymorphic call site where JSC gives up optimization. For example, in the following
    dromaeo's benchmark, we can see so various DOM nodes at the `cur.firstChild` site, like,
    HTMLDivElement, HTMLAnchorElement, Text, Comment etc. JSC gives up optimization since we
    encounter so many Structures. This should be optimized since they share the large part of
    prototype-chain and they hit the exactly same CustomGetter, Node.prototype.firstChild.
    We will handle this and when we optimize it, this DOMJIT works well on Dromaeo.

        test( "firstChild", function(){
            var nodes = document.body.childNodes, nl = nodes.length;

            for ( var i = 0; i < num; i++ ) {
                for ( var j = 0; j < nl; j++ ) {
                    var cur = nodes[j];
                    while ( cur )
                        cur = cur.firstChild;
                    ret = cur;
                }
            }
        });

2. Emit code in IC.

    Currently, we only optimize DOMJIT accessors in DFG and FTL. However, we should leverage
    this DOMJIT::Patchpoint to emit inlined code even in Inline Caching (IC). We will emit
    CheckDOM's code for IC's guard phase, and emit CallDOM's code for IC's get phase. This
    offers performance benefit even if we live in baseline JIT code. And this should be easy.

[1]: With the following one, we can see 3x improvement (26ms v.s. 80ms).

    var element = document.getElementsByTagName('div')[3];
    var before = Date.now();
    for (var i = 0; i < 1e7; ++i)
        element.firstChild;
    console.log(Date.now() - before);

* CMakeLists.txt:
* ForwardingHeaders/domjit/DOMJITGetterSetter.h:
* ForwardingHeaders/domjit/DOMJITPatchpoint.h: Copied from Source/JavaScriptCore/domjit/DOMJITSlowPathCalls.h.
* ForwardingHeaders/domjit/DOMJITPatchpointParams.h: Copied from Source/JavaScriptCore/domjit/DOMJITSlowPathCalls.h.
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/JSDOMGlobalObject.h:
* bindings/js/JSDOMWrapper.h:
(WebCore::JSDOMWrapper::offsetOfWrapped):
(WebCore::JSDOMWrapper::wrapped): Deleted.
* bindings/js/ScriptWrappable.h:
(WebCore::ScriptWrappable::offsetOfWrapper):
* bindings/scripts/CodeGeneratorJS.pm:
(GetJSCAttributesForAttribute):
(GenerateHeader):
(GeneratePropertiesHashTable):
(GenerateImplementation):
(GenerateHashTableValueArray):
* bindings/scripts/IDLAttributes.txt:
* dom/ContainerNode.h:
(WebCore::ContainerNode::lastChildMemoryOffset):
(WebCore::ContainerNode::lastChild): Deleted.
* dom/Node.h:
(WebCore::Node::flagIsContainer):
(WebCore::Node::flagIsText): Deleted.
* dom/Node.idl:
* domjit/DOMJITHelpers.h: Added.
(WebCore::DOMJITHelpers::branchIfNotWorldIsNormal):
(WebCore::DOMJITHelpers::branchIfNotWeakIsLive):
(WebCore::DOMJITHelpers::tryLookUpWrapperCache):
(WebCore::DOMJITHelpers::toWrapper):
(WebCore::DOMJITHelpers::branchIfDOMWrapper):
(WebCore::DOMJITHelpers::branchIfNotDOMWrapper):
(WebCore::DOMJITHelpers::branchIfNode):
(WebCore::DOMJITHelpers::branchIfNotNode):
(WebCore::DOMJITHelpers::branchIfElement):
(WebCore::DOMJITHelpers::branchIfNotElement):
(WebCore::DOMJITHelpers::branchIfDocumentWrapper):
(WebCore::DOMJITHelpers::branchIfNotDocumentWrapper):
* domjit/JSNodeDOMJIT.cpp: Added.
(WebCore::toWrapperSlow):
(WebCore::createCallDOMForOffsetAccess):
(WebCore::checkNode):
(WebCore::NodeFirstChildDOMJIT::checkDOM):
(WebCore::NodeFirstChildDOMJIT::callDOM):
(WebCore::NodeLastChildDOMJIT::checkDOM):
(WebCore::NodeLastChildDOMJIT::callDOM):
(WebCore::NodeNextSiblingDOMJIT::checkDOM):
(WebCore::NodeNextSiblingDOMJIT::callDOM):
(WebCore::NodePreviousSiblingDOMJIT::checkDOM):
(WebCore::NodePreviousSiblingDOMJIT::callDOM):
(WebCore::NodeParentNodeDOMJIT::checkDOM):
(WebCore::NodeParentNodeDOMJIT::callDOM):

Source/WTF:

Add CAST_OFFSET. It is not necessary for JSCell thingy
since we don't use virtual member functions. However, it
is not true for WebCore DOM wrapped objects.

* wtf/StdLibExtras.h:

LayoutTests:

* js/dom/domjit-accessor-monomorphic-expected.txt: Added.
* js/dom/domjit-accessor-monomorphic.html: Added.
* js/dom/domjit-accessor-polymorphic-expected.txt: Added.
* js/dom/domjit-accessor-polymorphic.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@207013 268f45cc-cd09-0410-ab3c-d52691b4dbfc

30 files changed:
LayoutTests/ChangeLog
LayoutTests/js/dom/domjit-accessor-monomorphic-expected.txt [new file with mode: 0644]
LayoutTests/js/dom/domjit-accessor-monomorphic.html [new file with mode: 0644]
LayoutTests/js/dom/domjit-accessor-polymorphic-expected.txt [new file with mode: 0644]
LayoutTests/js/dom/domjit-accessor-polymorphic.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/domjit/DOMJITSlowPathCalls.h
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/heap/WeakImpl.h
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jit/JITOperations.h
Source/WTF/ChangeLog
Source/WTF/wtf/StdLibExtras.h
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/ForwardingHeaders/domjit/DOMJITGetterSetter.h
Source/WebCore/ForwardingHeaders/domjit/DOMJITPatchpoint.h [new file with mode: 0644]
Source/WebCore/ForwardingHeaders/domjit/DOMJITPatchpointParams.h [new file with mode: 0644]
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/bindings/js/JSDOMGlobalObject.h
Source/WebCore/bindings/js/JSDOMWrapper.h
Source/WebCore/bindings/js/ScriptWrappable.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/IDLAttributes.txt
Source/WebCore/dom/ContainerNode.h
Source/WebCore/dom/Node.h
Source/WebCore/dom/Node.idl
Source/WebCore/domjit/DOMJITHelpers.h [new file with mode: 0644]
Source/WebCore/domjit/JSNodeDOMJIT.cpp [new file with mode: 0644]

index 22c976c..bb33bc5 100644 (file)
@@ -1,3 +1,15 @@
+2016-10-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Implement Node accessors in DOMJIT
+        https://bugs.webkit.org/show_bug.cgi?id=163005
+
+        Reviewed by Filip Pizlo.
+
+        * js/dom/domjit-accessor-monomorphic-expected.txt: Added.
+        * js/dom/domjit-accessor-monomorphic.html: Added.
+        * js/dom/domjit-accessor-polymorphic-expected.txt: Added.
+        * js/dom/domjit-accessor-polymorphic.html: Added.
+
 2016-10-10  Ryan Haddad  <ryanhaddad@apple.com>
 
         Marking media/controls/airplay-picker.html as flaky on ElCapitan+.
diff --git a/LayoutTests/js/dom/domjit-accessor-monomorphic-expected.txt b/LayoutTests/js/dom/domjit-accessor-monomorphic-expected.txt
new file mode 100644 (file)
index 0000000..4ee76a3
--- /dev/null
@@ -0,0 +1,13 @@
+Test DOMJIT accessors work in monomorphic call sites.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS func("firstChild", document.getElementById("target"), document.getElementById("firstChild")) is true
+PASS func("lastChild", document.getElementById("target"), document.getElementById("lastChild")) is true
+PASS func("nextSibling", document.getElementById("target"), document.getElementById("nextSibling")) is true
+PASS func("previousSibling", document.getElementById("target"), document.getElementById("previousSibling")) is true
+PASS func("parentNode", document.getElementById("target"), document.getElementById("parentNode")) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/dom/domjit-accessor-monomorphic.html b/LayoutTests/js/dom/domjit-accessor-monomorphic.html
new file mode 100644 (file)
index 0000000..21b8348
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<div id="parentNode">
+<div id="previousSibling"></div><div id="target"><div id="firstChild"></div><div id="lastChild"></div></div><div id="nextSibling"></div>
+</div>
+
+<script>
+description('Test DOMJIT accessors work in monomorphic call sites.');
+var tests = [
+    "firstChild",
+    "lastChild",
+    "nextSibling",
+    "previousSibling",
+    "parentNode",
+].map(function (name) {
+    var func = `
+        return function ${name}(name, element, result) {
+            for (var i = 0; i < 1e4; ++i) {
+                if (element.${name} !== result)
+                    return false;
+            }
+            return true;
+        };
+    `;
+    return [ name, Function(func)() ];
+});
+
+for (var [name, func] of tests) {
+    shouldBeTrue(`func("${name}", document.getElementById("target"), document.getElementById("${name}"))`);
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/dom/domjit-accessor-polymorphic-expected.txt b/LayoutTests/js/dom/domjit-accessor-polymorphic-expected.txt
new file mode 100644 (file)
index 0000000..e84dcaa
--- /dev/null
@@ -0,0 +1,8 @@
+Test DOMJIT accessors work in polymorphic call sites.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/dom/domjit-accessor-polymorphic.html b/LayoutTests/js/dom/domjit-accessor-polymorphic.html
new file mode 100644 (file)
index 0000000..bd18906
--- /dev/null
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+
+<div id="parentNode0">
+<div id="previousSibling0"></div><div id="target0"><div id="firstChild0"></div><div id="lastChild0"></div></div><div id="nextSibling0"></div>
+</div>
+
+<section id="parentNode1">
+<section id="previousSibling1"></section><section id="target1"><section id="firstChild1"></section><section id="lastChild1"></section></section><section id="nextSibling1"></section>
+</section>
+
+<span id="parentNode2">
+<span id="previousSibling2"></span><span id="target2"><span id="firstChild2"></span><span id="lastChild2"></span></span><span id="nextSibling2"></span>
+</span>
+
+<script>
+description('Test DOMJIT accessors work in polymorphic call sites.');
+var tests = [
+    "firstChild",
+    "lastChild",
+    "nextSibling",
+    "previousSibling",
+    "parentNode",
+].map(function (name) {
+    var func = `
+        return function ${name}(name, element, result) {
+            if (element.${name} !== result)
+                return false;
+            return true;
+        };
+    `;
+    return [ name, Function(func)() ];
+});
+
+for (var [name, func] of tests) {
+    for (var i = 0; i < 1e4; ++i) {
+        for (var j = 0; j < 3; ++j) {
+            shouldBeTrueQuiet(`func("${name}", document.getElementById("target${j}"), document.getElementById("${name}${j}"))`);
+        }
+    }
+}
+
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index c00aad9..85a0083 100644 (file)
@@ -1,3 +1,32 @@
+2016-10-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Implement Node accessors in DOMJIT
+        https://bugs.webkit.org/show_bug.cgi?id=163005
+
+        Reviewed by Filip Pizlo.
+
+        Add some helper methods and offsetOfXXX for JSC::Weak since it is used
+        for DOM wrapper caching.
+
+        And make DOMJIT::Patchpoint in FTL closer to one in DFG. We add resultConstraint
+        to avoid the situation that the same register is allocated to child and result.
+
+        We also extend DOMJIT::Patchpoint to tell useTagTypeNumberRegister / useTagMaskRegister.
+
+        * dfg/DFGSpeculativeJIT.h:
+        (JSC::DFG::SpeculativeJIT::callOperation):
+        * domjit/DOMJITSlowPathCalls.h:
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileCheckDOM):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallDOM):
+        * heap/WeakImpl.h:
+        (JSC::WeakImpl::offsetOfJSValue):
+        (JSC::WeakImpl::offsetOfWeakHandleOwner):
+        * jit/AssemblyHelpers.h:
+        (JSC::AssemblyHelpers::boxCell):
+        (JSC::AssemblyHelpers::boxInt32): Deleted.
+        * jit/JITOperations.h:
+
 2016-10-09  Filip Pizlo  <fpizlo@apple.com>
 
         Air should expose API for pinning registers
index 4e61273..638787f 100644 (file)
@@ -1639,6 +1639,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, arg2);
         return appendCallSetResult(operation, result);
     }
+    JITCompiler::Call callOperation(J_JITOperation_EGP operation, GPRReg result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallSetResult(operation, result);
+    }
     JITCompiler::Call callOperation(J_JITOperation_EJJ operation, GPRReg result, GPRReg arg1, GPRReg arg2)
     {
         m_jit.setupArgumentsWithExecState(arg1, arg2);
@@ -1894,6 +1899,11 @@ public:
         m_jit.setupArgumentsWithExecState(arg1, TrustedImmPtr(pointer));
         return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
     }
+    JITCompiler::Call callOperation(J_JITOperation_EGP operation, JSValueRegs result, GPRReg arg1, GPRReg arg2)
+    {
+        m_jit.setupArgumentsWithExecState(arg1, arg2);
+        return appendCallSetResult(operation, result.payloadGPR(), result.tagGPR());
+    }
     JITCompiler::Call callOperation(J_JITOperation_EP operation, JSValueRegs result, GPRReg arg1)
     {
         m_jit.setupArgumentsWithExecState(arg1);
index 81b9057..cb22b1f 100644 (file)
@@ -30,5 +30,6 @@
 // macro(OperationType, ArgType1, ArgType2, ...)
 #define DOMJIT_SLOW_PATH_CALLS(macro) \
     macro(J_JITOperation_EP, JSValueRegs, GPRReg) \
+    macro(J_JITOperation_EGP, JSValueRegs, GPRReg, GPRReg) \
 
 #endif
index 1e5a9b2..a38629e 100644 (file)
@@ -8731,6 +8731,8 @@ private:
         patchpoint->appendSomeRegister(cell);
         patchpoint->numGPScratchRegisters = domJIT->numGPScratchRegisters;
         patchpoint->numFPScratchRegisters = domJIT->numFPScratchRegisters;
+        patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
+        patchpoint->append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
 
         State* state = &m_ftlState;
         Node* node = m_node;
@@ -8774,17 +8776,20 @@ private:
         PatchpointValue* patchpoint = m_out.patchpoint(Int64);
         patchpoint->appendSomeRegister(globalObject);
         patchpoint->appendSomeRegister(cell);
-        patchpoint->append(m_tagMask, ValueRep::lateReg(GPRInfo::tagMaskRegister));
-        patchpoint->append(m_tagTypeNumber, ValueRep::lateReg(GPRInfo::tagTypeNumberRegister));
+        patchpoint->append(m_tagMask, ValueRep::reg(GPRInfo::tagMaskRegister));
+        patchpoint->append(m_tagTypeNumber, ValueRep::reg(GPRInfo::tagTypeNumberRegister));
         RefPtr<PatchpointExceptionHandle> exceptionHandle = preparePatchpointForExceptions(patchpoint);
         patchpoint->clobber(RegisterSet::macroScratchRegisters());
         patchpoint->numGPScratchRegisters = domJIT->numGPScratchRegisters;
         patchpoint->numFPScratchRegisters = domJIT->numFPScratchRegisters;
+        patchpoint->resultConstraint = ValueRep::SomeEarlyRegister;
 
         State* state = &m_ftlState;
         Node* node = m_node;
         patchpoint->setGenerator(
             [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                AllowMacroScratchRegisterUsage allowScratch(jit);
+
                 Vector<GPRReg> gpScratch;
                 Vector<FPRReg> fpScratch;
                 Vector<DOMJIT::Reg> regs;
@@ -8794,8 +8799,6 @@ private:
                 regs.append(JSValueRegs(params[0].gpr()));
                 regs.append(params[1].gpr());
                 regs.append(params[2].gpr());
-                regs.append(params[3].gpr());
-                regs.append(params[4].gpr());
 
                 for (unsigned i = 0; i < domJIT->numGPScratchRegisters; ++i)
                     gpScratch.append(params.gpScratch(i));
index d817b72..3f6ffbf 100644 (file)
@@ -51,7 +51,9 @@ public:
     void setState(State);
 
     const JSValue& jsValue();
+    static ptrdiff_t offsetOfJSValue() { return OBJECT_OFFSETOF(WeakImpl, m_jsValue); }
     WeakHandleOwner* weakHandleOwner();
+    static ptrdiff_t offsetOfWeakHandleOwner() { return OBJECT_OFFSETOF(WeakImpl, m_weakHandleOwner); }
     void* context();
 
     static WeakImpl* asWeakImpl(JSValue*);
index c6d8bc3..a99f98f 100644 (file)
@@ -1204,6 +1204,16 @@ public:
         move(TrustedImm32(JSValue::Int32Tag), boxedRegs.tagGPR());
 #endif
     }
+
+    void boxCell(GPRReg cellGPR, JSValueRegs boxedRegs)
+    {
+#if USE(JSVALUE64)
+        move(cellGPR, boxedRegs.gpr());
+#else
+        move(cellGPR, boxedRegs.payloadGPR());
+        move(TrustedImm32(JSValue::CellTag), boxedRegs.tagGPR());
+#endif
+    }
     
     void callExceptionFuzz();
     
index 5810e2d..338a40a 100644 (file)
@@ -155,6 +155,7 @@ typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJssZ)(ExecState*, JSStrin
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJssReo)(ExecState*, JSString*, RegExpObject*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJssReoJss)(ExecState*, JSString*, RegExpObject*, JSString*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EJP)(ExecState*, EncodedJSValue, void*);
+typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EGP)(ExecState*, JSGlobalObject*, void*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EP)(ExecState*, void*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EPP)(ExecState*, void*, void*);
 typedef EncodedJSValue (JIT_OPERATION *J_JITOperation_EPS)(ExecState*, void*, size_t);
index 27faed5..860bf48 100644 (file)
@@ -1,3 +1,16 @@
+2016-10-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Implement Node accessors in DOMJIT
+        https://bugs.webkit.org/show_bug.cgi?id=163005
+
+        Reviewed by Filip Pizlo.
+
+        Add CAST_OFFSET. It is not necessary for JSCell thingy
+        since we don't use virtual member functions. However, it
+        is not true for WebCore DOM wrapped objects.
+
+        * wtf/StdLibExtras.h:
+
 2016-10-07  Chris Dumez  <cdumez@apple.com>
 
         window.navigator.language incorrectly returns all lowercase string
index ba049fa..5f388eb 100644 (file)
@@ -68,6 +68,8 @@
 // NULL can cause compiler problems, especially in cases of multiple inheritance.
 #define OBJECT_OFFSETOF(class, field) (reinterpret_cast<ptrdiff_t>(&(reinterpret_cast<class*>(0x4000)->field)) - 0x4000)
 
+#define CAST_OFFSET(from, to) (reinterpret_cast<uintptr_t>(static_cast<to>((reinterpret_cast<from>(0x4000)))) - 0x4000)
+
 // STRINGIZE: Can convert any value to quoted string, even expandable macros
 #define STRINGIZE(exp) #exp
 #define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp)
index d523b74..d309904 100644 (file)
@@ -49,6 +49,7 @@ set(WebCore_INCLUDE_DIRECTORIES
     "${WEBCORE_DIR}/cssjit"
     "${WEBCORE_DIR}/dom"
     "${WEBCORE_DIR}/dom/default"
+    "${WEBCORE_DIR}/domjit"
     "${WEBCORE_DIR}/editing"
     "${WEBCORE_DIR}/fileapi"
     "${WEBCORE_DIR}/history"
@@ -1529,6 +1530,8 @@ set(WebCore_SOURCES
 
     dom/default/PlatformMessagePortChannel.cpp
 
+    domjit/JSNodeDOMJIT.cpp
+
     editing/AlternativeTextController.cpp
     editing/AppendNodeCommand.cpp
     editing/ApplyBlockElementCommand.cpp
index 6f595a6..5420fd3 100644 (file)
@@ -1,3 +1,132 @@
+2016-10-10  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Implement Node accessors in DOMJIT
+        https://bugs.webkit.org/show_bug.cgi?id=163005
+
+        Reviewed by Filip Pizlo.
+
+        This patch implements DOMJIT accessors in WebCore. We plan to offer 2 things in 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. 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.
+
+        This patch implements the first one, hand written DOM inlining facility. We add a new IDL attribute,
+        "DOMJIT". This means that "This readonly attribute have a DOMJIT patchpoint compiler".
+        We annotate several accessors at first. "firstChild", "lastChild", "nextSibling", "previousSibling",
+        and "parentNode". And we implement DOMJIT::Patchpoint for that in JSNodeDOMJIT.cpp.
+
+        This patchpoint will be integrated into JSC's DFG and FTL. And these tiers can drop type checks and
+        inline the entire code of these accessors. JSC compiler still does not know much about DOM. And WebCore
+        does not know much about each tier of JSC. WebCore just offers the generic patchpoints and they are
+        used in both DFG and FTL tiers. The layer separation is still kept.
+
+        While very small microbenchmark[1] shows performance benefit, still we cannot improve DOM
+        benchmarks due to the lack of following implementations. Once the following implementations
+        are implemented, we will get performance boost.
+
+        1. Super polymorphic sites.
+
+            This inlining is super effective if we run some microbenchmarks. However, Dromaeo does not
+            show so much performance benefit. This is because Dromaeo's dom-traverse.html is super
+            polymorphic call site where JSC gives up optimization. For example, in the following
+            dromaeo's benchmark, we can see so various DOM nodes at the `cur.firstChild` site, like,
+            HTMLDivElement, HTMLAnchorElement, Text, Comment etc. JSC gives up optimization since we
+            encounter so many Structures. This should be optimized since they share the large part of
+            prototype-chain and they hit the exactly same CustomGetter, Node.prototype.firstChild.
+            We will handle this and when we optimize it, this DOMJIT works well on Dromaeo.
+
+                test( "firstChild", function(){
+                    var nodes = document.body.childNodes, nl = nodes.length;
+
+                    for ( var i = 0; i < num; i++ ) {
+                        for ( var j = 0; j < nl; j++ ) {
+                            var cur = nodes[j];
+                            while ( cur )
+                                cur = cur.firstChild;
+                            ret = cur;
+                        }
+                    }
+                });
+
+        2. Emit code in IC.
+
+            Currently, we only optimize DOMJIT accessors in DFG and FTL. However, we should leverage
+            this DOMJIT::Patchpoint to emit inlined code even in Inline Caching (IC). We will emit
+            CheckDOM's code for IC's guard phase, and emit CallDOM's code for IC's get phase. This
+            offers performance benefit even if we live in baseline JIT code. And this should be easy.
+
+        [1]: With the following one, we can see 3x improvement (26ms v.s. 80ms).
+
+            var element = document.getElementsByTagName('div')[3];
+            var before = Date.now();
+            for (var i = 0; i < 1e7; ++i)
+                element.firstChild;
+            console.log(Date.now() - before);
+
+        * CMakeLists.txt:
+        * ForwardingHeaders/domjit/DOMJITGetterSetter.h:
+        * ForwardingHeaders/domjit/DOMJITPatchpoint.h: Copied from Source/JavaScriptCore/domjit/DOMJITSlowPathCalls.h.
+        * ForwardingHeaders/domjit/DOMJITPatchpointParams.h: Copied from Source/JavaScriptCore/domjit/DOMJITSlowPathCalls.h.
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/JSDOMGlobalObject.h:
+        * bindings/js/JSDOMWrapper.h:
+        (WebCore::JSDOMWrapper::offsetOfWrapped):
+        (WebCore::JSDOMWrapper::wrapped): Deleted.
+        * bindings/js/ScriptWrappable.h:
+        (WebCore::ScriptWrappable::offsetOfWrapper):
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GetJSCAttributesForAttribute):
+        (GenerateHeader):
+        (GeneratePropertiesHashTable):
+        (GenerateImplementation):
+        (GenerateHashTableValueArray):
+        * bindings/scripts/IDLAttributes.txt:
+        * dom/ContainerNode.h:
+        (WebCore::ContainerNode::lastChildMemoryOffset):
+        (WebCore::ContainerNode::lastChild): Deleted.
+        * dom/Node.h:
+        (WebCore::Node::flagIsContainer):
+        (WebCore::Node::flagIsText): Deleted.
+        * dom/Node.idl:
+        * domjit/DOMJITHelpers.h: Added.
+        (WebCore::DOMJITHelpers::branchIfNotWorldIsNormal):
+        (WebCore::DOMJITHelpers::branchIfNotWeakIsLive):
+        (WebCore::DOMJITHelpers::tryLookUpWrapperCache):
+        (WebCore::DOMJITHelpers::toWrapper):
+        (WebCore::DOMJITHelpers::branchIfDOMWrapper):
+        (WebCore::DOMJITHelpers::branchIfNotDOMWrapper):
+        (WebCore::DOMJITHelpers::branchIfNode):
+        (WebCore::DOMJITHelpers::branchIfNotNode):
+        (WebCore::DOMJITHelpers::branchIfElement):
+        (WebCore::DOMJITHelpers::branchIfNotElement):
+        (WebCore::DOMJITHelpers::branchIfDocumentWrapper):
+        (WebCore::DOMJITHelpers::branchIfNotDocumentWrapper):
+        * domjit/JSNodeDOMJIT.cpp: Added.
+        (WebCore::toWrapperSlow):
+        (WebCore::createCallDOMForOffsetAccess):
+        (WebCore::checkNode):
+        (WebCore::NodeFirstChildDOMJIT::checkDOM):
+        (WebCore::NodeFirstChildDOMJIT::callDOM):
+        (WebCore::NodeLastChildDOMJIT::checkDOM):
+        (WebCore::NodeLastChildDOMJIT::callDOM):
+        (WebCore::NodeNextSiblingDOMJIT::checkDOM):
+        (WebCore::NodeNextSiblingDOMJIT::callDOM):
+        (WebCore::NodePreviousSiblingDOMJIT::checkDOM):
+        (WebCore::NodePreviousSiblingDOMJIT::callDOM):
+        (WebCore::NodeParentNodeDOMJIT::checkDOM):
+        (WebCore::NodeParentNodeDOMJIT::callDOM):
+
 2016-10-10  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Support InputEvent.data for the new InputEvent spec
index 470441c..c427544 100644 (file)
@@ -1,3 +1,28 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
 #ifndef WebCore_FWD_DOMJITGetterSetter_h
 #define WebCore_FWD_DOMJITGetterSetter_h
 #include <JavaScriptCore/DOMJITGetterSetter.h>
diff --git a/Source/WebCore/ForwardingHeaders/domjit/DOMJITPatchpoint.h b/Source/WebCore/ForwardingHeaders/domjit/DOMJITPatchpoint.h
new file mode 100644 (file)
index 0000000..c28d1dd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebCore_FWD_DOMJITPatchpoint_h
+#define WebCore_FWD_DOMJITPatchpoint_h
+#include <JavaScriptCore/DOMJITPatchpoint.h>
+#endif
diff --git a/Source/WebCore/ForwardingHeaders/domjit/DOMJITPatchpointParams.h b/Source/WebCore/ForwardingHeaders/domjit/DOMJITPatchpointParams.h
new file mode 100644 (file)
index 0000000..f24d4c3
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WebCore_FWD_DOMJITPatchpointParams_h
+#define WebCore_FWD_DOMJITPatchpointParams_h
+#include <JavaScriptCore/DOMJITPatchpointParams.h>
+#endif
index aa70d0f..23b88ea 100644 (file)
                E1FF57A60F01256B00891EBB /* ThreadGlobalData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1FF57A50F01256B00891EBB /* ThreadGlobalData.cpp */; };
                E1FF8F6C180DB5BE00132674 /* CryptoAlgorithmRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1FF8F6A180DB5BE00132674 /* CryptoAlgorithmRegistry.cpp */; };
                E1FF8F6D180DB5BE00132674 /* CryptoAlgorithmRegistry.h in Headers */ = {isa = PBXBuildFile; fileRef = E1FF8F6B180DB5BE00132674 /* CryptoAlgorithmRegistry.h */; };
+               E3150EA61DA7219000194012 /* JSNodeDOMJIT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3AFA9641DA6E908002861BD /* JSNodeDOMJIT.cpp */; };
+               E3150EA71DA7219300194012 /* DOMJITHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = E3150EA51DA7218D00194012 /* DOMJITHelpers.h */; };
                E38838981BAD145F00D62EE3 /* ScriptModuleLoader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E38838941BAD145F00D62EE3 /* ScriptModuleLoader.cpp */; };
                E38838991BAD145F00D62EE3 /* ScriptModuleLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = E38838951BAD145F00D62EE3 /* ScriptModuleLoader.h */; };
                E3B2F0EB1D7F4C9D00B0C9D1 /* LoadableClassicScript.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3B2F0E31D7F35EC00B0C9D1 /* LoadableClassicScript.cpp */; };
                E1FF8F661807460800132674 /* JSWebKitSubtleCryptoCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSWebKitSubtleCryptoCustom.cpp; sourceTree = "<group>"; };
                E1FF8F6A180DB5BE00132674 /* CryptoAlgorithmRegistry.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CryptoAlgorithmRegistry.cpp; sourceTree = "<group>"; };
                E1FF8F6B180DB5BE00132674 /* CryptoAlgorithmRegistry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoAlgorithmRegistry.h; sourceTree = "<group>"; };
+               E3150EA51DA7218D00194012 /* DOMJITHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITHelpers.h; sourceTree = "<group>"; };
                E38838941BAD145F00D62EE3 /* ScriptModuleLoader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptModuleLoader.cpp; sourceTree = "<group>"; };
                E38838951BAD145F00D62EE3 /* ScriptModuleLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptModuleLoader.h; sourceTree = "<group>"; };
+               E3AFA9641DA6E908002861BD /* JSNodeDOMJIT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSNodeDOMJIT.cpp; sourceTree = "<group>"; };
                E3B2F0E31D7F35EC00B0C9D1 /* LoadableClassicScript.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LoadableClassicScript.cpp; sourceTree = "<group>"; };
                E3B2F0E41D7F35EC00B0C9D1 /* LoadableClassicScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadableClassicScript.h; sourceTree = "<group>"; };
                E3B2F0E71D7F35EC00B0C9D1 /* LoadableScript.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadableScript.h; sourceTree = "<group>"; };
                                F523D18402DE42E8018635CA /* css */,
                                26B9998D1803ADFA00D01121 /* cssjit */,
                                F523D32402DE4478018635CA /* dom */,
+                               E3AFA9631DA6E8AF002861BD /* domjit */,
                                93309D86099E64910056E581 /* editing */,
                                976D6C57122B8A18001FD1F7 /* fileapi */,
                                51741D080B07257000ED442C /* history */,
                        name = Crypto;
                        sourceTree = "<group>";
                };
+               E3AFA9631DA6E8AF002861BD /* domjit */ = {
+                       isa = PBXGroup;
+                       children = (
+                               E3150EA51DA7218D00194012 /* DOMJITHelpers.h */,
+                               E3AFA9641DA6E908002861BD /* JSNodeDOMJIT.cpp */,
+                       );
+                       path = domjit;
+                       sourceTree = "<group>";
+               };
                E46E97860DAAD61B0071E894 /* animation */ = {
                        isa = PBXGroup;
                        children = (
                                A871DC240A15205700B12A68 /* HTMLMetaElement.h in Headers */,
                                A454424B119B3661009BE912 /* HTMLMeterElement.h in Headers */,
                                A8CFF7A70A156978000A4234 /* HTMLModElement.h in Headers */,
+                               E3150EA71DA7219300194012 /* DOMJITHelpers.h in Headers */,
                                A8DF3FD4097FA0FC0052981B /* HTMLNameCollection.h in Headers */,
                                A871D45A0A127CBC00B12A68 /* HTMLObjectElement.h in Headers */,
                                A8EA79F10A1916DF00A8EF5F /* HTMLOListElement.h in Headers */,
                                BCC065870F3CE2A700CD2D87 /* JSClientRect.cpp in Sources */,
                                46A58AC51D46B3FA00432036 /* JSClientRectCustom.cpp in Sources */,
                                BCC065890F3CE2A700CD2D87 /* JSClientRectList.cpp in Sources */,
+                               E3150EA61DA7219000194012 /* JSNodeDOMJIT.cpp in Sources */,
                                51FB5505113E3E9100821176 /* JSCloseEvent.cpp in Sources */,
                                A584FE3B1864E2D800843B10 /* JSCommandLineAPIHost.cpp in Sources */,
                                A584FE381864DAC100843B10 /* JSCommandLineAPIHostCustom.cpp in Sources */,
index d40c43f..a01bf56 100644 (file)
@@ -72,6 +72,7 @@ namespace WebCore {
 
         DOMWrapperWorld& world() { return m_world.get(); }
         bool worldIsNormal() const { return m_worldIsNormal; }
+        static ptrdiff_t offsetOfWorldIsNormal() { return OBJECT_OFFSETOF(JSDOMGlobalObject, m_worldIsNormal); }
 
         JSBuiltinInternalFunctions& builtinInternalFunctions() { return m_builtinInternalFunctions; }
 
@@ -95,7 +96,7 @@ namespace WebCore {
 
         Event* m_currentEvent;
         Ref<DOMWrapperWorld> m_world;
-        bool m_worldIsNormal;
+        uint8_t m_worldIsNormal;
 
     private:
         void addBuiltinGlobals(JSC::VM&);
index 62510cd..009f6df 100644 (file)
@@ -59,6 +59,7 @@ public:
     static constexpr bool isDOMWrapper = true;
 
     ImplementationClass& wrapped() const { return const_cast<ImplementationClass&>(m_wrapped.get()); }
+    static ptrdiff_t offsetOfWrapped() { return OBJECT_OFFSETOF(JSDOMWrapper<ImplementationClass>, m_wrapped); }
 
 protected:
     JSDOMWrapper(JSC::Structure* structure, JSC::JSGlobalObject& globalObject, Ref<ImplementationClass>&& impl)
index 5d93261..5375f66 100644 (file)
@@ -48,6 +48,9 @@ public:
     void setWrapper(JSDOMObject*, JSC::WeakHandleOwner*, void*);
     void clearWrapper(JSDOMObject*);
 
+    template<typename Derived>
+    static ptrdiff_t offsetOfWrapper() { return CAST_OFFSET(Derived*, ScriptWrappable*) + OBJECT_OFFSETOF(ScriptWrappable, m_wrapper); }
+
 protected:
     ~ScriptWrappable() { }
 
index f64d930..943b966 100644 (file)
@@ -641,6 +641,7 @@ sub GetJSCAttributesForAttribute
     push(@specials, "DontEnum") if ($attribute->signature->extendedAttributes->{NotEnumerable} || $is_global_constructor);
     push(@specials, "ReadOnly") if IsReadonly($attribute);
     push(@specials, "CustomAccessor") unless $is_global_constructor or IsJSBuiltin($interface, $attribute);
+    push(@specials, "DOMJITAttribute") if $attribute->signature->extendedAttributes->{"DOMJIT"};
     push(@specials, "Accessor | Builtin") if  IsJSBuiltin($interface, $attribute);
     return (@specials > 0) ? join(" | ", @specials) : "0";
 }
@@ -1418,6 +1419,8 @@ sub GenerateHeader
     my $hasForwardDeclaringFunctions = 0;
     my $hasForwardDeclaringAttributes = 0;
 
+    my $hasDOMJITAttributes = 0;
+
     # Attribute and function enums
     if ($numAttributes > 0) {
         push(@headerContent, "    static ${className}* castForAttribute(JSC::ExecState*, JSC::EncodedJSValue);\n");
@@ -1434,6 +1437,7 @@ sub GenerateHeader
                 $needsVisitChildren = 1;
                 push(@headerContent, "#endif\n") if $conditionalString;
             }
+            $hasDOMJITAttributes = 1 if $attribute->signature->extendedAttributes->{"DOMJIT"};
 
             $hasForwardDeclaringAttributes = 1 if $attribute->signature->extendedAttributes->{ForwardDeclareInHeader};
         }
@@ -1641,6 +1645,29 @@ sub GenerateHeader
         }
     }
 
+    if ($hasDOMJITAttributes) {
+        $headerIncludes{"<domjit/DOMJITGetterSetter.h>"} = 1;
+        push(@headerContent,"// DOMJIT emitters for attributes\n\n");
+        foreach my $attribute (@{$interface->attributes}) {
+            next unless $attribute->signature->extendedAttributes->{"DOMJIT"};
+
+            my $interfaceName = $interface->name;
+            my $className = $interfaceName . $codeGenerator->WK_ucfirst($attribute->signature->name);
+            my $domJITClassName = $className . "DOMJIT";
+
+            push(@headerContent, "JSC::DOMJIT::GetterSetter* domJITGetterSetterFor$className(void);\n");
+
+            push(@headerContent, "class $domJITClassName : public JSC::DOMJIT::GetterSetter {\n");
+            push(@headerContent, "public:\n");
+            push(@headerContent, "    $domJITClassName();\n");
+            push(@headerContent, "#if ENABLE(JIT)\n");
+            push(@headerContent, "    Ref<JSC::DOMJIT::Patchpoint> checkDOM() override;\n");
+            push(@headerContent, "    Ref<JSC::DOMJIT::Patchpoint> callDOM() override;\n");
+            push(@headerContent, "#endif\n");
+            push(@headerContent, "};\n\n");
+        }
+    }
+
     if (HasCustomConstructor($interface)) {
         push(@headerContent, "// Custom constructor\n");
         push(@headerContent, "JSC::EncodedJSValue JSC_HOST_CALL construct${className}(JSC::ExecState&);\n\n");
@@ -1720,14 +1747,19 @@ sub GeneratePropertiesHashTable
         my $special = GetJSCAttributesForAttribute($interface, $attribute);
         push(@$hashSpecials, $special);
 
-        my $getter = GetAttributeGetterName($interface, $className, $attribute);
-        push(@$hashValue1, $getter);
-
-        if (IsReadonly($attribute)) {
+        if ($attribute->signature->extendedAttributes->{"DOMJIT"}) {
+            push(@$hashValue1, "domJITGetterSetterFor" . $interface->name . $codeGenerator->WK_ucfirst($attribute->signature->name));
             push(@$hashValue2, "0");
         } else {
-            my $setter = GetAttributeSetterName($interface, $className, $attribute);
-            push(@$hashValue2, $setter);
+            my $getter = GetAttributeGetterName($interface, $className, $attribute);
+            push(@$hashValue1, $getter);
+
+            if (IsReadonly($attribute)) {
+                push(@$hashValue2, "0");
+            } else {
+                my $setter = GetAttributeSetterName($interface, $className, $attribute);
+                push(@$hashValue2, $setter);
+            }
         }
 
         my $conditional = $attribute->signature->extendedAttributes->{Conditional};
@@ -2506,17 +2538,23 @@ sub GenerateImplementation
             my @specials = ();
             push(@specials, "DontDelete") unless $attribute->signature->extendedAttributes->{Deletable};
             push(@specials, "ReadOnly") if IsReadonly($attribute);
+            push(@specials, "DOMJITAttribute") if $attribute->signature->extendedAttributes->{"DOMJIT"};
             my $special = (@specials > 0) ? join(" | ", @specials) : "0";
             push(@hashSpecials, $special);
 
-            my $getter = GetAttributeGetterName($interface, $className, $attribute);
-            push(@hashValue1, $getter);
-
-            if (IsReadonly($attribute)) {
+            if ($attribute->signature->extendedAttributes->{"DOMJIT"}) {
+                push(@hashValue1, "domJITGetterSetterFor" . $interface->name . $codeGenerator->WK_ucfirst($attribute->signature->name));
                 push(@hashValue2, "0");
             } else {
-                my $setter = GetAttributeSetterName($interface, $className, $attribute);
-                push(@hashValue2, $setter);
+                my $getter = GetAttributeGetterName($interface, $className, $attribute);
+                push(@hashValue1, $getter);
+
+                if (IsReadonly($attribute)) {
+                    push(@hashValue2, "0");
+                } else {
+                    my $setter = GetAttributeSetterName($interface, $className, $attribute);
+                    push(@hashValue2, $setter);
+                }
             }
 
             my $conditional = $attribute->signature->extendedAttributes->{Conditional};
@@ -3094,6 +3132,26 @@ sub GenerateImplementation
 
             push(@implContent, "}\n\n");
 
+            if ($attribute->signature->extendedAttributes->{"DOMJIT"}) {
+                $implIncludes{"<wtf/NeverDestroyed.h>"} = 1;
+                my $interfaceName = $interface->name;
+                my $attributeName = $attribute->signature->name;
+                my $generatorName = $interfaceName . $codeGenerator->WK_ucfirst($attribute->signature->name);
+                my $domJITClassName = $generatorName . "DOMJIT";
+                my $getter = GetAttributeGetterName($interface, $generatorName, $attribute);
+                my $setter = IsReadonly($attribute) ? "nullptr" : GetAttributeSetterName($interface, $generatorName, $attribute);
+                push(@implContent, "$domJITClassName::$domJITClassName()\n");
+                push(@implContent, "    : JSC::DOMJIT::GetterSetter($getter, $setter, ${className}::info())\n");
+                push(@implContent, "{\n");
+                push(@implContent, "}\n\n");
+
+                push(@implContent, "JSC::DOMJIT::GetterSetter* domJITGetterSetterFor" . $generatorName . "()\n");
+                push(@implContent, "{\n");
+                push(@implContent, "    static NeverDestroyed<$domJITClassName> compiler;\n");
+                push(@implContent, "    return &compiler.get();\n");
+                push(@implContent, "}\n\n");
+            }
+
             push(@implContent, "#endif\n\n") if $attributeConditionalString;
         }
 
@@ -5094,6 +5152,8 @@ sub GenerateHashTableValueArray
             $firstTargetType = "static_cast<BuiltinGenerator>";
         } elsif ("@$specials[$i]" =~ m/ConstantInteger/) {
             $firstTargetType = "";
+        } elsif ("@$specials[$i]" =~ m/DOMJITAttribute/) {
+            $firstTargetType = "static_cast<DOMJITGetterSetterGenerator>";
         } else {
             $firstTargetType = "static_cast<PropertySlot::GetValueFunc>";
             $secondTargetType = "static_cast<PutPropertySlot::PutValueFunc>";
index 1aad98a..f49f467 100644 (file)
@@ -55,6 +55,7 @@ CustomReturn
 CustomSetPrototype
 CustomSetter
 CustomToJSObject
+DOMJIT
 Deletable
 DoNotCheckConstants
 DoNotCheckSecurity
index ba93b7f..46c432e 100644 (file)
@@ -41,6 +41,7 @@ public:
     Node* firstChild() const { return m_firstChild; }
     static ptrdiff_t firstChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_firstChild); }
     Node* lastChild() const { return m_lastChild; }
+    static ptrdiff_t lastChildMemoryOffset() { return OBJECT_OFFSETOF(ContainerNode, m_lastChild); }
     bool hasChildNodes() const { return m_firstChild; }
     bool hasOneChild() const { return m_firstChild && !m_firstChild->nextSibling(); }
 
index e249c2b..88714e1 100644 (file)
@@ -559,10 +559,11 @@ public:
     void updateAncestorConnectedSubframeCountForRemoval() const;
     void updateAncestorConnectedSubframeCountForInsertion() const;
 
-#if ENABLE(CSS_SELECTOR_JIT)
+#if ENABLE(JIT)
     static ptrdiff_t nodeFlagsMemoryOffset() { return OBJECT_OFFSETOF(Node, m_nodeFlags); }
     static ptrdiff_t rareDataMemoryOffset() { return OBJECT_OFFSETOF(Node, m_data.m_rareData); }
     static int32_t flagIsText() { return IsTextFlag; }
+    static int32_t flagIsContainer() { return IsContainerFlag; }
     static int32_t flagIsElement() { return IsElementFlag; }
     static int32_t flagIsHTML() { return IsHTMLFlag; }
     static int32_t flagIsLink() { return IsLinkFlag; }
@@ -574,7 +575,7 @@ public:
 
     static int32_t flagAffectsNextSiblingElementStyle() { return AffectsNextSiblingElementStyle; }
     static int32_t flagStyleIsAffectedByPreviousSibling() { return StyleIsAffectedByPreviousSibling; }
-#endif // ENABLE(CSS_SELECTOR_JIT)
+#endif // ENABLE(JIT)
 
 protected:
     enum NodeFlags {
index 6b77e33..6f2742f 100644 (file)
     [SetterMayThrowLegacyException] attribute DOMString? nodeValue;
 
     readonly attribute unsigned short nodeType;
-    readonly attribute Node? parentNode;
+    [DOMJIT] readonly attribute Node? parentNode;
     readonly attribute NodeList childNodes;
-    readonly attribute Node? firstChild;
-    readonly attribute Node? lastChild;
-    readonly attribute Node? previousSibling;
-    readonly attribute Node? nextSibling;
+    [DOMJIT] readonly attribute Node? firstChild;
+    [DOMJIT] readonly attribute Node? lastChild;
+    [DOMJIT] readonly attribute Node? previousSibling;
+    [DOMJIT] readonly attribute Node? nextSibling;
     readonly attribute Document? ownerDocument;
 
     [Custom, MayThrowLegacyException] Node insertBefore([CustomReturn] Node newChild, Node? refChild);
diff --git a/Source/WebCore/domjit/DOMJITHelpers.h b/Source/WebCore/domjit/DOMJITHelpers.h
new file mode 100644 (file)
index 0000000..8991624
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#pragma once
+
+#include "JSDOMWrapper.h"
+#include <domjit/DOMJITPatchpoint.h>
+#include <domjit/DOMJITPatchpointParams.h>
+
+#if ENABLE(JIT)
+
+namespace WebCore {
+namespace DOMJITHelpers {
+
+using JSC::CCallHelpers;
+using JSC::GPRReg;
+using JSC::JSValueRegs;
+
+inline CCallHelpers::Jump branchIfNotWorldIsNormal(CCallHelpers& jit, GPRReg globalObject)
+{
+    return jit.branchTest8(CCallHelpers::Zero, CCallHelpers::Address(globalObject, JSDOMGlobalObject::offsetOfWorldIsNormal()));
+}
+
+inline CCallHelpers::Jump branchIfNotWeakIsLive(CCallHelpers& jit, GPRReg weakImpl)
+{
+    return jit.branchTestPtr(CCallHelpers::NonZero, CCallHelpers::Address(weakImpl, JSC::WeakImpl::offsetOfWeakHandleOwner()), CCallHelpers::TrustedImm32(JSC::WeakImpl::StateMask));
+}
+
+template<typename WrappedType>
+void tryLookUpWrapperCache(CCallHelpers& jit, CCallHelpers::JumpList& failureCases, GPRReg wrapped, GPRReg resultGPR)
+{
+    jit.loadPtr(CCallHelpers::Address(wrapped, ScriptWrappable::offsetOfWrapper<WrappedType>()), resultGPR);
+    failureCases.append(jit.branchTestPtr(CCallHelpers::Zero, resultGPR));
+    failureCases.append(branchIfNotWeakIsLive(jit, resultGPR));
+    jit.loadPtr(CCallHelpers::Address(resultGPR, JSC::WeakImpl::offsetOfJSValue() + JSC::JSValue::offsetOfPayload()), resultGPR);
+}
+
+template<typename WrappedType, typename ToJSFunction>
+void toWrapper(CCallHelpers& jit, const JSC::DOMJIT::PatchpointParams& params, GPRReg wrapped, GPRReg globalObject, JSValueRegs result, ToJSFunction function)
+{
+    ASSERT(wrapped != result.payloadGPR());
+    ASSERT(globalObject != result.payloadGPR());
+    GPRReg payloadGPR = result.payloadGPR();
+    CCallHelpers::JumpList slowCases;
+    slowCases.append(branchIfNotWorldIsNormal(jit, globalObject));
+    tryLookUpWrapperCache<WrappedType>(jit, slowCases, wrapped, payloadGPR);
+    jit.boxCell(payloadGPR, result);
+    params.addSlowPathCall(slowCases, jit, function, result, globalObject, wrapped);
+}
+
+inline CCallHelpers::Jump branchIfDOMWrapper(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branch8(
+        CCallHelpers::AboveOrEqual,
+        CCallHelpers::Address(target, JSC::JSCell::typeInfoTypeOffset()),
+        CCallHelpers::TrustedImm32(JSC::JSType(JSDOMWrapperType)));
+}
+
+inline CCallHelpers::Jump branchIfNotDOMWrapper(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branch8(
+        CCallHelpers::Below,
+        CCallHelpers::Address(target, JSC::JSCell::typeInfoTypeOffset()),
+        CCallHelpers::TrustedImm32(JSC::JSType(JSDOMWrapperType)));
+}
+
+inline CCallHelpers::Jump branchIfNode(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branch8(
+        CCallHelpers::AboveOrEqual,
+        CCallHelpers::Address(target, JSC::JSCell::typeInfoTypeOffset()),
+        CCallHelpers::TrustedImm32(JSC::JSType(JSNodeType)));
+}
+
+inline CCallHelpers::Jump branchIfNotNode(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branch8(
+        CCallHelpers::Below,
+        CCallHelpers::Address(target, JSC::JSCell::typeInfoTypeOffset()),
+        CCallHelpers::TrustedImm32(JSC::JSType(JSNodeType)));
+}
+
+inline CCallHelpers::Jump branchIfElement(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branch8(
+        CCallHelpers::AboveOrEqual,
+        CCallHelpers::Address(target, JSC::JSCell::typeInfoTypeOffset()),
+        CCallHelpers::TrustedImm32(JSC::JSType(JSElementType)));
+}
+
+inline CCallHelpers::Jump branchIfNotElement(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branch8(
+        CCallHelpers::Below,
+        CCallHelpers::Address(target, JSC::JSCell::typeInfoTypeOffset()),
+        CCallHelpers::TrustedImm32(JSC::JSType(JSElementType)));
+}
+
+inline CCallHelpers::Jump branchIfDocumentWrapper(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branchIfType(target, JSC::JSType(JSDocumentWrapperType));
+}
+
+inline CCallHelpers::Jump branchIfNotDocumentWrapper(CCallHelpers& jit, GPRReg target)
+{
+    return jit.branchIfNotType(target, JSC::JSType(JSDocumentWrapperType));
+}
+
+} }
+
+#endif
diff --git a/Source/WebCore/domjit/JSNodeDOMJIT.cpp b/Source/WebCore/domjit/JSNodeDOMJIT.cpp
new file mode 100644 (file)
index 0000000..f360663
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSNode.h"
+
+#if ENABLE(JIT)
+
+#include "DOMJITHelpers.h"
+#include "JSDOMWrapper.h"
+#include "Node.h"
+#include <domjit/DOMJITPatchpoint.h>
+#include <domjit/DOMJITPatchpointParams.h>
+
+using namespace JSC;
+
+namespace WebCore {
+
+enum class IsContainerGuardRequirement { Required, NotRequired };
+
+template<typename WrappedNode>
+EncodedJSValue toWrapperSlow(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, void* result)
+{
+    ASSERT(exec);
+    ASSERT(result);
+    ASSERT(globalObject);
+    return JSValue::encode(toJS(exec, static_cast<JSDOMGlobalObject*>(globalObject), *static_cast<WrappedNode*>(result)));
+}
+
+template<typename WrappedNode>
+static Ref<DOMJIT::Patchpoint> createCallDOMForOffsetAccess(ptrdiff_t offset, IsContainerGuardRequirement isContainerGuardRequirement)
+{
+    Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
+    patchpoint->numGPScratchRegisters = 1;
+    patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+        JSValueRegs result = params[0].jsValueRegs();
+        GPRReg globalObject = params[1].gpr();
+        GPRReg node = params[2].gpr();
+        GPRReg scratch = params.gpScratch(0);
+
+        CCallHelpers::JumpList nullCases;
+        // Load a wrapped object. "node" should be already type checked by CheckDOM.
+        jit.loadPtr(CCallHelpers::Address(node, JSNode::offsetOfWrapped()), scratch);
+
+        if (isContainerGuardRequirement == IsContainerGuardRequirement::Required)
+            nullCases.append(jit.branchTest32(CCallHelpers::Zero, CCallHelpers::Address(scratch, Node::nodeFlagsMemoryOffset()), CCallHelpers::TrustedImm32(Node::flagIsContainer())));
+
+        jit.loadPtr(CCallHelpers::Address(scratch, offset), scratch);
+        nullCases.append(jit.branchTestPtr(CCallHelpers::Zero, scratch));
+
+        DOMJITHelpers::toWrapper<WrappedNode>(jit, params, scratch, globalObject, result, toWrapperSlow<WrappedNode>);
+        CCallHelpers::Jump done = jit.jump();
+
+        nullCases.link(&jit);
+        jit.moveValue(jsNull(), result);
+        done.link(&jit);
+        return CCallHelpers::JumpList();
+    });
+    return patchpoint;
+}
+
+static Ref<DOMJIT::Patchpoint> checkNode()
+{
+    Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
+    patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+        CCallHelpers::JumpList failureCases;
+        failureCases.append(DOMJITHelpers::branchIfNotNode(jit, params[0].gpr()));
+        return failureCases;
+    });
+    return patchpoint;
+}
+
+// Node#firstChild.
+Ref<DOMJIT::Patchpoint> NodeFirstChildDOMJIT::checkDOM()
+{
+    return checkNode();
+}
+
+Ref<DOMJIT::Patchpoint> NodeFirstChildDOMJIT::callDOM()
+{
+    return createCallDOMForOffsetAccess<Node>(CAST_OFFSET(Node*, ContainerNode*) + ContainerNode::firstChildMemoryOffset(), IsContainerGuardRequirement::Required);
+}
+
+// Node#lastChild.
+Ref<DOMJIT::Patchpoint> NodeLastChildDOMJIT::checkDOM()
+{
+    return checkNode();
+}
+
+Ref<DOMJIT::Patchpoint> NodeLastChildDOMJIT::callDOM()
+{
+    return createCallDOMForOffsetAccess<Node>(CAST_OFFSET(Node*, ContainerNode*) + ContainerNode::lastChildMemoryOffset(), IsContainerGuardRequirement::Required);
+}
+
+// Node#nextSibling.
+Ref<DOMJIT::Patchpoint> NodeNextSiblingDOMJIT::checkDOM()
+{
+    return checkNode();
+}
+
+Ref<DOMJIT::Patchpoint> NodeNextSiblingDOMJIT::callDOM()
+{
+    return createCallDOMForOffsetAccess<Node>(Node::nextSiblingMemoryOffset(), IsContainerGuardRequirement::NotRequired);
+}
+
+// Node#previousSibling.
+Ref<DOMJIT::Patchpoint> NodePreviousSiblingDOMJIT::checkDOM()
+{
+    return checkNode();
+}
+
+Ref<DOMJIT::Patchpoint> NodePreviousSiblingDOMJIT::callDOM()
+{
+    return createCallDOMForOffsetAccess<Node>(Node::previousSiblingMemoryOffset(), IsContainerGuardRequirement::NotRequired);
+}
+
+// Node#parentNode.
+Ref<DOMJIT::Patchpoint> NodeParentNodeDOMJIT::checkDOM()
+{
+    return checkNode();
+}
+
+Ref<DOMJIT::Patchpoint> NodeParentNodeDOMJIT::callDOM()
+{
+    return createCallDOMForOffsetAccess<ContainerNode>(Node::parentNodeMemoryOffset(), IsContainerGuardRequirement::NotRequired);
+}
+
+}
+
+#endif