[DOMJIT] Add initial CheckDOM and CallDOM implementations
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Oct 2016 05:20:10 +0000 (05:20 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 6 Oct 2016 05:20:10 +0000 (05:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=162941

Reviewed by Filip Pizlo.

JSTests:

* stress/domjit-getter-poly.js: Added.
(shouldBe):
(access):
* stress/domjit-getter-proto.js: Added.
(shouldBe):
(access):
* stress/domjit-getter-super-poly.js: Added.
(shouldBe):
(access):
* stress/domjit-getter.js: Added.
(shouldBe):
(access):

Source/JavaScriptCore:

This patch implements a prototype of DOMJIT accelerated getter.
We add two new DFG nodes, CheckDOM and CallDOM.

CheckDOM is used to filter inappropriate |this| object for DOM getter. Its functionality
is equivalent to jsDynamicCast's Check. You can use like "CheckDOM, @1, JSNode::info()",
and this CheckDOM incurs a BadType exit if the class of the given @1 is not a subclass of
JSNode::info().

CallDOM is used to emit actual DOM operations. It takes GlobalObject and checked DOM
object. And it returns JSValue as its result.

Both CheckDOM and CallDOM can take a DOMJIT::Patchpoint. This is somewhat code snippet
generator, and is injectable to DFG and FTL. DFG and FTL set up registers correctly
according to DOMJIT::Patchpoint's requirement and invoke this patchpoint generator to emit code.
While CallDOM always requires a patchpoint, ideally CheckDOM does not require it since
isSubclassOf check can be implemented in DFG / FTL side. However, some classes have a
faster way to query isSubclassOf. For example, JSNode in WebCore introduces a special
JSType to optimize this query. CheckDOM's patchpoint gives us a chance to emit special
faster code for such a case.

By leveraging above nodes, we can construct DOMJIT accelerated getter. When DFG recognizes the
given getter call is CustomGetter and it has DOMJIT::GetterSetter information, DFG emits the above nodes.
We implemented a prototype in jsc.cpp shell as DOMJITGetter to test the functionality.

Notes about the future extensions.

1. Currently, we do not allow CallDOM to emit any function calls. This will be extended by
   adding `addSlowPathCall` functionality to DOMJIT::Patchpoint later. Interesting thing is that
   we need to create an abstraction over DFG slow path call and FTL slow path call!

2. CheckDOM is not handled in DFGTypeCheckHoistingPhase yet. And we have a chance to merge several CheckDOM into one.
   For example, given CheckDOM A and CheckDOM B to the same target. If A is subclass of B, we can merge them to CheckDOM A.

* JavaScriptCore.xcodeproj/project.pbxproj:
* b3/B3Effects.h:
(JSC::B3::Effects::forCheck):
* b3/B3Value.cpp:
(JSC::B3::Value::effects):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
(JSC::GetByIdStatus::makesCalls):
(JSC::GetByIdStatus::dump):
* bytecode/GetByIdStatus.h:
(JSC::GetByIdStatus::GetByIdStatus):
(JSC::GetByIdStatus::isCustom):
(JSC::GetByIdStatus::takesSlowPath):
(JSC::GetByIdStatus::isSimple): Deleted.
* bytecode/SpeculatedType.cpp:
(JSC::speculationFromClassInfo):
* dfg/DFGAbstractInterpreter.h:
(JSC::DFG::AbstractInterpreter::filterClassInfo):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::filterClassInfo):
* dfg/DFGAbstractValue.cpp:
(JSC::DFG::AbstractValue::filterClassInfo):
* dfg/DFGAbstractValue.h:
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::handleDOMJITGetter):
(JSC::DFG::ByteCodeParser::handleGetById):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNode.h:
(JSC::DFG::Node::hasHeapPrediction):
(JSC::DFG::Node::hasDOMJIT):
(JSC::DFG::Node::domJIT):
(JSC::DFG::Node::hasClassInfo):
(JSC::DFG::Node::classInfo):
(JSC::DFG::Node::OpInfoWrapper::OpInfoWrapper):
(JSC::DFG::Node::OpInfoWrapper::operator=):
* dfg/DFGNodeType.h:
* dfg/DFGOpInfo.h:
(JSC::DFG::OpInfo::OpInfo):
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::allocateTemporaryRegistersForPatchpoint):
(JSC::DFG::SpeculativeJIT::compileCallDOM):
(JSC::DFG::SpeculativeJIT::compileCheckDOM):
* dfg/DFGSpeculativeJIT.h:
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGStructureAbstractValue.cpp:
(JSC::DFG::StructureAbstractValue::filterClassInfoSlow):
(JSC::DFG::StructureAbstractValue::isSubClassOf):
* dfg/DFGStructureAbstractValue.h:
(JSC::DFG::StructureAbstractValue::filterClassInfo):
(JSC::DFG::StructureAbstractValue::filter): Deleted.
* domjit/DOMJITPatchpointParams.h: Copied from Source/JavaScriptCore/dfg/DFGOpInfo.h.
(JSC::DOMJIT::PatchpointParams::~PatchpointParams):
(JSC::DOMJIT::PatchpointParams::size):
(JSC::DOMJIT::PatchpointParams::at):
(JSC::DOMJIT::PatchpointParams::operator[]):
(JSC::DOMJIT::PatchpointParams::gpScratch):
(JSC::DOMJIT::PatchpointParams::fpScratch):
(JSC::DOMJIT::PatchpointParams::PatchpointParams):
* domjit/DOMJITReg.h: Added.
(JSC::DOMJIT::Reg::Reg):
(JSC::DOMJIT::Reg::isGPR):
(JSC::DOMJIT::Reg::isFPR):
(JSC::DOMJIT::Reg::isJSValueRegs):
(JSC::DOMJIT::Reg::gpr):
(JSC::DOMJIT::Reg::fpr):
(JSC::DOMJIT::Reg::jsValueRegs):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileCheckDOM):
(JSC::FTL::DFG::LowerDFGToB3::compileCallDOM):
(JSC::FTL::DFG::LowerDFGToB3::compileUnreachable): Deleted.
* jit/AssemblyHelpers.h:
* jsc.cpp:
(WTF::DOMJITNode::DOMJITNode):
(WTF::DOMJITNode::createStructure):
(WTF::DOMJITNode::create):
(WTF::DOMJITNode::value):
(WTF::DOMJITNode::offsetOfValue):
(WTF::DOMJITGetter::DOMJITGetter):
(WTF::DOMJITGetter::createStructure):
(WTF::DOMJITGetter::create):
(WTF::DOMJITGetter::DOMJITNodeDOMJIT::DOMJITNodeDOMJIT):
(WTF::DOMJITGetter::domJITNodeGetterSetter):
(WTF::DOMJITGetter::finishCreation):
(WTF::DOMJITGetter::customGetter):
(GlobalObject::finishCreation):
(functionCreateDOMJITNodeObject):
(functionCreateDOMJITGetterObject):

Source/WTF:

* wtf/Box.h:
(WTF::Box::Box):

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

40 files changed:
JSTests/ChangeLog
JSTests/stress/domjit-getter-poly.js [new file with mode: 0644]
JSTests/stress/domjit-getter-proto.js [new file with mode: 0644]
JSTests/stress/domjit-getter-super-poly.js [new file with mode: 0644]
JSTests/stress/domjit-getter.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/b3/B3Effects.h
Source/JavaScriptCore/b3/B3Value.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.h
Source/JavaScriptCore/bytecode/SpeculatedType.cpp
Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGAbstractValue.cpp
Source/JavaScriptCore/dfg/DFGAbstractValue.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGOpInfo.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/dfg/DFGStructureAbstractValue.cpp
Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h
Source/JavaScriptCore/domjit/DOMJITPatchpointParams.h [new file with mode: 0644]
Source/JavaScriptCore/domjit/DOMJITReg.h [new file with mode: 0644]
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/AssemblyHelpers.h
Source/JavaScriptCore/jsc.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/Box.h

index c2113d0..3e6c51b 100644 (file)
@@ -1,3 +1,23 @@
+2016-10-05  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Add initial CheckDOM and CallDOM implementations
+        https://bugs.webkit.org/show_bug.cgi?id=162941
+
+        Reviewed by Filip Pizlo.
+
+        * stress/domjit-getter-poly.js: Added.
+        (shouldBe):
+        (access):
+        * stress/domjit-getter-proto.js: Added.
+        (shouldBe):
+        (access):
+        * stress/domjit-getter-super-poly.js: Added.
+        (shouldBe):
+        (access):
+        * stress/domjit-getter.js: Added.
+        (shouldBe):
+        (access):
+
 2016-10-04  Saam Barati  <sbarati@apple.com>
 
         String.prototype.toLowerCase should be a DFG/FTL intrinsic
diff --git a/JSTests/stress/domjit-getter-poly.js b/JSTests/stress/domjit-getter-poly.js
new file mode 100644 (file)
index 0000000..ce311ef
--- /dev/null
@@ -0,0 +1,19 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+var domjit1 = createDOMJITGetterObject();
+var domjit2 = createDOMJITGetterObject();
+
+function access(domjit)
+{
+    return domjit.customGetter + domjit.customGetter;
+}
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(access((i & 0x1) ? domjit1 : domjit2), 84);
+
+shouldBe(access({ customGetter: 42 }), 84);
+domjit1.test = 44;
+shouldBe(access(domjit1), 84);
diff --git a/JSTests/stress/domjit-getter-proto.js b/JSTests/stress/domjit-getter-proto.js
new file mode 100644 (file)
index 0000000..c3ba884
--- /dev/null
@@ -0,0 +1,17 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+var domjit = createDOMJITNodeObject();
+function access(domjit)
+{
+    return domjit.customGetter + domjit.customGetter;
+}
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(access(domjit), 84);
+
+shouldBe(access({ customGetter: 42 }), 84);
+domjit.test = 44;
+shouldBe(access(domjit), 84);
diff --git a/JSTests/stress/domjit-getter-super-poly.js b/JSTests/stress/domjit-getter-super-poly.js
new file mode 100644 (file)
index 0000000..5487dd3
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+var domjits = [];
+for (var i = 0; i < 100; ++i)
+    domjits.push(createDOMJITGetterObject());
+
+function access(domjit)
+{
+    return domjit.customGetter + domjit.customGetter;
+}
+
+for (var i = 0; i < 1e2; ++i) {
+    for (var j = 0; j < domjits.length; ++j)
+        shouldBe(access(domjits[j]), 84);
+}
diff --git a/JSTests/stress/domjit-getter.js b/JSTests/stress/domjit-getter.js
new file mode 100644 (file)
index 0000000..1ba2884
--- /dev/null
@@ -0,0 +1,18 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+var domjit = createDOMJITGetterObject();
+
+function access(domjit)
+{
+    return domjit.customGetter + domjit.customGetter;
+}
+
+for (var i = 0; i < 1e4; ++i)
+    shouldBe(access(domjit), 84);
+
+shouldBe(access({ customGetter: 42 }), 84);
+domjit.test = 44;
+shouldBe(access(domjit), 84);
index 6d875a3..dd71a0e 100644 (file)
@@ -1,5 +1,150 @@
 2016-10-05  Yusuke Suzuki  <utatane.tea@gmail.com>
 
+        [DOMJIT] Add initial CheckDOM and CallDOM implementations
+        https://bugs.webkit.org/show_bug.cgi?id=162941
+
+        Reviewed by Filip Pizlo.
+
+        This patch implements a prototype of DOMJIT accelerated getter.
+        We add two new DFG nodes, CheckDOM and CallDOM.
+
+        CheckDOM is used to filter inappropriate |this| object for DOM getter. Its functionality
+        is equivalent to jsDynamicCast's Check. You can use like "CheckDOM, @1, JSNode::info()",
+        and this CheckDOM incurs a BadType exit if the class of the given @1 is not a subclass of
+        JSNode::info().
+
+        CallDOM is used to emit actual DOM operations. It takes GlobalObject and checked DOM
+        object. And it returns JSValue as its result.
+
+        Both CheckDOM and CallDOM can take a DOMJIT::Patchpoint. This is somewhat code snippet
+        generator, and is injectable to DFG and FTL. DFG and FTL set up registers correctly
+        according to DOMJIT::Patchpoint's requirement and invoke this patchpoint generator to emit code.
+        While CallDOM always requires a patchpoint, ideally CheckDOM does not require it since
+        isSubclassOf check can be implemented in DFG / FTL side. However, some classes have a
+        faster way to query isSubclassOf. For example, JSNode in WebCore introduces a special
+        JSType to optimize this query. CheckDOM's patchpoint gives us a chance to emit special
+        faster code for such a case.
+
+        By leveraging above nodes, we can construct DOMJIT accelerated getter. When DFG recognizes the
+        given getter call is CustomGetter and it has DOMJIT::GetterSetter information, DFG emits the above nodes.
+        We implemented a prototype in jsc.cpp shell as DOMJITGetter to test the functionality.
+
+        Notes about the future extensions.
+
+        1. Currently, we do not allow CallDOM to emit any function calls. This will be extended by
+           adding `addSlowPathCall` functionality to DOMJIT::Patchpoint later. Interesting thing is that
+           we need to create an abstraction over DFG slow path call and FTL slow path call!
+
+        2. CheckDOM is not handled in DFGTypeCheckHoistingPhase yet. And we have a chance to merge several CheckDOM into one.
+           For example, given CheckDOM A and CheckDOM B to the same target. If A is subclass of B, we can merge them to CheckDOM A.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * b3/B3Effects.h:
+        (JSC::B3::Effects::forCheck):
+        * b3/B3Value.cpp:
+        (JSC::B3::Value::effects):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):
+        (JSC::GetByIdStatus::makesCalls):
+        (JSC::GetByIdStatus::dump):
+        * bytecode/GetByIdStatus.h:
+        (JSC::GetByIdStatus::GetByIdStatus):
+        (JSC::GetByIdStatus::isCustom):
+        (JSC::GetByIdStatus::takesSlowPath):
+        (JSC::GetByIdStatus::isSimple): Deleted.
+        * bytecode/SpeculatedType.cpp:
+        (JSC::speculationFromClassInfo):
+        * dfg/DFGAbstractInterpreter.h:
+        (JSC::DFG::AbstractInterpreter::filterClassInfo):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::filterClassInfo):
+        * dfg/DFGAbstractValue.cpp:
+        (JSC::DFG::AbstractValue::filterClassInfo):
+        * dfg/DFGAbstractValue.h:
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::handleDOMJITGetter):
+        (JSC::DFG::ByteCodeParser::handleGetById):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGConstantFoldingPhase.cpp:
+        (JSC::DFG::ConstantFoldingPhase::foldConstants):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNode.h:
+        (JSC::DFG::Node::hasHeapPrediction):
+        (JSC::DFG::Node::hasDOMJIT):
+        (JSC::DFG::Node::domJIT):
+        (JSC::DFG::Node::hasClassInfo):
+        (JSC::DFG::Node::classInfo):
+        (JSC::DFG::Node::OpInfoWrapper::OpInfoWrapper):
+        (JSC::DFG::Node::OpInfoWrapper::operator=):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGOpInfo.h:
+        (JSC::DFG::OpInfo::OpInfo):
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT.cpp:
+        (JSC::DFG::allocateTemporaryRegistersForPatchpoint):
+        (JSC::DFG::SpeculativeJIT::compileCallDOM):
+        (JSC::DFG::SpeculativeJIT::compileCheckDOM):
+        * dfg/DFGSpeculativeJIT.h:
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGStructureAbstractValue.cpp:
+        (JSC::DFG::StructureAbstractValue::filterClassInfoSlow):
+        (JSC::DFG::StructureAbstractValue::isSubClassOf):
+        * dfg/DFGStructureAbstractValue.h:
+        (JSC::DFG::StructureAbstractValue::filterClassInfo):
+        (JSC::DFG::StructureAbstractValue::filter): Deleted.
+        * domjit/DOMJITPatchpointParams.h: Copied from Source/JavaScriptCore/dfg/DFGOpInfo.h.
+        (JSC::DOMJIT::PatchpointParams::~PatchpointParams):
+        (JSC::DOMJIT::PatchpointParams::size):
+        (JSC::DOMJIT::PatchpointParams::at):
+        (JSC::DOMJIT::PatchpointParams::operator[]):
+        (JSC::DOMJIT::PatchpointParams::gpScratch):
+        (JSC::DOMJIT::PatchpointParams::fpScratch):
+        (JSC::DOMJIT::PatchpointParams::PatchpointParams):
+        * domjit/DOMJITReg.h: Added.
+        (JSC::DOMJIT::Reg::Reg):
+        (JSC::DOMJIT::Reg::isGPR):
+        (JSC::DOMJIT::Reg::isFPR):
+        (JSC::DOMJIT::Reg::isJSValueRegs):
+        (JSC::DOMJIT::Reg::gpr):
+        (JSC::DOMJIT::Reg::fpr):
+        (JSC::DOMJIT::Reg::jsValueRegs):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCheckDOM):
+        (JSC::FTL::DFG::LowerDFGToB3::compileCallDOM):
+        (JSC::FTL::DFG::LowerDFGToB3::compileUnreachable): Deleted.
+        * jit/AssemblyHelpers.h:
+        * jsc.cpp:
+        (WTF::DOMJITNode::DOMJITNode):
+        (WTF::DOMJITNode::createStructure):
+        (WTF::DOMJITNode::create):
+        (WTF::DOMJITNode::value):
+        (WTF::DOMJITNode::offsetOfValue):
+        (WTF::DOMJITGetter::DOMJITGetter):
+        (WTF::DOMJITGetter::createStructure):
+        (WTF::DOMJITGetter::create):
+        (WTF::DOMJITGetter::DOMJITNodeDOMJIT::DOMJITNodeDOMJIT):
+        (WTF::DOMJITGetter::domJITNodeGetterSetter):
+        (WTF::DOMJITGetter::finishCreation):
+        (WTF::DOMJITGetter::customGetter):
+        (GlobalObject::finishCreation):
+        (functionCreateDOMJITNodeObject):
+        (functionCreateDOMJITGetterObject):
+
+2016-10-05  Yusuke Suzuki  <utatane.tea@gmail.com>
+
         [JSC] Do not construct Simple GetByIdStatus against self-custom-accessor case
         https://bugs.webkit.org/show_bug.cgi?id=162993
 
index dc4426e..b697e61 100644 (file)
                E35E03601B7AB43E0073AD2A /* InspectorInstrumentationObject.h in Headers */ = {isa = PBXBuildFile; fileRef = E35E035E1B7AB43E0073AD2A /* InspectorInstrumentationObject.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3794E751B77EB97005543AE /* ModuleAnalyzer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3794E731B77EB97005543AE /* ModuleAnalyzer.cpp */; };
                E3794E761B77EB97005543AE /* ModuleAnalyzer.h in Headers */ = {isa = PBXBuildFile; fileRef = E3794E741B77EB97005543AE /* ModuleAnalyzer.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E37AD83C1DA4928600F3D412 /* DOMJITPatchpointParams.h in Headers */ = {isa = PBXBuildFile; fileRef = E37AD83A1DA4928000F3D412 /* DOMJITPatchpointParams.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               E37AD83D1DA4928600F3D412 /* DOMJITReg.h in Headers */ = {isa = PBXBuildFile; fileRef = E37AD83B1DA4928000F3D412 /* DOMJITReg.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E3963CEE1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3963CEC1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp */; };
                E39D45F51D39005600B3B377 /* InterpreterInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = E39D9D841D39000600667282 /* InterpreterInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E39DA4A61B7E8B7C0084F33A /* JSModuleRecord.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */; };
                E35E03611B7AB4850073AD2A /* InspectorInstrumentationObject.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = InspectorInstrumentationObject.js; sourceTree = "<group>"; };
                E3794E731B77EB97005543AE /* ModuleAnalyzer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ModuleAnalyzer.cpp; sourceTree = "<group>"; };
                E3794E741B77EB97005543AE /* ModuleAnalyzer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ModuleAnalyzer.h; sourceTree = "<group>"; };
+               E37AD83A1DA4928000F3D412 /* DOMJITPatchpointParams.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITPatchpointParams.h; sourceTree = "<group>"; };
+               E37AD83B1DA4928000F3D412 /* DOMJITReg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DOMJITReg.h; sourceTree = "<group>"; };
                E3963CEC1B73F75000EB4CE5 /* NodesAnalyzeModule.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NodesAnalyzeModule.cpp; sourceTree = "<group>"; };
                E39D9D841D39000600667282 /* InterpreterInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InterpreterInlines.h; sourceTree = "<group>"; };
                E39DA4A41B7E8B7C0084F33A /* JSModuleRecord.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSModuleRecord.cpp; sourceTree = "<group>"; };
                        children = (
                                E3FF752F1D9CEA1200C7E16D /* DOMJITGetterSetter.h */,
                                E3C08E3B1DA41B7B0039478F /* DOMJITPatchpoint.h */,
+                               E37AD83A1DA4928000F3D412 /* DOMJITPatchpointParams.h */,
+                               E37AD83B1DA4928000F3D412 /* DOMJITReg.h */,
                        );
                        path = domjit;
                        sourceTree = "<group>";
                                E39D45F51D39005600B3B377 /* InterpreterInlines.h in Headers */,
                                0FFB921C16D02F110055A5DB /* DFGOSRExitCompilationInfo.h in Headers */,
                                0FC0977114693AF500CF2442 /* DFGOSRExitCompiler.h in Headers */,
+                               E37AD83D1DA4928600F3D412 /* DOMJITReg.h in Headers */,
                                0F7025AA1714B0FC00382C0E /* DFGOSRExitCompilerCommon.h in Headers */,
                                0F392C8A1B46188400844728 /* DFGOSRExitFuzz.h in Headers */,
                                0FEFC9AB1681A3B600567F53 /* DFGOSRExitJumpPlaceholder.h in Headers */,
                                9959E92E1BD17FA4001AA413 /* xxd.pl in Headers */,
                                79B1788E1D399B8000B1A567 /* JITMathICForwards.h in Headers */,
                                451539B912DC994500EF7AC4 /* Yarr.h in Headers */,
+                               E37AD83C1DA4928600F3D412 /* DOMJITPatchpointParams.h in Headers */,
                                86704B8512DBA33700A9FE7B /* YarrInterpreter.h in Headers */,
                                86704B8712DBA33700A9FE7B /* YarrJIT.h in Headers */,
                                0FB4767F1D99AEAD008EA6CB /* GCDeferralContextInlines.h in Headers */,
index c04748f..bec4786 100644 (file)
@@ -78,6 +78,15 @@ struct Effects {
         return result;
     }
 
+    static Effects forCheck()
+    {
+        Effects result;
+        result.exitsSideways = true;
+        // The program could read anything after exiting, and it's on us to declare this.
+        result.reads = HeapRange::top();
+        return result;
+    }
+
     bool mustExecute() const
     {
         return terminal || exitsSideways || writesLocalState || writes;
index 161aced..a087f51 100644 (file)
@@ -614,9 +614,7 @@ Effects Value::effects() const
     case CheckSub:
     case CheckMul:
     case Check:
-        result.exitsSideways = true;
-        // The program could read anything after exiting, and it's on us to declare this.
-        result.reads = HeapRange::top();
+        result = Effects::forCheck();
         break;
     case Upsilon:
     case Set:
index 39466dd..18c1c7e 100644 (file)
@@ -246,6 +246,7 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
                     domJIT = access.domJIT();
                     if (!domJIT)
                         return GetByIdStatus(slowPathState, true);
+                    result.m_state = Custom;
                     break;
                 }
                 default: {
@@ -263,6 +264,16 @@ GetByIdStatus GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback(
 
                 if (!result.appendVariant(variant))
                     return GetByIdStatus(slowPathState, true);
+
+                if (domJIT) {
+                    // Give up when cutom accesses are not merged into one.
+                    if (result.numVariants() != 1)
+                        return GetByIdStatus(slowPathState, true);
+                } else {
+                    // Give up when custom access and simple access are mixed.
+                    if (result.m_state == Custom)
+                        return GetByIdStatus(slowPathState, true);
+                }
                 break;
             } }
         }
@@ -362,6 +373,7 @@ bool GetByIdStatus::makesCalls() const
     switch (m_state) {
     case NoInformation:
     case TakesSlowPath:
+    case Custom:
         return false;
     case Simple:
         for (unsigned i = m_variants.size(); i--;) {
@@ -403,6 +415,9 @@ void GetByIdStatus::dump(PrintStream& out) const
     case Simple:
         out.print("Simple");
         break;
+    case Custom:
+        out.print("Custom");
+        break;
     case TakesSlowPath:
         out.print("TakesSlowPath");
         break;
index f49e153..02b0d26 100644 (file)
@@ -44,6 +44,7 @@ public:
         NoInformation,  // It's uncached so we have no information.
         Simple,         // It's cached for a simple access to a known object property with
                         // a possible structure chain and a possible specific value.
+        Custom,         // It's cached for a custom accessor with a possible structure chain.
         TakesSlowPath,  // It's known to often take slow path.
         MakesCalls      // It's known to take paths that make calls.
     };
@@ -64,7 +65,7 @@ public:
         : m_state(state)
         , m_wasSeenInJIT(wasSeenInJIT)
     {
-        ASSERT((state == Simple) == variant.isSet());
+        ASSERT((state == Simple || state == Custom) == variant.isSet());
         m_variants.append(variant);
     }
     
@@ -82,13 +83,14 @@ public:
     bool isSet() const { return m_state != NoInformation; }
     bool operator!() const { return !isSet(); }
     bool isSimple() const { return m_state == Simple; }
+    bool isCustom() const { return m_state == Custom; }
 
     size_t numVariants() const { return m_variants.size(); }
     const Vector<GetByIdVariant, 1>& variants() const { return m_variants; }
     const GetByIdVariant& at(size_t index) const { return m_variants[index]; }
     const GetByIdVariant& operator[](size_t index) const { return at(index); }
 
-    bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls; }
+    bool takesSlowPath() const { return m_state == TakesSlowPath || m_state == MakesCalls || m_state == Custom; }
     bool makesCalls() const;
     
     bool wasSeenInJIT() const { return m_wasSeenInJIT; }
index 719d8f2..2425292 100644 (file)
@@ -353,6 +353,12 @@ SpeculatedType speculationFromTypedArrayType(TypedArrayType type)
 
 SpeculatedType speculationFromClassInfo(const ClassInfo* classInfo)
 {
+    if (classInfo == JSString::info())
+        return SpecString;
+
+    if (classInfo == Symbol::info())
+        return SpecSymbol;
+
     if (classInfo == JSFinalObject::info())
         return SpecFinalObject;
     
@@ -385,6 +391,9 @@ SpeculatedType speculationFromClassInfo(const ClassInfo* classInfo)
     
     if (isTypedView(classInfo->typedArrayStorageType))
         return speculationFromTypedArrayType(classInfo->typedArrayStorageType);
+
+    if (classInfo->isSubClassOf(JSArray::info()))
+        return SpecDerivedArray;
     
     if (classInfo->isSubClassOf(JSObject::info()))
         return SpecObjectOther;
index 5972816..bcf6d7e 100644 (file)
@@ -134,10 +134,17 @@ public:
         return filterByValue(forNode(node), value);
     }
     
+    template<typename T>
+    FiltrationResult filterClassInfo(T node, const ClassInfo* classInfo)
+    {
+        return filterClassInfo(forNode(node), classInfo);
+    }
+
     FiltrationResult filter(AbstractValue&, const StructureSet&, SpeculatedType admittedTypes = SpecNone);
     FiltrationResult filterArrayModes(AbstractValue&, ArrayModes);
     FiltrationResult filter(AbstractValue&, SpeculatedType);
     FiltrationResult filterByValue(AbstractValue&, FrozenValue);
+    FiltrationResult filterClassInfo(AbstractValue&, const ClassInfo*);
     
     PhiChildren* phiChildren() { return m_phiChildren.get(); }
     
index 2d50c64..9f5f0e0 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "ArrayConstructor.h"
 #include "DFGAbstractInterpreter.h"
+#include "DOMJITGetterSetter.h"
 #include "GetByIdStatus.h"
 #include "GetterSetter.h"
 #include "HashMapImpl.h"
@@ -2268,6 +2269,28 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
     case ReallocatePropertyStorage:
         forNode(node).clear(); // The result is not a JS value.
         break;
+    case CheckDOM: {
+        JSValue constant = forNode(node->child1()).value();
+        if (constant) {
+            if (constant.isCell() && constant.asCell()->inherits(node->classInfo())) {
+                m_state.setFoundConstants(true);
+                ASSERT(constant);
+                break;
+            }
+        }
+
+        AbstractValue& value = forNode(node->child1());
+
+        if (value.m_structure.isSubClassOf(node->classInfo()))
+            m_state.setFoundConstants(true);
+
+        filterClassInfo(value, node->classInfo());
+        break;
+    }
+    case CallDOM:
+        clobberWorld(node->origin.semantic, clobberLimit);
+        forNode(node).makeBytecodeTop();
+        break;
     case CheckArray: {
         if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) {
             m_state.setFoundConstants(true);
@@ -3056,6 +3079,16 @@ FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue(
 }
 
 template<typename AbstractStateType>
+FiltrationResult AbstractInterpreter<AbstractStateType>::filterClassInfo(
+    AbstractValue& value, const ClassInfo* classInfo)
+{
+    if (value.filterClassInfo(m_graph, classInfo) == FiltrationOK)
+        return FiltrationOK;
+    m_state.setIsValid(false);
+    return Contradiction;
+}
+
+template<typename AbstractStateType>
 void AbstractInterpreter<AbstractStateType>::executeDoubleUnaryOpEffects(Node* node, double(*equivalentFunction)(double))
 {
     JSValue child = forNode(node->child1()).value();
index c3efdac..b287c27 100644 (file)
@@ -306,6 +306,23 @@ FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes)
     return normalizeClarity();
 }
 
+FiltrationResult AbstractValue::filterClassInfo(Graph& graph, const ClassInfo* classInfo)
+{
+    // FIXME: AI should track ClassInfo to leverage hierarchical class information.
+    // https://bugs.webkit.org/show_bug.cgi?id=162989
+    if (isClear())
+        return FiltrationOK;
+
+    m_type &= speculationFromClassInfo(classInfo);
+    m_structure.filterClassInfo(classInfo);
+
+    m_structure.filter(m_type);
+
+    filterArrayModesByType();
+    filterValueByType();
+    return normalizeClarity(graph);
+}
+
 FiltrationResult AbstractValue::filter(SpeculatedType type)
 {
     if ((m_type & type) == m_type)
index 32eb0f1..8a91014 100644 (file)
@@ -307,6 +307,7 @@ struct AbstractValue {
     FiltrationResult filter(SpeculatedType);
     FiltrationResult filterByValue(const FrozenValue& value);
     FiltrationResult filter(const AbstractValue&);
+    FiltrationResult filterClassInfo(Graph&, const ClassInfo*);
 
     FiltrationResult filter(Graph&, const InferredType::Descriptor&);
     
index dd70e3d..8cdf50a 100644 (file)
@@ -219,6 +219,7 @@ private:
     bool handleConstantInternalFunction(Node* callTargetNode, int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind, SpeculatedType, const ChecksFunctor& insertChecks);
     Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, const InferredType::Descriptor&, Node* value);
     Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, const InferredType::Descriptor&, NodeType = GetByOffset);
+    bool handleDOMJITGetter(int resultOperand, const GetByIdVariant&, Node* thisNode, SpeculatedType prediction);
 
     // Create a presence ObjectPropertyCondition based on some known offset and structure set. Does not
     // check the validity of the condition, but it may return a null one if it encounters a contradiction.
@@ -2673,6 +2674,28 @@ bool ByteCodeParser::handleIntrinsicGetter(int resultOperand, const GetByIdVaria
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+bool ByteCodeParser::handleDOMJITGetter(int resultOperand, const GetByIdVariant& variant, Node* thisNode, SpeculatedType prediction)
+{
+    if (!variant.domJIT())
+        return false;
+
+    DOMJIT::GetterSetter* domJIT = variant.domJIT();
+
+    // We do not need to actually look up CustomGetterSetter here. Checking Structures or registering watchpoints are enough,
+    // since replacement of CustomGetterSetter always incurs Structure transition.
+    if (!check(variant.conditionSet()))
+        return false;
+    addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), thisNode);
+
+    // We do not need to emit CheckCell thingy here. When the custom accessor is replaced to different one, Structure transition occurs.
+    addToGraph(CheckDOM, OpInfo(domJIT), OpInfo(domJIT->thisClassInfo()), thisNode);
+    Node* globalObject = addToGraph(GetGlobalObject, thisNode);
+    addVarArgChild(globalObject); // GlobalObject of thisNode is always used to create a DOMWrapper.
+    addVarArgChild(thisNode);
+    set(VirtualRegister(resultOperand), addToGraph(Node::VarArg, CallDOM, OpInfo(domJIT), OpInfo(prediction)));
+    return true;
+}
+
 template<typename ChecksFunctor>
 bool ByteCodeParser::handleTypedArrayConstructor(
     int resultOperand, InternalFunction* function, int registerOffset,
@@ -3267,6 +3290,20 @@ void ByteCodeParser::handleGetById(
     else
         getById = TryGetById;
 
+    // Special path for custom accessors since custom's offset does not have any meanings.
+    // So, this is completely different from Simple one. But we have a chance to optimize it when we use DOMJIT.
+    if (getByIdStatus.isCustom()) {
+        ASSERT(getByIdStatus.numVariants() == 1);
+        ASSERT(!getByIdStatus.makesCalls());
+        GetByIdVariant variant = getByIdStatus[0];
+        ASSERT(variant.domJIT());
+        if (handleDOMJITGetter(destinationOperand, variant, base, prediction)) {
+            if (m_graph.compilation())
+                m_graph.compilation()->noticeInlinedGetById();
+            return;
+        }
+    }
+
     ASSERT(type == AccessType::Get || !getByIdStatus.makesCalls());
     if (!getByIdStatus.isSimple() || !getByIdStatus.numVariants() || !Options::useAccessInlining()) {
         set(VirtualRegister(destinationOperand),
index ed23afa..179963d 100644 (file)
@@ -885,6 +885,15 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
         def(HeapLocation(ButterflyLoc, JSObject_butterfly, node->child1()), LazyNode(node));
         return;
 
+    case CheckDOM:
+        def(PureValue(node, node->classInfo()));
+        return;
+
+    case CallDOM:
+        read(World);
+        write(Heap);
+        return;
+
     case Arrayify:
     case ArrayifyToStructure:
         read(JSCell_structureID);
index 5d6329d..d9bc7e7 100644 (file)
@@ -158,6 +158,28 @@ private:
                 }
                 break;
             }
+
+            case CheckDOM: {
+                JSValue constant = m_state.forNode(node->child1()).value();
+                if (constant) {
+                    if (constant.isCell() && constant.asCell()->inherits(node->classInfo())) {
+                        m_interpreter.execute(indexInBlock);
+                        node->remove();
+                        eliminated = true;
+                        break;
+                    }
+                }
+
+                AbstractValue& value = m_state.forNode(node->child1());
+
+                if (value.m_structure.isSubClassOf(node->classInfo())) {
+                    m_interpreter.execute(indexInBlock);
+                    node->remove();
+                    eliminated = true;
+                    break;
+                }
+                break;
+            }
                 
             case GetIndexedPropertyStorage: {
                 JSArrayBufferView* view = m_graph.tryGetFoldableView(
index 9f13c73..0711de7 100644 (file)
@@ -118,6 +118,8 @@ bool doesGC(Graph& graph, Node* node)
     case CheckStructure:
     case GetExecutable:
     case GetButterfly:
+    case CallDOM:
+    case CheckDOM:
     case CheckArray:
     case GetScope:
     case SkipScope:
index d058949..b53ac88 100644 (file)
@@ -1699,6 +1699,15 @@ private:
             break;
         }
 
+        case CheckDOM:
+            fixEdge<CellUse>(node->child1());
+            break;
+
+        case CallDOM:
+            fixEdge<KnownCellUse>(m_graph.varArgChild(node, 0)); // GlobalObject.
+            fixEdge<CellUse>(m_graph.varArgChild(node, 1)); // DOM.
+            break;
+
 #if !ASSERT_DISABLED
         // Have these no-op cases here to ensure that nobody forgets to add handlers for new opcodes.
         case SetArgument:
index 99a82c9..4237a52 100644 (file)
@@ -1446,6 +1446,7 @@ public:
         case StringReplaceRegExp:
         case ToNumber:
         case LoadFromJSMapBucket:
+        case CallDOM:
             return true;
         default:
             return false;
@@ -2312,7 +2313,28 @@ public:
         ASSERT(hasBasicBlockLocation());
         return m_opInfo.as<BasicBlockLocation*>();
     }
-    
+
+    bool hasDOMJIT() const
+    {
+        return op() == CheckDOM || op() == CallDOM;
+    }
+
+    DOMJIT::GetterSetter* domJIT()
+    {
+        ASSERT(hasDOMJIT());
+        return m_opInfo.as<DOMJIT::GetterSetter*>();
+    }
+
+    bool hasClassInfo() const
+    {
+        return op() == CheckDOM;
+    }
+
+    const ClassInfo* classInfo()
+    {
+        return m_opInfo2.as<const ClassInfo*>();
+    }
+
     Node* replacement() const
     {
         return m_misc.replacement;
@@ -2392,6 +2414,11 @@ private:
             u.int64 = 0;
             u.pointer = pointer;
         }
+        OpInfoWrapper(const void* constPointer)
+        {
+            u.int64 = 0;
+            u.constPointer = constPointer;
+        }
         OpInfoWrapper& operator=(uint32_t int32)
         {
             u.int64 = 0;
@@ -2415,12 +2442,23 @@ private:
             u.pointer = pointer;
             return *this;
         }
+        OpInfoWrapper& operator=(const void* constPointer)
+        {
+            u.int64 = 0;
+            u.constPointer = constPointer;
+            return *this;
+        }
         template <typename T>
-        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_pointer<T>::value, T>::type
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_pointer<T>::value && !std::is_const<typename std::remove_pointer<T>::type>::value, T>::type
         {
             return static_cast<T>(u.pointer);
         }
         template <typename T>
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_pointer<T>::value && std::is_const<typename std::remove_pointer<T>::type>::value, T>::type
+        {
+            return static_cast<T>(u.constPointer);
+        }
+        template <typename T>
         ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_integral<T>::value && sizeof(T) == 4, T>::type
         {
             return u.int32;
@@ -2434,6 +2472,7 @@ private:
             uint32_t int32;
             uint64_t int64;
             void* pointer;
+            const void* constPointer;
         } u;
     };
     OpInfoWrapper m_opInfo;
index 8eaf724..714b8b2 100644 (file)
@@ -398,6 +398,9 @@ namespace JSC { namespace DFG {
     macro(IsNonEmptyMapBucket, NodeResultBoolean) \
     \
     macro(ToLowerCase, NodeResultJS) \
+    /* Nodes for DOM JIT */\
+    macro(CheckDOM, NodeMustGenerate) \
+    macro(CallDOM, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
 
 // This enum generates a monotonically increasing id for all Node types,
 // and is used by the subsequent enum to fill out the id (as accessed via the NodeIdMask).
index d47a970..57f3329 100644 (file)
@@ -43,6 +43,7 @@ struct OpInfo {
     explicit OpInfo(uintptr_t value) : m_value(static_cast<uint64_t>(value)) { }
 #endif
     explicit OpInfo(void* value) : m_value(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value))) { }
+    explicit OpInfo(const void* value) : m_value(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(value))) { }
     uint64_t m_value;
 };
 
index 56ba571..bea3616 100644 (file)
@@ -699,7 +699,8 @@ private:
         case GetClosureVar:
         case GetFromArguments:
         case LoadFromJSMapBucket:
-        case ToNumber: {
+        case ToNumber:
+        case CallDOM: {
             setPrediction(m_currentNode->getHeapPrediction());
             break;
         }
@@ -824,6 +825,9 @@ private:
             break;
         }
 
+        case CheckDOM:
+            break;
+
         case CallObjectConstructor: {
             setPrediction(SpecObject);
             break;
index da82d7b..fa327c9 100644 (file)
@@ -218,6 +218,8 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case CheckStructure:
     case GetExecutable:
     case GetButterfly:
+    case CallDOM:
+    case CheckDOM:
     case CheckArray:
     case Arrayify:
     case ArrayifyToStructure:
index 239be9d..050f3f0 100644 (file)
@@ -38,6 +38,8 @@
 #include "DFGOSRExitFuzz.h"
 #include "DFGSaneStringGetByValSlowPathGenerator.h"
 #include "DFGSlowPathGenerator.h"
+#include "DOMJITPatchpoint.h"
+#include "DOMJITPatchpointParams.h"
 #include "DirectArguments.h"
 #include "JITAddGenerator.h"
 #include "JITBitAndGenerator.h"
@@ -7111,6 +7113,74 @@ void SpeculativeJIT::compileGetButterfly(Node* node)
     storageResult(resultGPR, node);
 }
 
+static void allocateTemporaryRegistersForPatchpoint(SpeculativeJIT* jit, Vector<GPRTemporary>& gpHolders, Vector<FPRTemporary>& fpHolders, Vector<GPRReg>& gpScratch, Vector<FPRReg>& fpScratch, DOMJIT::Patchpoint& patchpoint)
+{
+    for (unsigned i = 0; i < patchpoint.numGPScratchRegisters; ++i) {
+        GPRTemporary temporary(jit);
+        gpScratch.append(temporary.gpr());
+        gpHolders.append(WTFMove(temporary));
+    }
+
+    for (unsigned i = 0; i < patchpoint.numFPScratchRegisters; ++i) {
+        FPRTemporary temporary(jit);
+        fpScratch.append(temporary.fpr());
+        fpHolders.append(WTFMove(temporary));
+    }
+}
+
+void SpeculativeJIT::compileCallDOM(Node* node)
+{
+    Ref<DOMJIT::Patchpoint> patchpoint = node->domJIT()->callDOM();
+
+    Vector<GPRReg> gpScratch;
+    Vector<FPRReg> fpScratch;
+    Vector<DOMJIT::Reg> regs;
+
+    // FIXME: patchpoint should have a way to tell this can reuse "base" register.
+    // Teaching DFG about DOMJIT::Patchpoint clobber information is nice.
+    SpeculateCellOperand globalObject(this, m_jit.graph().varArgChild(node, 0));
+    SpeculateCellOperand base(this, m_jit.graph().varArgChild(node, 1));
+    JSValueRegsTemporary result(this);
+
+    regs.append(result.regs());
+    regs.append(globalObject.gpr());
+    regs.append(base.gpr());
+#if USE(JSVALUE64)
+    regs.append(static_cast<GPRReg>(GPRInfo::tagMaskRegister));
+    regs.append(static_cast<GPRReg>(GPRInfo::tagTypeNumberRegister));
+#endif
+
+    Vector<GPRTemporary> gpTempraries;
+    Vector<FPRTemporary> fpTempraries;
+    allocateTemporaryRegistersForPatchpoint(this, gpTempraries, fpTempraries, gpScratch, fpScratch, patchpoint.get());
+    DOMJIT::PatchpointParams params(WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
+    patchpoint->generator()->run(m_jit, params);
+    jsValueResult(result.regs(), node);
+}
+
+void SpeculativeJIT::compileCheckDOM(Node* node)
+{
+    // FIXME: We can add the fallback implementation that inlines jsDynamicCast things here.
+    Ref<DOMJIT::Patchpoint> patchpoint = node->domJIT()->checkDOM();
+
+    Vector<GPRReg> gpScratch;
+    Vector<FPRReg> fpScratch;
+    Vector<DOMJIT::Reg> regs;
+
+    SpeculateCellOperand base(this, node->child1());
+    GPRReg baseGPR = base.gpr();
+    regs.append(baseGPR);
+
+    Vector<GPRTemporary> gpTempraries;
+    Vector<FPRTemporary> fpTempraries;
+    allocateTemporaryRegistersForPatchpoint(this, gpTempraries, fpTempraries, gpScratch, fpScratch, patchpoint.get());
+
+    DOMJIT::PatchpointParams params(WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
+    CCallHelpers::JumpList failureCases = patchpoint->generator()->run(m_jit, params);
+    speculationCheck(BadType, JSValueSource::unboxedCell(baseGPR), node->child1(), failureCases);
+    noResult(node);
+}
+
 GPRReg SpeculativeJIT::temporaryRegisterForPutByVal(GPRTemporary& temporary, ArrayMode arrayMode)
 {
     if (!putByValWillNeedExtraRegister(arrayMode))
index 536ed79..0db6250 100644 (file)
@@ -2543,6 +2543,8 @@ public:
     void compileAllocatePropertyStorage(Node*);
     void compileReallocatePropertyStorage(Node*);
     void compileGetButterfly(Node*);
+    void compileCallDOM(Node*);
+    void compileCheckDOM(Node*);
     
 #if USE(JSVALUE32_64)
     template<typename BaseOperandType, typename PropertyOperandType, typename ValueOperandType, typename TagType>
index c95949e..9aa093a 100644 (file)
@@ -5435,6 +5435,14 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case CallDOM:
+        compileCallDOM(node);
+        break;
+
+    case CheckDOM:
+        compileCheckDOM(node);
+        break;
+
     case Unreachable:
         unreachable(node);
         break;
index 4dd21dd..c0cf423 100644 (file)
@@ -5566,6 +5566,14 @@ void SpeculativeJIT::compile(Node* node)
         compileMaterializeNewObject(node);
         break;
 
+    case CallDOM:
+        compileCallDOM(node);
+        break;
+
+    case CheckDOM:
+        compileCheckDOM(node);
+        break;
+
 #if ENABLE(FTL_JIT)        
     case CheckTierUpInLoop: {
         MacroAssembler::Jump callTierUp = m_jit.branchAdd32(
index 7a823ee..88d5775 100644 (file)
@@ -258,6 +258,15 @@ void StructureAbstractValue::filterSlow(SpeculatedType type)
         });
 }
 
+void StructureAbstractValue::filterClassInfoSlow(const ClassInfo* classInfo)
+{
+    ASSERT(!isTop());
+    m_set.genericFilter(
+        [&] (Structure* structure) {
+            return structure->classInfo()->isSubClassOf(classInfo);
+        });
+}
+
 bool StructureAbstractValue::contains(Structure* structure) const
 {
     if (isInfinite())
@@ -319,6 +328,19 @@ bool StructureAbstractValue::overlaps(const StructureAbstractValue& other) const
     return overlaps(other.m_set);
 }
 
+bool StructureAbstractValue::isSubClassOf(const ClassInfo* classInfo) const
+{
+    if (isInfinite())
+        return false;
+
+    // Note taht this function returns true if the structure set is empty.
+    for (const Structure* structure : m_set) {
+        if (!structure->classInfo()->isSubClassOf(classInfo))
+            return false;
+    }
+    return true;
+}
+
 bool StructureAbstractValue::equalsSlow(const StructureAbstractValue& other) const
 {
     ASSERT(m_set.m_pointer != other.m_set.m_pointer);
index ca94a79..51f7937 100644 (file)
@@ -163,6 +163,12 @@ public:
         if (isNeitherClearNorTop())
             filterSlow(type);
     }
+
+    ALWAYS_INLINE void filterClassInfo(const ClassInfo* classInfo)
+    {
+        if (isNeitherClearNorTop())
+            filterClassInfoSlow(classInfo);
+    }
     
     ALWAYS_INLINE bool operator==(const StructureAbstractValue& other) const
     {
@@ -231,6 +237,8 @@ public:
     
     bool overlaps(const StructureSet& other) const;
     bool overlaps(const StructureAbstractValue& other) const;
+
+    bool isSubClassOf(const ClassInfo*) const;
     
     void validateReferences(const TrackedReferences&) const;
     
@@ -241,6 +249,7 @@ private:
     static const unsigned clobberedSupremacyThreshold = 2;
     
     void filterSlow(SpeculatedType type);
+    void filterClassInfoSlow(const ClassInfo*);
     bool mergeSlow(const StructureAbstractValue& other);
     
     bool equalsSlow(const StructureAbstractValue& other) const;
diff --git a/Source/JavaScriptCore/domjit/DOMJITPatchpointParams.h b/Source/JavaScriptCore/domjit/DOMJITPatchpointParams.h
new file mode 100644 (file)
index 0000000..f238639
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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 "CCallHelpers.h"
+#include "DOMJITReg.h"
+#include "RegisterSet.h"
+
+namespace JSC { namespace DOMJIT {
+
+class PatchpointParams {
+WTF_MAKE_NONCOPYABLE(PatchpointParams);
+public:
+    virtual ~PatchpointParams() { }
+
+    unsigned size() const { return m_regs.size(); }
+    const Reg& at(unsigned index) const { return m_regs[index]; }
+    const Reg& operator[](unsigned index) const { return at(index); }
+
+    GPRReg gpScratch(unsigned index) const { return m_gpScratch[index]; }
+    FPRReg fpScratch(unsigned index) const { return m_fpScratch[index]; }
+
+    PatchpointParams(Vector<Reg>&& regs, Vector<GPRReg>&& gpScratch, Vector<FPRReg>&& fpScratch)
+        : m_regs(WTFMove(regs))
+        , m_gpScratch(WTFMove(gpScratch))
+        , m_fpScratch(WTFMove(fpScratch))
+    {
+    }
+
+private:
+
+    Vector<Reg> m_regs;
+    Vector<GPRReg> m_gpScratch;
+    Vector<FPRReg> m_fpScratch;
+};
+
+} }
+
+#endif
diff --git a/Source/JavaScriptCore/domjit/DOMJITReg.h b/Source/JavaScriptCore/domjit/DOMJITReg.h
new file mode 100644 (file)
index 0000000..b6801ef
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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 "Reg.h"
+#include <wtf/Variant.h>
+
+#if ENABLE(JIT)
+
+namespace JSC { namespace DOMJIT {
+
+// It is quite unfortunate that 32 bit environment exists on DFG! This means that JSValueRegs contains 2 registers
+// in such an environment. If we use GPRReg and FPRReg in DOMJITPatchpointParams, DOMJITPatchpointParams may contain
+// different number of registers in 32bit and 64bit environments when we pass JSValueRegs, it is confusing.
+// Therefore, we introduce an abstraction that DOMJIT::Reg, which is a polymorphic register class. It can refer FPRReg,
+// GPRReg, and "JSValueRegs". Note that isGPR() will return false if the target Reg is "JSValueRegs" even if the
+// environment is 64bit.
+//
+// FIXME: Eventually we should move this class into JSC and make is available for other JIT code.
+// https://bugs.webkit.org/show_bug.cgi?id=162990
+class Reg {
+public:
+    enum class Type : uint8_t {
+        GPR = 0,
+        FPR = 1,
+        JSValue = 2,
+    };
+
+    Reg(GPRReg reg)
+        : m_variant(reg)
+    {
+    }
+
+    Reg(FPRReg reg)
+        : m_variant(reg)
+    {
+    }
+
+    Reg(JSValueRegs regs)
+        : m_variant(regs)
+    {
+    }
+
+    bool isGPR() const { return m_variant.index() == static_cast<unsigned>(Type::GPR); }
+    bool isFPR() const { return m_variant.index() == static_cast<unsigned>(Type::FPR); }
+    bool isJSValueRegs() const { return m_variant.index() == static_cast<unsigned>(Type::JSValue); }
+
+    GPRReg gpr() const
+    {
+        ASSERT(isGPR());
+        return std::experimental::get<GPRReg>(m_variant);
+    }
+    FPRReg fpr() const
+    {
+        ASSERT(isFPR());
+        return std::experimental::get<FPRReg>(m_variant);
+    }
+    JSValueRegs jsValueRegs() const
+    {
+        ASSERT(isJSValueRegs());
+        return std::experimental::get<JSValueRegs>(m_variant);
+    }
+
+private:
+    std::experimental::variant<GPRReg, FPRReg, JSValueRegs> m_variant;
+};
+
+} }
+
+#endif
index fbf871c..e808f43 100644 (file)
@@ -266,6 +266,8 @@ inline CapabilityLevel canCompile(Node* node)
     case DefineDataProperty:
     case DefineAccessorProperty:
     case ToLowerCase:
+    case CheckDOM:
+    case CallDOM:
         // These are OK.
         break;
 
index 96fabaa..9889fb0 100644 (file)
@@ -44,6 +44,8 @@
 #include "DFGInPlaceAbstractState.h"
 #include "DFGOSRAvailabilityAnalysisPhase.h"
 #include "DFGOSRExitFuzz.h"
+#include "DOMJITPatchpoint.h"
+#include "DOMJITPatchpointParams.h"
 #include "DirectArguments.h"
 #include "FTLAbstractHeapRepository.h"
 #include "FTLAvailableRecovery.h"
@@ -1046,6 +1048,12 @@ private:
         case ToLowerCase:
             compileToLowerCase();
             break;
+        case CheckDOM:
+            compileCheckDOM();
+            break;
+        case CallDOM:
+            compileCallDOM();
+            break;
 
         case PhantomLocal:
         case LoopHint:
@@ -8712,6 +8720,94 @@ private:
         
         crash();
     }
+
+    void compileCheckDOM()
+    {
+        LValue cell = lowCell(m_node->child1());
+
+        RefPtr<DOMJIT::Patchpoint> domJIT = m_node->domJIT()->checkDOM();
+
+        PatchpointValue* patchpoint = m_out.patchpoint(Void);
+        patchpoint->appendSomeRegister(cell);
+        patchpoint->numGPScratchRegisters = domJIT->numGPScratchRegisters;
+        patchpoint->numFPScratchRegisters = domJIT->numFPScratchRegisters;
+
+        State* state = &m_ftlState;
+        NodeOrigin origin = m_origin;
+        unsigned osrExitArgumentOffset = patchpoint->numChildren();
+        OSRExitDescriptor* exitDescriptor = appendOSRExitDescriptor(jsValueValue(cell), m_node->child1().node());
+        patchpoint->appendColdAnys(buildExitArguments(exitDescriptor, origin.forExit, jsValueValue(cell)));
+
+        patchpoint->setGenerator(
+            [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                Vector<GPRReg> gpScratch;
+                Vector<FPRReg> fpScratch;
+                Vector<DOMJIT::Reg> regs;
+
+                regs.append(params[0].gpr());
+
+                for (unsigned i = 0; i < domJIT->numGPScratchRegisters; ++i)
+                    gpScratch.append(params.gpScratch(i));
+
+                for (unsigned i = 0; i < domJIT->numFPScratchRegisters; ++i)
+                    fpScratch.append(params.fpScratch(i));
+
+                RefPtr<OSRExitHandle> handle = exitDescriptor->emitOSRExitLater(*state, BadType, origin, params, osrExitArgumentOffset);
+
+                DOMJIT::PatchpointParams domJITParams(WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
+                CCallHelpers::JumpList failureCases = domJIT->generator()->run(jit, domJITParams);
+
+                jit.addLinkTask([=] (LinkBuffer& linkBuffer) {
+                    linkBuffer.link(failureCases, linkBuffer.locationOf(handle->label));
+                });
+            });
+        patchpoint->effects = Effects::forCheck();
+    }
+
+    void compileCallDOM()
+    {
+        LValue globalObject = lowCell(m_graph.varArgChild(m_node, 0));
+        LValue cell = lowCell(m_graph.varArgChild(m_node, 1));
+
+        RefPtr<DOMJIT::Patchpoint> domJIT = m_node->domJIT()->callDOM();
+        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));
+        RefPtr<PatchpointExceptionHandle> exceptionHandle = preparePatchpointForExceptions(patchpoint);
+        patchpoint->clobber(RegisterSet::macroScratchRegisters());
+        patchpoint->numGPScratchRegisters = domJIT->numGPScratchRegisters;
+        patchpoint->numFPScratchRegisters = domJIT->numFPScratchRegisters;
+
+        patchpoint->setGenerator(
+            [=] (CCallHelpers& jit, const StackmapGenerationParams& params) {
+                Vector<GPRReg> gpScratch;
+                Vector<FPRReg> fpScratch;
+                Vector<DOMJIT::Reg> regs;
+
+                // FIXME: patchpoint should have a way to tell this can reuse "base" register.
+                // Teaching DFG about DOMJIT::Patchpoint clobber information is nice.
+                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));
+
+                for (unsigned i = 0; i < domJIT->numFPScratchRegisters; ++i)
+                    fpScratch.append(params.fpScratch(i));
+
+                Box<CCallHelpers::JumpList> exceptions = exceptionHandle->scheduleExitCreation(params)->jumps(jit);
+
+                DOMJIT::PatchpointParams domJITParams(WTFMove(regs), WTFMove(gpScratch), WTFMove(fpScratch));
+                domJIT->generator()->run(jit, domJITParams);
+            });
+        patchpoint->effects = Effects::forCall();
+        setJSValue(patchpoint);
+    }
     
     void compareEqObjectOrOtherToObject(Edge leftChild, Edge rightChild)
     {
index 769491a..c6d8bc3 100644 (file)
@@ -1209,9 +1209,9 @@ public:
     
     enum ExceptionCheckKind { NormalExceptionCheck, InvertedExceptionCheck };
     enum ExceptionJumpWidth { NormalJumpWidth, FarJumpWidth };
-    Jump emitExceptionCheck(
+    JS_EXPORT_PRIVATE Jump emitExceptionCheck(
         ExceptionCheckKind = NormalExceptionCheck, ExceptionJumpWidth = NormalJumpWidth);
-    Jump emitNonPatchableExceptionCheck();
+    JS_EXPORT_PRIVATE Jump emitNonPatchableExceptionCheck();
 
 #if ENABLE(SAMPLING_COUNTERS)
     static void emitCount(MacroAssembler& jit, AbstractSamplingCounter& counter, int32_t increment = 1)
index f15000b..b79a873 100644 (file)
@@ -28,6 +28,9 @@
 #include "ButterflyInlines.h"
 #include "CodeBlock.h"
 #include "Completion.h"
+#include "DOMJITGetterSetter.h"
+#include "DOMJITPatchpoint.h"
+#include "DOMJITPatchpointParams.h"
 #include "Disassembler.h"
 #include "Exception.h"
 #include "ExceptionHelpers.h"
@@ -72,6 +75,7 @@
 #include <type_traits>
 #include <wtf/CurrentTime.h>
 #include <wtf/MainThread.h>
+#include <wtf/NeverDestroyed.h>
 #include <wtf/StringPrintStream.h>
 #include <wtf/text/StringBuilder.h>
 
@@ -537,12 +541,133 @@ private:
     WriteBarrier<JSC::Unknown> m_hiddenValue;
 };
 
+class DOMJITNode : public JSNonFinalObject {
+public:
+    DOMJITNode(VM& vm, Structure* structure)
+        : Base(vm, structure)
+    {
+    }
+
+    DECLARE_INFO;
+    typedef JSNonFinalObject 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 DOMJITNode* create(VM& vm, Structure* structure)
+    {
+        DOMJITNode* getter = new (NotNull, allocateCell<DOMJITNode>(vm.heap, sizeof(DOMJITNode))) DOMJITNode(vm, structure);
+        getter->finishCreation(vm);
+        return getter;
+    }
+
+    int32_t value() const
+    {
+        return m_value;
+    }
+
+    static ptrdiff_t offsetOfValue() { return OBJECT_OFFSETOF(DOMJITNode, m_value); }
+
+private:
+    int32_t m_value { 42 };
+};
+
+class DOMJITGetter : public DOMJITNode {
+public:
+    DOMJITGetter(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 DOMJITGetter* create(VM& vm, Structure* structure)
+    {
+        DOMJITGetter* getter = new (NotNull, allocateCell<DOMJITGetter>(vm.heap, sizeof(DOMJITGetter))) DOMJITGetter(vm, structure);
+        getter->finishCreation(vm);
+        return getter;
+    }
+
+    class DOMJITNodeDOMJIT : public DOMJIT::GetterSetter {
+    public:
+        DOMJITNodeDOMJIT()
+            : DOMJIT::GetterSetter(DOMJITGetter::customGetter, nullptr, DOMJITNode::info())
+        {
+        }
+
+        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;
+        }
+
+        Ref<DOMJIT::Patchpoint> callDOM() override
+        {
+            Ref<DOMJIT::Patchpoint> patchpoint = DOMJIT::Patchpoint::create();
+            patchpoint->setGenerator([=](CCallHelpers& jit, const DOMJIT::PatchpointParams& params) {
+                JSValueRegs results = params[0].jsValueRegs();
+                GPRReg dom = params[2].gpr();
+
+                jit.load32(CCallHelpers::Address(dom, DOMJITNode::offsetOfValue()), results.payloadGPR());
+                jit.boxInt32(results.payloadGPR(), results);
+                return CCallHelpers::JumpList();
+            });
+            return patchpoint;
+        }
+    };
+
+    static DOMJIT::GetterSetter* domJITNodeGetterSetter()
+    {
+        static NeverDestroyed<DOMJITNodeDOMJIT> graph;
+        return &graph.get();
+    }
+
+private:
+    void finishCreation(VM& vm)
+    {
+        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);
+    }
+
+    static EncodedJSValue customGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName)
+    {
+        VM& vm = exec->vm();
+        auto scope = DECLARE_THROW_SCOPE(vm);
+
+        DOMJITNode* thisObject = jsDynamicCast<DOMJITNode*>(JSValue::decode(thisValue));
+        if (!thisObject)
+            return throwVMTypeError(exec, scope);
+        return JSValue::encode(jsNumber(thisObject->value()));
+    }
+};
 
 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) };
 const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(ImpureGetter) };
 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 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 };
@@ -571,6 +696,8 @@ static EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateRuntimeArray(ExecState*);
 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 functionCreateBuiltin(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateGlobalObject(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState*);
@@ -855,6 +982,8 @@ protected:
 
         addFunction(vm, "createImpureGetter", functionCreateImpureGetter, 1);
         addFunction(vm, "createCustomGetterObject", functionCreateCustomGetterObject, 0);
+        addFunction(vm, "createDOMJITNodeObject", functionCreateDOMJITNodeObject, 0);
+        addFunction(vm, "createDOMJITGetterObject", functionCreateDOMJITGetterObject, 0);
         addFunction(vm, "createBuiltin", functionCreateBuiltin, 2);
         addFunction(vm, "createGlobalObject", functionCreateGlobalObject, 0);
         addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);
@@ -1359,6 +1488,22 @@ EncodedJSValue JSC_HOST_CALL functionCreateCustomGetterObject(ExecState* exec)
     return JSValue::encode(result);
 }
 
+EncodedJSValue JSC_HOST_CALL functionCreateDOMJITNodeObject(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    Structure* structure = DOMJITNode::createStructure(exec->vm(), exec->lexicalGlobalObject(), DOMJITGetter::create(exec->vm(), DOMJITGetter::createStructure(exec->vm(), exec->lexicalGlobalObject(), jsNull())));
+    DOMJITNode* result = DOMJITNode::create(exec->vm(), structure);
+    return JSValue::encode(result);
+}
+
+EncodedJSValue JSC_HOST_CALL functionCreateDOMJITGetterObject(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    Structure* structure = DOMJITGetter::createStructure(exec->vm(), exec->lexicalGlobalObject(), jsNull());
+    DOMJITGetter* result = DOMJITGetter::create(exec->vm(), structure);
+    return JSValue::encode(result);
+}
+
 EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState* exec)
 {
     JSLockHolder lock(exec);
index 3303454..e663e95 100644 (file)
@@ -1,3 +1,13 @@
+2016-10-05  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [DOMJIT] Add initial CheckDOM and CallDOM implementations
+        https://bugs.webkit.org/show_bug.cgi?id=162941
+
+        Reviewed by Filip Pizlo.
+
+        * wtf/Box.h:
+        (WTF::Box::Box):
+
 2016-10-05  Zan Dobersek  <zdobersek@igalia.com>
 
         Rename ENABLE_ENCRYPTED_MEDIA_V2 to ENABLE_LEGACY_ENCRYPTED_MEDIA
index ad9d5c5..b661321 100644 (file)
@@ -40,6 +40,10 @@ public:
     {
     }
 
+    Box(std::nullptr_t)
+    {
+    }
+
     template<typename... Arguments>
     static Box create(Arguments&&... arguments)
     {