[DOMJIT] Use DOMJIT::Patchpoint in IC
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Oct 2016 20:43:43 +0000 (20:43 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Oct 2016 20:43:43 +0000 (20:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=163223

Reviewed by Saam Barati.

JSTests:

* stress/domjit-exception-ic.js: Added.
(shouldBe):
(access):
* stress/domjit-exception.js: Added.
(shouldBe):
(access):
* stress/domjit-getter-complex-with-incorrect-object.js: Added.
(shouldThrow):
(access):
(i.shouldThrow):
* stress/domjit-getter-complex.js: Added.
(shouldBe):
(access):
* stress/domjit-getter-try-catch-getter-as-get-by-id-register-restoration.js: Added.
(assert):
(bar):
(foo):

Source/JavaScriptCore:

This patch uses DOMJIT::Patchpoint to inline DOM accesses even in IC!
It is useful for Baseline JIT cases and GetById cases in DFG and FTL.
In AccessCase, we construct the environment that allows DOMJIT::Patchpoint
to emit code and make DOMJIT accessors inlined in IC.

To allow DOMJIT::Patchpoint to emit code, we create a mechanism to emit calls
required in DOMJIT::Patchpoint. This system is useful when we create the super-
polymorphic support[1] later. And inlining mechanism is useful even after
introducing super-polymorphic support since it can work even after we fire the
watchpoint for super-polymorphic handling.

This patch improves Dromaeo dom-traverse 8% (263.95 runs/s v.s. 244.07 runs/s).

[1]: https://bugs.webkit.org/show_bug.cgi?id=163226

* CMakeLists.txt:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/DOMJITAccessCasePatchpointParams.cpp: Added.
(JSC::SlowPathCallGeneratorWithArguments::SlowPathCallGeneratorWithArguments):
(JSC::SlowPathCallGeneratorWithArguments::generateImpl):
(JSC::DOMJITAccessCasePatchpointParams::emitSlowPathCalls):
* bytecode/DOMJITAccessCasePatchpointParams.h: Copied from Source/JavaScriptCore/ftl/FTLDOMJITPatchpointParams.h.
(JSC::DOMJITAccessCasePatchpointParams::DOMJITAccessCasePatchpointParams):
(JSC::DOMJITAccessCasePatchpointParams::SlowPathCallGenerator::~SlowPathCallGenerator):
* bytecode/PolymorphicAccess.cpp:
(JSC::AccessGenerationState::liveRegistersForCall):
(JSC::AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite):
(JSC::calleeSaveRegisters):
(JSC::AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling):
(JSC::AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException):
(JSC::AccessGenerationState::restoreLiveRegistersFromStackForCall):
(JSC::AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal):
(JSC::AccessGenerationState::originalExceptionHandler):
(JSC::AccessCase::generateImpl):
(JSC::AccessCase::emitDOMJITGetter):
(JSC::PolymorphicAccess::regenerate):
(JSC::AccessGenerationState::preserveLiveRegistersToStackForCall): Deleted.
* bytecode/PolymorphicAccess.h:
(JSC::AccessGenerationState::SpillState::isEmpty):
(JSC::AccessGenerationState::setSpillStateForJSGetterSetter):
(JSC::AccessGenerationState::spillStateForJSGetterSetter):
(JSC::AccessGenerationState::liveRegistersForCall): Deleted.
(JSC::AccessGenerationState::numberOfStackBytesUsedForRegisterPreservation): Deleted.
(JSC::AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite): Deleted.
* dfg/DFGDOMJITPatchpointParams.cpp:
* dfg/DFGDOMJITPatchpointParams.h:
* domjit/DOMJITPatchpoint.h:
* domjit/DOMJITPatchpointParams.h:
(JSC::DOMJIT::PatchpointParams::addSlowPathCall):
* ftl/FTLDOMJITPatchpointParams.cpp:
* ftl/FTLDOMJITPatchpointParams.h:
* jsc.cpp:
(WTF::DOMJITNode::checkDOMJITNode):
(WTF::DOMJITGetterComplex::DOMJITGetterComplex):
(WTF::DOMJITGetterComplex::createStructure):
(WTF::DOMJITGetterComplex::create):
(WTF::DOMJITGetterComplex::DOMJITNodeDOMJIT::DOMJITNodeDOMJIT):
(WTF::DOMJITGetterComplex::domJITNodeGetterSetter):
(WTF::DOMJITGetterComplex::finishCreation):
(WTF::DOMJITGetterComplex::functionEnableException):
(WTF::DOMJITGetterComplex::customGetter):
(GlobalObject::finishCreation):
(functionCreateDOMJITGetterComplexObject):

Source/WebCore:

Make DOMJITPatchpointParams non-const.

* domjit/DOMJITHelpers.h:
(WebCore::DOMJITHelpers::toWrapper):
* domjit/JSNodeDOMJIT.cpp:
(WebCore::createCallDOMForOffsetAccess):
(WebCore::checkNode):
(WebCore::NodeNodeTypeDOMJIT::callDOM):

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

23 files changed:
JSTests/ChangeLog
JSTests/stress/domjit-exception-ic.js [new file with mode: 0644]
JSTests/stress/domjit-exception.js [new file with mode: 0644]
JSTests/stress/domjit-getter-complex-with-incorrect-object.js [new file with mode: 0644]
JSTests/stress/domjit-getter-complex.js [new file with mode: 0644]
JSTests/stress/domjit-getter-try-catch-getter-as-get-by-id-register-restoration.js [new file with mode: 0644]
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/DOMJITAccessCasePatchpointParams.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/DOMJITAccessCasePatchpointParams.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PolymorphicAccess.cpp
Source/JavaScriptCore/bytecode/PolymorphicAccess.h
Source/JavaScriptCore/dfg/DFGDOMJITPatchpointParams.cpp
Source/JavaScriptCore/dfg/DFGDOMJITPatchpointParams.h
Source/JavaScriptCore/domjit/DOMJITPatchpoint.h
Source/JavaScriptCore/domjit/DOMJITPatchpointParams.h
Source/JavaScriptCore/ftl/FTLDOMJITPatchpointParams.cpp
Source/JavaScriptCore/ftl/FTLDOMJITPatchpointParams.h
Source/JavaScriptCore/jsc.cpp
Source/WebCore/ChangeLog
Source/WebCore/domjit/DOMJITHelpers.h
Source/WebCore/domjit/JSNodeDOMJIT.cpp

index 3b4ffce..a9a214a 100644 (file)
@@ -1,3 +1,28 @@
+2016-10-17  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Use DOMJIT::Patchpoint in IC
+        https://bugs.webkit.org/show_bug.cgi?id=163223
+
+        Reviewed by Saam Barati.
+
+        * stress/domjit-exception-ic.js: Added.
+        (shouldBe):
+        (access):
+        * stress/domjit-exception.js: Added.
+        (shouldBe):
+        (access):
+        * stress/domjit-getter-complex-with-incorrect-object.js: Added.
+        (shouldThrow):
+        (access):
+        (i.shouldThrow):
+        * stress/domjit-getter-complex.js: Added.
+        (shouldBe):
+        (access):
+        * stress/domjit-getter-try-catch-getter-as-get-by-id-register-restoration.js: Added.
+        (assert):
+        (bar):
+        (foo):
+
 2016-10-15  Saam Barati  <sbarati@apple.com>
 
         Assertion failed under operationToLowerCase with a rope with zero length
diff --git a/JSTests/stress/domjit-exception-ic.js b/JSTests/stress/domjit-exception-ic.js
new file mode 100644 (file)
index 0000000..828a225
--- /dev/null
@@ -0,0 +1,37 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+(function () {
+    let domjit = createDOMJITGetterComplexObject();
+    function access(object)
+    {
+        return object.customGetter;
+    }
+    noInline(access);
+    let normal = {
+        customGetter: 42
+    };
+    for (let i = 0; i < 1e4; ++i) {
+        shouldBe(access(domjit), 42);
+        shouldBe(access(normal), 42);
+    }
+    domjit.enableException();
+    shouldThrow(() => access(domjit), `Error: DOMJITGetterComplex slow call exception`);
+}());
diff --git a/JSTests/stress/domjit-exception.js b/JSTests/stress/domjit-exception.js
new file mode 100644 (file)
index 0000000..09acb22
--- /dev/null
@@ -0,0 +1,67 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+(function () {
+    let domjit = createDOMJITGetterComplexObject();
+    function access(object)
+    {
+        return object.customGetter;
+    }
+    noInline(access);
+    for (var i = 0; i < 1e4; ++i)
+        shouldBe(access(domjit), 42);
+    domjit.enableException();
+    shouldThrow(() => access(domjit), `Error: DOMJITGetterComplex slow call exception`);
+}());
+(function () {
+    let domjit = createDOMJITGetterComplexObject();
+    function access(object)
+    {
+        return object.customGetter;
+    }
+    noInline(access);
+    for (let i = 0; i < 1e2; ++i)
+        shouldBe(access(domjit), 42);
+    domjit.enableException();
+    shouldThrow(() => access(domjit), `Error: DOMJITGetterComplex slow call exception`);
+}());
+(function () {
+    let domjit = createDOMJITGetterComplexObject();
+    function access(object)
+    {
+        return object.customGetter;
+    }
+    noInline(access);
+    for (let i = 0; i < 50; ++i)
+        shouldBe(access(domjit), 42);
+    domjit.enableException();
+    shouldThrow(() => access(domjit), `Error: DOMJITGetterComplex slow call exception`);
+}());
+(function () {
+    let domjit = createDOMJITGetterComplexObject();
+    function access(object)
+    {
+        return object.customGetter;
+    }
+    noInline(access);
+    for (let i = 0; i < 10; ++i)
+        shouldBe(access(domjit), 42);
+    domjit.enableException();
+    shouldThrow(() => access(domjit), `Error: DOMJITGetterComplex slow call exception`);
+}());
diff --git a/JSTests/stress/domjit-getter-complex-with-incorrect-object.js b/JSTests/stress/domjit-getter-complex-with-incorrect-object.js
new file mode 100644 (file)
index 0000000..b4b80cc
--- /dev/null
@@ -0,0 +1,28 @@
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+var complex = createDOMJITGetterComplexObject();
+var object = {};
+object.__proto__ = complex;
+function access(object)
+{
+    return object.customGetter;
+}
+noInline(access);
+for (var i = 0; i < 1e4; ++i) {
+    shouldThrow(() => {
+        access(object);
+    }, `TypeError: Type error`);
+}
diff --git a/JSTests/stress/domjit-getter-complex.js b/JSTests/stress/domjit-getter-complex.js
new file mode 100644 (file)
index 0000000..195de7e
--- /dev/null
@@ -0,0 +1,14 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+var complex = createDOMJITGetterComplexObject();
+function access(complex)
+{
+    return complex.customGetter;
+}
+noInline(access);
+for (var i = 0; i < 1e4; ++i) {
+    shouldBe(access(complex), 42);
+}
diff --git a/JSTests/stress/domjit-getter-try-catch-getter-as-get-by-id-register-restoration.js b/JSTests/stress/domjit-getter-try-catch-getter-as-get-by-id-register-restoration.js
new file mode 100644 (file)
index 0000000..5ec93cc
--- /dev/null
@@ -0,0 +1,52 @@
+function assert(b) {
+    if (!b) throw new Error("bad value");
+}
+noInline(assert);
+
+let i;
+var o1 = createDOMJITGetterComplexObject();
+o1.x = "x";
+
+var o2 = {
+    customGetter: 40
+}
+
+var o3 = {
+    x: 100,
+    customGetter: "f"
+}
+
+function bar(i) {
+    if (i === -1000)
+        return o1;
+
+    if (i % 2)
+        return o3;
+    else
+        return o2;
+}
+noInline(bar);
+
+function foo(i) {
+    var o = bar(i);
+    let v;
+    let v2;
+    let v3;
+    try {
+        v2 = o.x;
+        v = o.customGetter + v2;
+    } catch(e) {
+        assert(v2 === "x");
+        assert(o === o1);
+    }
+}
+noInline(foo);
+
+foo(i);
+for (i = 0; i < 1000; i++)
+    foo(i);
+
+o1.enableException();
+i = -1000;
+for (let j = 0; j < 1000; j++)
+    foo(i);
index 0fdb8dd..4fb856d 100644 (file)
@@ -204,6 +204,7 @@ set(JavaScriptCore_SOURCES
     bytecode/ComplexGetStatus.cpp
     bytecode/DataFormat.cpp
     bytecode/DFGExitProfile.cpp
+    bytecode/DOMJITAccessCasePatchpointParams.cpp
     bytecode/DeferredCompilationCallback.cpp
     bytecode/DeferredSourceDump.cpp
     bytecode/ExecutionCounter.cpp
index bec84c4..7182d38 100644 (file)
@@ -1,3 +1,74 @@
+2016-10-17  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Use DOMJIT::Patchpoint in IC
+        https://bugs.webkit.org/show_bug.cgi?id=163223
+
+        Reviewed by Saam Barati.
+
+        This patch uses DOMJIT::Patchpoint to inline DOM accesses even in IC!
+        It is useful for Baseline JIT cases and GetById cases in DFG and FTL.
+        In AccessCase, we construct the environment that allows DOMJIT::Patchpoint
+        to emit code and make DOMJIT accessors inlined in IC.
+
+        To allow DOMJIT::Patchpoint to emit code, we create a mechanism to emit calls
+        required in DOMJIT::Patchpoint. This system is useful when we create the super-
+        polymorphic support[1] later. And inlining mechanism is useful even after
+        introducing super-polymorphic support since it can work even after we fire the
+        watchpoint for super-polymorphic handling.
+
+        This patch improves Dromaeo dom-traverse 8% (263.95 runs/s v.s. 244.07 runs/s).
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=163226
+
+        * CMakeLists.txt:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/DOMJITAccessCasePatchpointParams.cpp: Added.
+        (JSC::SlowPathCallGeneratorWithArguments::SlowPathCallGeneratorWithArguments):
+        (JSC::SlowPathCallGeneratorWithArguments::generateImpl):
+        (JSC::DOMJITAccessCasePatchpointParams::emitSlowPathCalls):
+        * bytecode/DOMJITAccessCasePatchpointParams.h: Copied from Source/JavaScriptCore/ftl/FTLDOMJITPatchpointParams.h.
+        (JSC::DOMJITAccessCasePatchpointParams::DOMJITAccessCasePatchpointParams):
+        (JSC::DOMJITAccessCasePatchpointParams::SlowPathCallGenerator::~SlowPathCallGenerator):
+        * bytecode/PolymorphicAccess.cpp:
+        (JSC::AccessGenerationState::liveRegistersForCall):
+        (JSC::AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite):
+        (JSC::calleeSaveRegisters):
+        (JSC::AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling):
+        (JSC::AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException):
+        (JSC::AccessGenerationState::restoreLiveRegistersFromStackForCall):
+        (JSC::AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal):
+        (JSC::AccessGenerationState::originalExceptionHandler):
+        (JSC::AccessCase::generateImpl):
+        (JSC::AccessCase::emitDOMJITGetter):
+        (JSC::PolymorphicAccess::regenerate):
+        (JSC::AccessGenerationState::preserveLiveRegistersToStackForCall): Deleted.
+        * bytecode/PolymorphicAccess.h:
+        (JSC::AccessGenerationState::SpillState::isEmpty):
+        (JSC::AccessGenerationState::setSpillStateForJSGetterSetter):
+        (JSC::AccessGenerationState::spillStateForJSGetterSetter):
+        (JSC::AccessGenerationState::liveRegistersForCall): Deleted.
+        (JSC::AccessGenerationState::numberOfStackBytesUsedForRegisterPreservation): Deleted.
+        (JSC::AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite): Deleted.
+        * dfg/DFGDOMJITPatchpointParams.cpp:
+        * dfg/DFGDOMJITPatchpointParams.h:
+        * domjit/DOMJITPatchpoint.h:
+        * domjit/DOMJITPatchpointParams.h:
+        (JSC::DOMJIT::PatchpointParams::addSlowPathCall):
+        * ftl/FTLDOMJITPatchpointParams.cpp:
+        * ftl/FTLDOMJITPatchpointParams.h:
+        * jsc.cpp:
+        (WTF::DOMJITNode::checkDOMJITNode):
+        (WTF::DOMJITGetterComplex::DOMJITGetterComplex):
+        (WTF::DOMJITGetterComplex::createStructure):
+        (WTF::DOMJITGetterComplex::create):
+        (WTF::DOMJITGetterComplex::DOMJITNodeDOMJIT::DOMJITNodeDOMJIT):
+        (WTF::DOMJITGetterComplex::domJITNodeGetterSetter):
+        (WTF::DOMJITGetterComplex::finishCreation):
+        (WTF::DOMJITGetterComplex::functionEnableException):
+        (WTF::DOMJITGetterComplex::customGetter):
+        (GlobalObject::finishCreation):
+        (functionCreateDOMJITGetterComplexObject):
+
 2016-10-17  Saam Barati  <sbarati@apple.com>
 
         Build fix for HasOwnPropertyCache::Entry caused slowdown by introducing a constructor that doesn't use move semantics for the RefPtr<UniquedStringImpl> parameter
index ee8c7f9..bcecac7 100644 (file)
                E39DA4A61B7E8B7C0084F33A /* JSModuleRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */; };
                E39DA4A71B7E8B7C0084F33A /* JSModuleRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3A421431D6F58930007C617 /* PreciseJumpTargetsInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E3A421421D6F588F0007C617 /* PreciseJumpTargetsInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E3BFD0BB1DAF80870065DEA2 /* DOMJITAccessCasePatchpointParams.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3BFD0B91DAF807C0065DEA2 /* DOMJITAccessCasePatchpointParams.cpp */; };
+               E3BFD0BC1DAF808E0065DEA2 /* DOMJITAccessCasePatchpointParams.h in Headers */ = {isa = PBXBuildFile; fileRef = E3BFD0BA1DAF807C0065DEA2 /* DOMJITAccessCasePatchpointParams.h */; };
                E3C08E3C1DA41B810039478F /* DOMJITPatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = E3C08E3B1DA41B7B0039478F /* DOMJITPatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3D239C81B829C1C00BBEF67 /* JSModuleEnvironment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */; };
                E3D239C91B829C1C00BBEF67 /* JSModuleEnvironment.h in Headers */ = {isa = PBXBuildFile; fileRef = E3D239C71B829C1C00BBEF67 /* JSModuleEnvironment.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleRecord.cpp; sourceTree = "<group>"; };
                E39DA4A51B7E8B7C0084F33A /* JSModuleRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSModuleRecord.h; sourceTree = "<group>"; };
                E3A421421D6F588F0007C617 /* PreciseJumpTargetsInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreciseJumpTargetsInlines.h; sourceTree = "<group>"; };
+               E3BFD0B91DAF807C0065DEA2 /* DOMJITAccessCasePatchpointParams.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DOMJITAccessCasePatchpointParams.cpp; sourceTree = "<group>"; };
+               E3BFD0BA1DAF807C0065DEA2 /* DOMJITAccessCasePatchpointParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITAccessCasePatchpointParams.h; sourceTree = "<group>"; };
                E3C08E3B1DA41B7B0039478F /* DOMJITPatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITPatchpoint.h; sourceTree = "<group>"; };
                E3CB1E241DA7540A00FA1E56 /* DOMJITSlowPathCalls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITSlowPathCalls.h; sourceTree = "<group>"; };
                E3D239C61B829C1C00BBEF67 /* JSModuleEnvironment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleEnvironment.cpp; sourceTree = "<group>"; };
                                0F0B83A514BCF50400885B4F /* CodeType.h */,
                                0F6FC74E196110A800E1D02D /* ComplexGetStatus.cpp */,
                                0F6FC74F196110A800E1D02D /* ComplexGetStatus.h */,
+                               E3BFD0B91DAF807C0065DEA2 /* DOMJITAccessCasePatchpointParams.cpp */,
+                               E3BFD0BA1DAF807C0065DEA2 /* DOMJITAccessCasePatchpointParams.h */,
                                62E3D5EF1B8D0B7300B868BB /* DataFormat.cpp */,
                                0F426A4A1460CD6B00131F8F /* DataFormat.h */,
                                0FC712DC17CD8778008CC93C /* DeferredCompilationCallback.cpp */,
                                0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */,
                                86EC9DC51328DF82002B2AD7 /* DFGByteCodeParser.h in Headers */,
                                0F256C361627B0AD007F2783 /* DFGCallArrayAllocatorSlowPathGenerator.h in Headers */,
+                               E3BFD0BC1DAF808E0065DEA2 /* DOMJITAccessCasePatchpointParams.h in Headers */,
                                0FBDB9AD1AB0FBC6000B57E5 /* DFGCallCreateDirectArgumentsSlowPathGenerator.h in Headers */,
                                0F7B294B14C3CD2F007C3DB1 /* DFGCapabilities.h in Headers */,
                                0FFFC95814EF90A200C72532 /* DFGCFAPhase.h in Headers */,
                                E35E035F1B7AB43E0073AD2A /* InspectorInstrumentationObject.cpp in Sources */,
                                A532438B18568335002ED692 /* InspectorProtocolObjects.cpp in Sources */,
                                A50E4B6118809DD50068A46D /* InspectorRuntimeAgent.cpp in Sources */,
+                               E3BFD0BB1DAF80870065DEA2 /* DOMJITAccessCasePatchpointParams.cpp in Sources */,
                                A593CF821840377100BFCE27 /* InspectorValues.cpp in Sources */,
                                147F39CF107EC37600427A48 /* InternalFunction.cpp in Sources */,
                                1429D7D40ED2128200B89619 /* Interpreter.cpp in Sources */,
diff --git a/Source/JavaScriptCore/bytecode/DOMJITAccessCasePatchpointParams.cpp b/Source/JavaScriptCore/bytecode/DOMJITAccessCasePatchpointParams.cpp
new file mode 100644 (file)
index 0000000..7a3f8ba
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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 "DOMJITAccessCasePatchpointParams.h"
+
+#include "LinkBuffer.h"
+#include "PolymorphicAccess.h"
+#include "StructureStubInfo.h"
+
+#if ENABLE(JIT)
+
+namespace JSC {
+
+template<typename JumpType, typename FunctionType, typename ResultType, typename... Arguments>
+class SlowPathCallGeneratorWithArguments : public DOMJITAccessCasePatchpointParams::SlowPathCallGenerator {
+public:
+    SlowPathCallGeneratorWithArguments(JumpType from, CCallHelpers::Label to, FunctionType function, ResultType result, std::tuple<Arguments...> arguments)
+        : m_from(from)
+        , m_to(to)
+        , m_function(function)
+        , m_result(result)
+        , m_arguments(arguments)
+    {
+    }
+
+    template<size_t... ArgumentsIndex>
+    CCallHelpers::JumpList generateImpl(VM& vm, AccessGenerationState& state, const RegisterSet& usedRegistersByPatchpoint, CCallHelpers& jit, std::index_sequence<ArgumentsIndex...>)
+    {
+        CCallHelpers::JumpList exceptions;
+        // We spill (1) the used registers by IC and (2) the used registers by DOMJIT::Patchpoint.
+        AccessGenerationState::SpillState spillState = state.preserveLiveRegistersToStackForCall(usedRegistersByPatchpoint);
+
+        jit.store32(
+            CCallHelpers::TrustedImm32(state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
+            CCallHelpers::tagFor(static_cast<VirtualRegister>(CallFrameSlot::argumentCount)));
+
+        jit.makeSpaceOnStackForCCall();
+
+        jit.storePtr(GPRInfo::callFrameRegister, &vm.topCallFrame);
+
+        // FIXME: Currently, we do not check any ARM EABI / SH4 things here.
+        // But it is OK because a compile error happens when you pass JSValueRegs as an argument.
+        // https://bugs.webkit.org/show_bug.cgi?id=163099
+        jit.setupArgumentsWithExecState(std::get<ArgumentsIndex>(m_arguments)...);
+
+        CCallHelpers::Call operationCall = jit.call();
+        auto function = m_function;
+        jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+            linkBuffer.link(operationCall, FunctionPtr(function));
+        });
+
+        jit.setupResults(m_result);
+        jit.reclaimSpaceOnStackForCCall();
+
+        CCallHelpers::Jump noException = jit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
+
+        state.restoreLiveRegistersFromStackForCallWithThrownException(spillState);
+        exceptions.append(jit.jump());
+
+        noException.link(&jit);
+        RegisterSet dontRestore;
+        dontRestore.set(m_result);
+        state.restoreLiveRegistersFromStackForCall(spillState, dontRestore);
+
+        return exceptions;
+    }
+
+    CCallHelpers::JumpList generate(VM& vm, AccessGenerationState& state, const RegisterSet& usedRegistersByPatchpoint, CCallHelpers& jit) override
+    {
+        m_from.link(&jit);
+        CCallHelpers::JumpList exceptions = generateImpl(vm, state, usedRegistersByPatchpoint, jit, std::make_index_sequence<std::tuple_size<std::tuple<Arguments...>>::value>());
+        jit.jump().linkTo(m_to, &jit);
+        return exceptions;
+    }
+
+protected:
+    JumpType m_from;
+    CCallHelpers::Label m_to;
+    FunctionType m_function;
+    ResultType m_result;
+    std::tuple<Arguments...> m_arguments;
+};
+
+#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) \
+    void DOMJITAccessCasePatchpointParams::addSlowPathCallImpl(CCallHelpers::JumpList from, CCallHelpers& jit, OperationType operation, ResultType result, std::tuple<__VA_ARGS__> args) \
+    { \
+        CCallHelpers::Label to = jit.label(); \
+        m_generators.append(std::make_unique<SlowPathCallGeneratorWithArguments<CCallHelpers::JumpList, OperationType, ResultType, __VA_ARGS__>>(from, to, operation, result, args)); \
+    } \
+
+DOMJIT_SLOW_PATH_CALLS(JSC_DEFINE_CALL_OPERATIONS)
+#undef JSC_DEFINE_CALL_OPERATIONS
+
+CCallHelpers::JumpList DOMJITAccessCasePatchpointParams::emitSlowPathCalls(VM& vm, AccessGenerationState& state, const RegisterSet& usedRegistersByPatchpoint, CCallHelpers& jit)
+{
+    CCallHelpers::JumpList exceptions;
+    for (auto& generator : m_generators)
+        exceptions.append(generator->generate(vm, state, usedRegistersByPatchpoint, jit));
+    return exceptions;
+}
+
+}
+
+#endif
diff --git a/Source/JavaScriptCore/bytecode/DOMJITAccessCasePatchpointParams.h b/Source/JavaScriptCore/bytecode/DOMJITAccessCasePatchpointParams.h
new file mode 100644 (file)
index 0000000..958aba7
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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
+
+#if ENABLE(JIT)
+
+#include "DOMJITPatchpointParams.h"
+
+namespace JSC {
+
+struct AccessGenerationState;
+
+class DOMJITAccessCasePatchpointParams : public DOMJIT::PatchpointParams {
+public:
+    DOMJITAccessCasePatchpointParams(Vector<DOMJIT::Value>&& regs, Vector<GPRReg>&& gpScratch, Vector<FPRReg>&& fpScratch)
+        : DOMJIT::PatchpointParams(WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch))
+    {
+    }
+
+    class SlowPathCallGenerator {
+    public:
+        virtual ~SlowPathCallGenerator() { }
+        virtual CCallHelpers::JumpList generate(VM&, AccessGenerationState&, const RegisterSet& usedRegistersByPatchpoint, CCallHelpers&) = 0;
+    };
+
+    CCallHelpers::JumpList emitSlowPathCalls(VM&, AccessGenerationState&, const RegisterSet& usedRegistersByPatchpoint, CCallHelpers&);
+
+private:
+#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) override;
+    DOMJIT_SLOW_PATH_CALLS(JSC_DEFINE_CALL_OPERATIONS)
+#undef JSC_DEFINE_CALL_OPERATIONS
+    Vector<std::unique_ptr<SlowPathCallGenerator>> m_generators;
+};
+
+}
+
+#endif
index 9a8a8e5..514d5e6 100644 (file)
@@ -31,6 +31,8 @@
 #include "BinarySwitch.h"
 #include "CCallHelpers.h"
 #include "CodeBlock.h"
+#include "DOMJITAccessCasePatchpointParams.h"
+#include "DOMJITCallDOMPatchpoint.h"
 #include "DirectArguments.h"
 #include "GetterSetter.h"
 #include "Heap.h"
@@ -72,7 +74,28 @@ void AccessGenerationState::succeed()
     success.append(jit->jump());
 }
 
-void AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra)
+const RegisterSet& AccessGenerationState::liveRegistersForCall()
+{
+    if (!m_calculatedRegistersForCallAndExceptionHandling)
+        calculateLiveRegistersForCallAndExceptionHandling();
+    return m_liveRegistersForCall;
+}
+
+const RegisterSet& AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite()
+{
+    if (!m_calculatedRegistersForCallAndExceptionHandling)
+        calculateLiveRegistersForCallAndExceptionHandling();
+    return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
+}
+
+static RegisterSet calleeSaveRegisters()
+{
+    RegisterSet result = RegisterSet::registersToNotSaveForJSCall();
+    result.filter(RegisterSet::registersToNotSaveForCCall());
+    return result;
+}
+
+const RegisterSet& AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling()
 {
     if (!m_calculatedRegistersForCallAndExceptionHandling) {
         m_calculatedRegistersForCallAndExceptionHandling = true;
@@ -83,35 +106,25 @@ void AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling(co
             RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType()));
 
         m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters());
-        m_liveRegistersForCall.merge(extra);
-        m_liveRegistersForCall.exclude(RegisterSet::registersToNotSaveForJSCall());
-        m_liveRegistersForCall.merge(extra);
+        m_liveRegistersForCall.exclude(calleeSaveRegisters());
     }
+    return m_liveRegistersForCall;
 }
 
-void AccessGenerationState::preserveLiveRegistersToStackForCall(const RegisterSet& extra)
+auto AccessGenerationState::preserveLiveRegistersToStackForCall(const RegisterSet& extra) -> SpillState
 {
-    calculateLiveRegistersForCallAndExceptionHandling(extra);
+    RegisterSet liveRegisters = liveRegistersForCall();
+    liveRegisters.merge(extra);
     
     unsigned extraStackPadding = 0;
-    unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegistersForCall(), extraStackPadding);
-    if (m_numberOfStackBytesUsedForRegisterPreservation != std::numeric_limits<unsigned>::max())
-        RELEASE_ASSERT(numberOfStackBytesUsedForRegisterPreservation == m_numberOfStackBytesUsedForRegisterPreservation);
-    m_numberOfStackBytesUsedForRegisterPreservation = numberOfStackBytesUsedForRegisterPreservation;
-}
-
-void AccessGenerationState::restoreLiveRegistersFromStackForCall(bool isGetter)
-{
-    RegisterSet dontRestore;
-    if (isGetter) {
-        // This is the result value. We don't want to overwrite the result with what we stored to the stack.
-        // We sometimes have to store it to the stack just in case we throw an exception and need the original value.
-        dontRestore.set(valueRegs);
-    }
-    restoreLiveRegistersFromStackForCall(dontRestore);
+    unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegisters, extraStackPadding);
+    return SpillState {
+        liveRegisters,
+        numberOfStackBytesUsedForRegisterPreservation
+    };
 }
 
-void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException()
+void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException(const SpillState& spillState)
 {
     // Even if we're a getter, we don't want to ignore the result value like we normally do
     // because the getter threw, and therefore, didn't return a value that means anything.
@@ -119,25 +132,26 @@ void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownExcept
     // inline cache. The subtlety here is if the base and the result are the same register,
     // and the getter threw, we want OSR exit to see the original base value, not the result
     // of the getter call.
-    RegisterSet dontRestore = liveRegistersForCall();
+    RegisterSet dontRestore = spillState.spilledRegisters;
     // As an optimization here, we only need to restore what is live for exception handling.
     // We can construct the dontRestore set to accomplish this goal by having it contain only
     // what is live for call but not live for exception handling. By ignoring things that are
     // only live at the call but not the exception handler, we will only restore things live
     // at the exception handler.
     dontRestore.exclude(liveRegistersToPreserveAtExceptionHandlingCallSite());
-    restoreLiveRegistersFromStackForCall(dontRestore);
+    restoreLiveRegistersFromStackForCall(spillState, dontRestore);
 }
 
-void AccessGenerationState::restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore)
+void AccessGenerationState::restoreLiveRegistersFromStackForCall(const SpillState& spillState, const RegisterSet& dontRestore)
 {
     unsigned extraStackPadding = 0;
-    ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, liveRegistersForCall(), dontRestore, m_numberOfStackBytesUsedForRegisterPreservation, extraStackPadding);
+    ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, spillState.spilledRegisters, dontRestore, spillState.numberOfStackBytesUsedForRegisterPreservation, extraStackPadding);
 }
 
 CallSiteIndex AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal()
 {
-    RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
+    if (!m_calculatedRegistersForCallAndExceptionHandling)
+        calculateLiveRegistersForCallAndExceptionHandling();
 
     if (!m_calculatedCallSiteIndex) {
         m_calculatedCallSiteIndex = true;
@@ -151,8 +165,11 @@ CallSiteIndex AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal
     return m_callSiteIndex;
 }
 
-const HandlerInfo& AccessGenerationState::originalExceptionHandler() const
+const HandlerInfo& AccessGenerationState::originalExceptionHandler()
 {
+    if (!m_calculatedRegistersForCallAndExceptionHandling)
+        calculateLiveRegistersForCallAndExceptionHandling();
+
     RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
     HandlerInfo* exceptionHandler = jit->codeBlock()->handlerForIndex(stubInfo->callSiteIndex.bits());
     RELEASE_ASSERT(exceptionHandler);
@@ -932,6 +949,16 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             return;
         }
 
+        if (m_type == CustomAccessorGetter && m_rareData->domJIT) {
+            // We do not need to emit CheckDOM operation since structure check ensures
+            // that the structure of the given base value is structure()! So all we should
+            // do is performing the CheckDOM thingy in IC compiling time here.
+            if (structure()->classInfo()->isSubClassOf(m_rareData->domJIT->thisClassInfo())) {
+                emitDOMJITGetter(state, baseForGetGPR);
+                return;
+            }
+        }
+
         // Stuff for custom getters/setters.
         CCallHelpers::Call operationCall;
 
@@ -940,12 +967,19 @@ void AccessCase::generateImpl(AccessGenerationState& state)
         CCallHelpers::Call fastPathCall;
         CCallHelpers::Call slowPathCall;
 
-        CCallHelpers::Jump success;
-        CCallHelpers::Jump fail;
-
         // This also does the necessary calculations of whether or not we're an
         // exception handling call site.
-        state.preserveLiveRegistersToStackForCall();
+        AccessGenerationState::SpillState spillState = state.preserveLiveRegistersToStackForCall();
+
+        auto restoreLiveRegistersFromStackForCall = [&](AccessGenerationState::SpillState& spillState, bool callHasReturnValue) {
+            RegisterSet dontRestore;
+            if (callHasReturnValue) {
+                // This is the result value. We don't want to overwrite the result with what we stored to the stack.
+                // We sometimes have to store it to the stack just in case we throw an exception and need the original value.
+                dontRestore.set(valueRegs);
+            }
+            state.restoreLiveRegistersFromStackForCall(spillState, dontRestore);
+        };
 
         jit.store32(
             CCallHelpers::TrustedImm32(state.callSiteIndexForExceptionHandlingOrOriginal().bits()),
@@ -966,6 +1000,8 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             // Therefore, we temporarily grow the stack for the purpose of the call and then
             // shrink it after.
 
+            state.setSpillStateForJSGetterSetter(spillState);
+
             RELEASE_ASSERT(!m_rareData->callLinkInfo);
             m_rareData->callLinkInfo = std::make_unique<CallLinkInfo>();
             
@@ -1065,9 +1101,10 @@ void AccessCase::generateImpl(AccessGenerationState& state)
 
             done.link(&jit);
 
-            jit.addPtr(CCallHelpers::TrustedImm32((codeBlock->stackPointerOffset() * sizeof(Register)) - state.preservedReusedRegisterState.numberOfBytesPreserved - state.numberOfStackBytesUsedForRegisterPreservation()),
+            jit.addPtr(CCallHelpers::TrustedImm32((codeBlock->stackPointerOffset() * sizeof(Register)) - state.preservedReusedRegisterState.numberOfBytesPreserved - spillState.numberOfStackBytesUsedForRegisterPreservation),
                 GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
-            state.restoreLiveRegistersFromStackForCall(isGetter());
+            bool callHasReturnValue = isGetter();
+            restoreLiveRegistersFromStackForCall(spillState, callHasReturnValue);
 
             jit.addLinkTask(
                 [=, &vm] (LinkBuffer& linkBuffer) {
@@ -1127,11 +1164,12 @@ void AccessCase::generateImpl(AccessGenerationState& state)
             CCallHelpers::Jump noException =
                 jit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
 
-            state.restoreLiveRegistersFromStackForCallWithThrownException();
+            state.restoreLiveRegistersFromStackForCallWithThrownException(spillState);
             state.emitExplicitExceptionHandler();
         
             noException.link(&jit);
-            state.restoreLiveRegistersFromStackForCall(isGetter());
+            bool callHasReturnValue = isGetter();
+            restoreLiveRegistersFromStackForCall(spillState, callHasReturnValue);
         }
         state.succeed();
         return;
@@ -1248,7 +1286,7 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                 RegisterSet extraRegistersToPreserve;
                 extraRegistersToPreserve.set(baseGPR);
                 extraRegistersToPreserve.set(valueRegs);
-                state.preserveLiveRegistersToStackForCall(extraRegistersToPreserve);
+                AccessGenerationState::SpillState spillState = state.preserveLiveRegistersToStackForCall(extraRegistersToPreserve);
                 
                 jit.store32(
                     CCallHelpers::TrustedImm32(
@@ -1288,11 +1326,11 @@ void AccessCase::generateImpl(AccessGenerationState& state)
                 CCallHelpers::Jump noException =
                     jit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
                 
-                state.restoreLiveRegistersFromStackForCallWithThrownException();
+                state.restoreLiveRegistersFromStackForCallWithThrownException(spillState);
                 state.emitExplicitExceptionHandler();
                 
                 noException.link(&jit);
-                state.restoreLiveRegistersFromStackForCall();
+                state.restoreLiveRegistersFromStackForCall(spillState);
             }
         }
 
@@ -1385,6 +1423,124 @@ void AccessCase::generateImpl(AccessGenerationState& state)
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+void AccessCase::emitDOMJITGetter(AccessGenerationState& state, GPRReg baseForGetGPR)
+{
+    CCallHelpers& jit = *state.jit;
+    VM& vm = *jit.vm();
+    StructureStubInfo& stubInfo = *state.stubInfo;
+    JSValueRegs valueRegs = state.valueRegs;
+    GPRReg baseGPR = state.baseGPR;
+    GPRReg scratchGPR = state.scratchGPR;
+
+    // We construct the environment that can execute the DOMJIT::Patchpoint here.
+    Ref<DOMJIT::CallDOMPatchpoint> patchpoint = m_rareData->domJIT->callDOM();
+
+    Vector<GPRReg> gpScratch;
+    Vector<FPRReg> fpScratch;
+    Vector<DOMJIT::Value> regs;
+
+    ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
+    allocator.lock(baseGPR);
+#if USE(JSVALUE32_64)
+    allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR));
+#endif
+    allocator.lock(valueRegs);
+    allocator.lock(scratchGPR);
+
+    GPRReg paramBaseGPR = InvalidGPRReg;
+    GPRReg paramGlobalObjectGPR = InvalidGPRReg;
+    JSValueRegs paramValueRegs = valueRegs;
+    GPRReg remainingScratchGPR = InvalidGPRReg;
+
+    // valueRegs and baseForGetGPR may be the same. For example, in Baseline JIT, we pass the same regT0 for baseGPR and valueRegs.
+    // In FTL, there is no constraint that the baseForGetGPR interferes with the result. To make implementation simple in
+    // DOMJIT::Patchpoint, DOMJIT::Patchpoint assumes that result registers always early interfere with input registers, in this case,
+    // baseForGetGPR. So we move baseForGetGPR to the other register if baseForGetGPR == valueRegs.
+    if (baseForGetGPR != valueRegs.payloadGPR()) {
+        paramBaseGPR = baseForGetGPR;
+        if (!patchpoint->requireGlobalObject)
+            remainingScratchGPR = scratchGPR;
+        else
+            paramGlobalObjectGPR = scratchGPR;
+    } else {
+        jit.move(valueRegs.payloadGPR(), scratchGPR);
+        paramBaseGPR = scratchGPR;
+        if (patchpoint->requireGlobalObject)
+            paramGlobalObjectGPR = allocator.allocateScratchGPR();
+    }
+
+    JSGlobalObject* globalObjectForDOMJIT = structure()->globalObject();
+
+    regs.append(paramValueRegs);
+    if (patchpoint->requireGlobalObject) {
+        ASSERT(paramGlobalObjectGPR != InvalidGPRReg);
+        regs.append(DOMJIT::Value(paramGlobalObjectGPR, globalObjectForDOMJIT));
+    }
+    regs.append(paramBaseGPR);
+
+    if (patchpoint->numGPScratchRegisters) {
+        unsigned i = 0;
+        if (remainingScratchGPR != InvalidGPRReg) {
+            gpScratch.append(remainingScratchGPR);
+            ++i;
+        }
+        for (; i < patchpoint->numGPScratchRegisters; ++i)
+            gpScratch.append(allocator.allocateScratchGPR());
+    }
+
+    for (unsigned i = 0; i < patchpoint->numFPScratchRegisters; ++i)
+        fpScratch.append(allocator.allocateScratchFPR());
+
+    // Let's store the reused registers to the stack. After that, we can use allocated scratch registers.
+    ScratchRegisterAllocator::PreservedState preservedState =
+        allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::SpaceForCCall);
+
+    if (verbose) {
+        dataLog("baseGPR = ", baseGPR, "\n");
+        dataLog("valueRegs = ", valueRegs, "\n");
+        dataLog("scratchGPR = ", scratchGPR, "\n");
+        dataLog("paramBaseGPR = ", paramBaseGPR, "\n");
+        if (paramGlobalObjectGPR != InvalidGPRReg)
+            dataLog("paramGlobalObjectGPR = ", paramGlobalObjectGPR, "\n");
+        dataLog("paramValueRegs = ", paramValueRegs, "\n");
+        for (unsigned i = 0; i < patchpoint->numGPScratchRegisters; ++i)
+            dataLog("gpScratch[", i, "] = ", gpScratch[i], "\n");
+    }
+
+    if (patchpoint->requireGlobalObject)
+        jit.move(CCallHelpers::TrustedImmPtr(globalObjectForDOMJIT), paramGlobalObjectGPR);
+
+    // We just spill the registers used in DOMJIT::Patchpoint here. For not spilled registers here explicitly,
+    // they must be in the used register set passed by the callers (Baseline, DFG, and FTL) if they need to be kept.
+    // Some registers can be locked, but not in the used register set. For example, the caller could make baseGPR
+    // same to valueRegs, and not include it in the used registers since it will be changed.
+    RegisterSet registersToSpillForCCall;
+    for (auto& value : regs) {
+        DOMJIT::Reg reg = value.reg();
+        if (reg.isJSValueRegs())
+            registersToSpillForCCall.set(reg.jsValueRegs());
+        else if (reg.isGPR())
+            registersToSpillForCCall.set(reg.gpr());
+        else
+            registersToSpillForCCall.set(reg.fpr());
+    }
+    for (GPRReg reg : gpScratch)
+        registersToSpillForCCall.set(reg);
+    for (FPRReg reg : fpScratch)
+        registersToSpillForCCall.set(reg);
+    registersToSpillForCCall.exclude(RegisterSet::registersToNotSaveForCCall());
+
+    DOMJITAccessCasePatchpointParams params(WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
+    patchpoint->generator()->run(jit, params);
+    allocator.restoreReusedRegistersByPopping(jit, preservedState);
+    state.succeed();
+
+    CCallHelpers::JumpList exceptions = params.emitSlowPathCalls(vm, state, registersToSpillForCCall, jit);
+    exceptions.link(&jit);
+    allocator.restoreReusedRegistersByPopping(jit, preservedState);
+    state.emitExplicitExceptionHandler();
+}
+
 PolymorphicAccess::PolymorphicAccess() { }
 PolymorphicAccess::~PolymorphicAccess() { }
 
@@ -1684,13 +1840,15 @@ AccessGenerationResult PolymorphicAccess::regenerate(
         MacroAssembler::Label makeshiftCatchHandler = jit.label();
 
         int stackPointerOffset = codeBlock->stackPointerOffset() * sizeof(EncodedJSValue);
+        AccessGenerationState::SpillState spillStateForJSGetterSetter = state.spillStateForJSGetterSetter();
+        ASSERT(!spillStateForJSGetterSetter.isEmpty());
         stackPointerOffset -= state.preservedReusedRegisterState.numberOfBytesPreserved;
-        stackPointerOffset -= state.numberOfStackBytesUsedForRegisterPreservation();
+        stackPointerOffset -= spillStateForJSGetterSetter.numberOfStackBytesUsedForRegisterPreservation;
 
         jit.loadPtr(vm.addressOfCallFrameForCatch(), GPRInfo::callFrameRegister);
         jit.addPtr(CCallHelpers::TrustedImm32(stackPointerOffset), GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
 
-        state.restoreLiveRegistersFromStackForCallWithThrownException();
+        state.restoreLiveRegistersFromStackForCallWithThrownException(spillStateForJSGetterSetter);
         state.restoreScratch();
         CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit.jump();
 
index 970a235..d302016 100644 (file)
@@ -277,6 +277,7 @@ private:
     
     void generateImpl(AccessGenerationState&);
     void emitIntrinsicGetter(AccessGenerationState&);
+    void emitDOMJITGetter(AccessGenerationState&, GPRReg baseForGetGPR);
     
     AccessType m_type { Load };
     State m_state { Primordial };
@@ -472,19 +473,21 @@ struct AccessGenerationState {
     void restoreScratch();
     void succeed();
 
-    void calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra = RegisterSet());
+    struct SpillState {
+        RegisterSet spilledRegisters { };
+        unsigned numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
 
-    void preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
+        bool isEmpty() const { return numberOfStackBytesUsedForRegisterPreservation == std::numeric_limits<unsigned>::max(); }
+    };
 
-    void restoreLiveRegistersFromStackForCall(bool isGetter = false);
-    void restoreLiveRegistersFromStackForCallWithThrownException();
-    void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
+    const RegisterSet& calculateLiveRegistersForCallAndExceptionHandling();
 
-    const RegisterSet& liveRegistersForCall()
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        return m_liveRegistersForCall;
-    }
+    SpillState preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
+
+    void restoreLiveRegistersFromStackForCallWithThrownException(const SpillState&);
+    void restoreLiveRegistersFromStackForCall(const SpillState&, const RegisterSet& dontRestore = RegisterSet());
+
+    const RegisterSet& liveRegistersForCall();
 
     CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
     CallSiteIndex callSiteIndexForExceptionHandling()
@@ -495,29 +498,30 @@ struct AccessGenerationState {
         return m_callSiteIndex;
     }
 
-    const HandlerInfo& originalExceptionHandler() const;
-    unsigned numberOfStackBytesUsedForRegisterPreservation() const
-    {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        return m_numberOfStackBytesUsedForRegisterPreservation;
-    }
+    const HandlerInfo& originalExceptionHandler();
 
     bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
     CallSiteIndex originalCallSiteIndex() const;
     
     void emitExplicitExceptionHandler();
-    
-private:
-    const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
+
+    void setSpillStateForJSGetterSetter(SpillState& spillState)
     {
-        RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
-        return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
+        if (!m_spillStateForJSGetterSetter.isEmpty()) {
+            ASSERT(m_spillStateForJSGetterSetter.numberOfStackBytesUsedForRegisterPreservation == spillState.numberOfStackBytesUsedForRegisterPreservation);
+            ASSERT(m_spillStateForJSGetterSetter.spilledRegisters == spillState.spilledRegisters);
+        }
+        m_spillStateForJSGetterSetter = spillState;
     }
+    SpillState spillStateForJSGetterSetter() const { return m_spillStateForJSGetterSetter; }
+    
+private:
+    const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite();
     
     RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
     RegisterSet m_liveRegistersForCall;
     CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
-    unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
+    SpillState m_spillStateForJSGetterSetter;
     bool m_calculatedRegistersForCallAndExceptionHandling : 1;
     bool m_needsToRestoreRegistersIfException : 1;
     bool m_calculatedCallSiteIndex : 1;
index 0a11c67..8ed77b5 100644 (file)
@@ -40,7 +40,7 @@ static void dispatch(SpeculativeJIT* jit, CCallHelpers::JumpList from, Operation
 }
 
 #define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) \
-    void DOMJITPatchpointParams::addSlowPathCallImpl(CCallHelpers::JumpList from, CCallHelpers&, OperationType operation, ResultType result, std::tuple<__VA_ARGS__> args) const \
+    void DOMJITPatchpointParams::addSlowPathCallImpl(CCallHelpers::JumpList from, CCallHelpers&, OperationType operation, ResultType result, std::tuple<__VA_ARGS__> args) \
     { \
         dispatch(m_jit, from, operation, result, args, std::make_index_sequence<std::tuple_size<decltype(args)>::value>()); \
     } \
index 46fdd4b..fb6ab0b 100644 (file)
@@ -42,7 +42,7 @@ public:
     }
 
 private:
-#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) const override;
+#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) override;
     DOMJIT_SLOW_PATH_CALLS(JSC_DEFINE_CALL_OPERATIONS)
 #undef JSC_DEFINE_CALL_OPERATIONS
 
index 38608e2..bb9f5e4 100644 (file)
@@ -34,7 +34,7 @@ namespace JSC { namespace DOMJIT {
 
 class PatchpointParams;
 
-typedef CCallHelpers::JumpList PatchpointGeneratorFunction(CCallHelpers&, const PatchpointParams&);
+typedef CCallHelpers::JumpList PatchpointGeneratorFunction(CCallHelpers&, PatchpointParams&);
 typedef SharedTask<PatchpointGeneratorFunction> PatchpointGenerator;
 
 // DOMJIT patchpoint is the way to inject an opaque code generator into DFG and FTL.
index aeece1b..d83b6b0 100644 (file)
@@ -55,13 +55,13 @@ public:
     }
 
     template<typename FunctionType, typename ResultType, typename... Arguments>
-    void addSlowPathCall(CCallHelpers::JumpList from, CCallHelpers& jit, FunctionType function, ResultType result, Arguments... arguments) const
+    void addSlowPathCall(CCallHelpers::JumpList from, CCallHelpers& jit, FunctionType function, ResultType result, Arguments... arguments)
     {
         addSlowPathCallImpl(from, jit, function, result, std::make_tuple(arguments...));
     }
 
 private:
-#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) JS_EXPORT_PRIVATE virtual void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) const = 0;
+#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) JS_EXPORT_PRIVATE virtual void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) = 0;
     DOMJIT_SLOW_PATH_CALLS(JSC_DEFINE_CALL_OPERATIONS)
 #undef JSC_DEFINE_CALL_OPERATIONS
 
index aa22558..106e2e5 100644 (file)
@@ -50,7 +50,7 @@ static void dispatch(CCallHelpers& jit, FTL::State* state, const B3::StackmapGen
 }
 
 #define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) \
-    void DOMJITPatchpointParams::addSlowPathCallImpl(CCallHelpers::JumpList from, CCallHelpers& jit, OperationType operation, ResultType result, std::tuple<__VA_ARGS__> args) const \
+    void DOMJITPatchpointParams::addSlowPathCallImpl(CCallHelpers::JumpList from, CCallHelpers& jit, OperationType operation, ResultType result, std::tuple<__VA_ARGS__> args) \
     { \
         dispatch(jit, &m_state, m_params, m_node, m_exceptions, from, operation, result, args, std::make_index_sequence<std::tuple_size<decltype(args)>::value>()); \
     } \
index 2d13eea..3387c90 100644 (file)
@@ -46,7 +46,7 @@ public:
     }
 
 private:
-#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) const override;
+#define JSC_DEFINE_CALL_OPERATIONS(OperationType, ResultType, ...) void addSlowPathCallImpl(CCallHelpers::JumpList, CCallHelpers&, OperationType, ResultType, std::tuple<__VA_ARGS__> args) override;
     DOMJIT_SLOW_PATH_CALLS(JSC_DEFINE_CALL_OPERATIONS)
 #undef JSC_DEFINE_CALL_OPERATIONS
 
index 6e89716..8402236 100644 (file)
@@ -557,6 +557,22 @@ public:
         return Structure::create(vm, globalObject, prototype, TypeInfo(JSC::JSType(LastJSCObjectType + 1), StructureFlags), info());
     }
 
+#if ENABLE(JIT)
+    static Ref<DOMJIT::Patchpoint> checkDOMJITNode()
+    {
+        Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
+        patchpoint->setGenerator([=](CCallHelpers& jit, DOMJIT::PatchpointParams& params) {
+            CCallHelpers::JumpList failureCases;
+            failureCases.append(jit.branch8(
+                CCallHelpers::NotEqual,
+                CCallHelpers::Address(params[0].gpr(), JSCell::typeInfoTypeOffset()),
+                CCallHelpers::TrustedImm32(JSC::JSType(LastJSCObjectType + 1))));
+            return failureCases;
+        });
+        return patchpoint;
+    }
+#endif
+
     static DOMJITNode* create(VM& vm, Structure* structure)
     {
         DOMJITNode* getter = new (NotNull, allocateCell<DOMJITNode>(vm.heap, sizeof(DOMJITNode))) DOMJITNode(vm, structure);
@@ -608,23 +624,14 @@ public:
 #if ENABLE(JIT)
         Ref<DOMJIT::Patchpoint> checkDOM() override
         {
-            Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
-            patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
-                CCallHelpers::JumpList failureCases;
-                failureCases.append(jit.branch8(
-                    CCallHelpers::NotEqual,
-                    CCallHelpers::Address(params[0].gpr(), JSCell::typeInfoTypeOffset()),
-                    CCallHelpers::TrustedImm32(JSC::JSType(LastJSCObjectType + 1))));
-                return failureCases;
-            });
-            return patchpoint;
+            return DOMJITNode::checkDOMJITNode();
         }
 
         Ref<DOMJIT::CallDOMPatchpoint> callDOM() override
         {
             Ref<DOMJIT::CallDOMPatchpoint> patchpoint = DOMJIT::CallDOMPatchpoint::create();
             patchpoint->requireGlobalObject = false;
-            patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+            patchpoint->setGenerator([=](CCallHelpers& jit, DOMJIT::PatchpointParams& params) {
                 JSValueRegs results = params[0].jsValueRegs();
                 GPRReg dom = params[1].gpr();
 
@@ -666,6 +673,115 @@ private:
     }
 };
 
+class DOMJITGetterComplex : public DOMJITNode {
+public:
+    DOMJITGetterComplex(VM& vm, Structure* structure)
+        : Base(vm, structure)
+    {
+    }
+
+    DECLARE_INFO;
+    typedef DOMJITNode Base;
+    static const unsigned StructureFlags = Base::StructureFlags;
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(JSC::JSType(LastJSCObjectType + 1), StructureFlags), info());
+    }
+
+    static DOMJITGetterComplex* create(VM& vm, JSGlobalObject* globalObject, Structure* structure)
+    {
+        DOMJITGetterComplex* getter = new (NotNull, allocateCell<DOMJITGetterComplex>(vm.heap, sizeof(DOMJITGetterComplex))) DOMJITGetterComplex(vm, structure);
+        getter->finishCreation(vm, globalObject);
+        return getter;
+    }
+
+    class DOMJITNodeDOMJIT : public DOMJIT::GetterSetter {
+    public:
+        DOMJITNodeDOMJIT()
+            : DOMJIT::GetterSetter(DOMJITGetterComplex::customGetter, nullptr, DOMJITNode::info())
+        {
+        }
+
+#if ENABLE(JIT)
+        Ref<DOMJIT::Patchpoint> checkDOM() override
+        {
+            return DOMJITNode::checkDOMJITNode();
+        }
+
+        Ref<DOMJIT::CallDOMPatchpoint> callDOM() override
+        {
+            RefPtr<DOMJIT::CallDOMPatchpoint> patchpoint = DOMJIT::CallDOMPatchpoint::create();
+            static_assert(GPRInfo::numberOfRegisters >= 4, "Number of registers should be larger or equal to 4.");
+            patchpoint->numGPScratchRegisters = GPRInfo::numberOfRegisters - 4;
+            patchpoint->numFPScratchRegisters = 3;
+            patchpoint->setGenerator([=](CCallHelpers& jit, DOMJIT::PatchpointParams& params) {
+                JSValueRegs results = params[0].jsValueRegs();
+                GPRReg domGPR = params[2].gpr();
+                for (unsigned i = 0; i < patchpoint->numGPScratchRegisters; ++i)
+                    jit.move(CCallHelpers::TrustedImm32(42), params.gpScratch(i));
+
+                params.addSlowPathCall(jit.jump(), jit, static_cast<EncodedJSValue(*)(ExecState*, void*)>([](ExecState* exec, void* pointer) {
+                    VM& vm = exec->vm();
+                    auto scope = DECLARE_THROW_SCOPE(vm);
+                    auto* object = static_cast<DOMJITNode*>(pointer);
+                    auto* domjitGetterComplex = jsDynamicCast<DOMJITGetterComplex*>(object);
+                    if (domjitGetterComplex) {
+                        if (domjitGetterComplex->m_enableException)
+                            return JSValue::encode(throwException(exec, scope, createError(exec, ASCIILiteral("DOMJITGetterComplex slow call exception"))));
+                    }
+                    return JSValue::encode(jsNumber(object->value()));
+                }), results, domGPR);
+                return CCallHelpers::JumpList();
+
+            });
+            return *patchpoint.get();
+        }
+#endif
+    };
+
+    static DOMJIT::GetterSetter* domJITNodeGetterSetter()
+    {
+        static NeverDestroyed<DOMJITNodeDOMJIT> graph;
+        return &graph.get();
+    }
+
+private:
+    void finishCreation(VM& vm, JSGlobalObject* globalObject)
+    {
+        Base::finishCreation(vm);
+        DOMJIT::GetterSetter* domJIT = domJITNodeGetterSetter();
+        CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, domJIT->getter(), domJIT->setter(), domJIT);
+        putDirectCustomAccessor(vm, Identifier::fromString(&vm, "customGetter"), customGetterSetter, ReadOnly | CustomAccessor);
+        putDirectNativeFunction(vm, globalObject, Identifier::fromString(&vm, "enableException"), 0, functionEnableException, NoIntrinsic, 0);
+    }
+
+    static EncodedJSValue JSC_HOST_CALL functionEnableException(ExecState* exec)
+    {
+        auto* object = jsDynamicCast<DOMJITGetterComplex*>(exec->thisValue());
+        if (object)
+            object->m_enableException = true;
+        return JSValue::encode(jsUndefined());
+    }
+
+    static EncodedJSValue customGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName)
+    {
+        VM& vm = exec->vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
+
+        auto* thisObject = jsDynamicCast<DOMJITNode*>(JSValue::decode(thisValue));
+        if (!thisObject)
+            return throwVMTypeError(exec, scope);
+        if (auto* domjitGetterComplex = jsDynamicCast<DOMJITGetterComplex*>(JSValue::decode(thisValue))) {
+            if (domjitGetterComplex->m_enableException)
+                return JSValue::encode(throwException(exec, scope, createError(exec, ASCIILiteral("DOMJITGetterComplex slow call exception"))));
+        }
+        return JSValue::encode(jsNumber(thisObject->value()));
+    }
+
+    bool m_enableException { false };
+};
+
 const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, CREATE_METHOD_TABLE(Element) };
 const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, 0, CREATE_METHOD_TABLE(Masquerader) };
 const ClassInfo Root::s_info = { "Root", &Base::s_info, 0, CREATE_METHOD_TABLE(Root) };
@@ -673,6 +789,7 @@ const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, CREAT
 const ClassInfo CustomGetter::s_info = { "CustomGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(CustomGetter) };
 const ClassInfo DOMJITNode::s_info = { "DOMJITNode", &Base::s_info, 0, CREATE_METHOD_TABLE(DOMJITNode) };
 const ClassInfo DOMJITGetter::s_info = { "DOMJITGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(DOMJITGetter) };
+const ClassInfo DOMJITGetterComplex::s_info = { "DOMJITGetterComplex", &Base::s_info, 0, CREATE_METHOD_TABLE(DOMJITGetterComplex) };
 const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, 0, CREATE_METHOD_TABLE(RuntimeArray) };
 const ClassInfo SimpleObject::s_info = { "SimpleObject", &Base::s_info, 0, CREATE_METHOD_TABLE(SimpleObject) };
 static bool test262AsyncPassed { false };
@@ -703,6 +820,7 @@ static EncodedJSValue JSC_HOST_CALL functionCreateImpureGetter(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateCustomGetterObject(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateDOMJITNodeObject(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterObject(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterComplexObject(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateBuiltin(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateGlobalObject(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState*);
@@ -990,6 +1108,7 @@ protected:
         addFunction(vm, "createCustomGetterObject", functionCreateCustomGetterObject, 0);
         addFunction(vm, "createDOMJITNodeObject", functionCreateDOMJITNodeObject, 0);
         addFunction(vm, "createDOMJITGetterObject", functionCreateDOMJITGetterObject, 0);
+        addFunction(vm, "createDOMJITGetterComplexObject", functionCreateDOMJITGetterComplexObject, 0);
         addFunction(vm, "createBuiltin", functionCreateBuiltin, 2);
         addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
         addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);
@@ -1511,6 +1630,14 @@ EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterObject(ExecState* exec)
     return JSValue::encode(result);
 }
 
+EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterComplexObject(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    Structure* structure = DOMJITGetterComplex::createStructure(exec->vm(), exec->lexicalGlobalObject(), jsNull());
+    DOMJITGetterComplex* result = DOMJITGetterComplex::create(exec->vm(), exec->lexicalGlobalObject(), structure);
+    return JSValue::encode(result);
+}
+
 EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState* exec)
 {
     JSLockHolder lock(exec);
index be423bd..5c9d603 100644 (file)
@@ -1,3 +1,19 @@
+2016-10-17  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Use DOMJIT::Patchpoint in IC
+        https://bugs.webkit.org/show_bug.cgi?id=163223
+
+        Reviewed by Saam Barati.
+
+        Make DOMJITPatchpointParams non-const.
+
+        * domjit/DOMJITHelpers.h:
+        (WebCore::DOMJITHelpers::toWrapper):
+        * domjit/JSNodeDOMJIT.cpp:
+        (WebCore::createCallDOMForOffsetAccess):
+        (WebCore::checkNode):
+        (WebCore::NodeNodeTypeDOMJIT::callDOM):
+
 2016-10-17  Chris Dumez  <cdumez@apple.com>
 
         Move form.reportValidity() behind InteractiveFormValidation setting
index dbe5365..1806b2a 100644 (file)
@@ -59,7 +59,7 @@ void tryLookUpWrapperCache(CCallHelpers& jit, CCallHelpers::JumpList& failureCas
 }
 
 template<typename WrappedType, typename ToJSFunction>
-void toWrapper(CCallHelpers& jit, const JSC::DOMJIT::PatchpointParams& params, GPRReg wrapped, GPRReg globalObject, JSValueRegs result, ToJSFunction function, JSC::JSValue globalObjectConstant)
+void toWrapper(CCallHelpers& jit, JSC::DOMJIT::PatchpointParams& params, GPRReg wrapped, GPRReg globalObject, JSValueRegs result, ToJSFunction function, JSC::JSValue globalObjectConstant)
 {
     ASSERT(wrapped != result.payloadGPR());
     ASSERT(globalObject != result.payloadGPR());
index b50d24e..d8f592c 100644 (file)
@@ -54,7 +54,7 @@ static Ref<DOMJIT::CallDOMPatchpoint> createCallDOMForOffsetAccess(ptrdiff_t off
 {
     Ref<DOMJIT::CallDOMPatchpoint> patchpoint = DOMJIT::CallDOMPatchpoint::create();
     patchpoint->numGPScratchRegisters = 1;
-    patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+    patchpoint->setGenerator([=](CCallHelpers& jit, DOMJIT::PatchpointParams& params) {
         JSValueRegs result = params[0].jsValueRegs();
         GPRReg globalObject = params[1].gpr();
         GPRReg node = params[2].gpr();
@@ -84,7 +84,7 @@ static Ref<DOMJIT::CallDOMPatchpoint> createCallDOMForOffsetAccess(ptrdiff_t off
 static Ref<DOMJIT::Patchpoint> checkNode()
 {
     Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
-    patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+    patchpoint->setGenerator([=](CCallHelpers& jit, DOMJIT::PatchpointParams& params) {
         CCallHelpers::JumpList failureCases;
         failureCases.append(DOMJITHelpers::branchIfNotNode(jit, params[0].gpr()));
         return failureCases;
@@ -157,7 +157,7 @@ Ref<DOMJIT::CallDOMPatchpoint> NodeNodeTypeDOMJIT::callDOM()
 {
     Ref<DOMJIT::CallDOMPatchpoint> patchpoint = DOMJIT::CallDOMPatchpoint::create();
     patchpoint->requireGlobalObject = false;
-    patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+    patchpoint->setGenerator([=](CCallHelpers& jit, DOMJIT::PatchpointParams& params) {
         JSValueRegs result = params[0].jsValueRegs();
         GPRReg node = params[1].gpr();
         jit.load8(CCallHelpers::Address(node, JSC::JSCell::typeInfoTypeOffset()), result.payloadGPR());