Skip to content

Commit 4f35b95

Browse files
committed
[DOMJIT] Add a way for DOMJIT::Patchpoint to express effects
https://bugs.webkit.org/show_bug.cgi?id=163657 Reviewed by Saam Barati. Source/JavaScriptCore: This patch introduces DOMJIT::Effect. It describes the side effects of the DOMJIT::CallDOMPatchpoint. DOMJIT::CallDOMPatchpoint can use this feature to teach the compilers about the effects. And the compilers will perform CSE based on the reported effects. As the same to B3's HeapRange, the effects are represented as a pair of integers. [begin, end) pair will represents the range of the abstract heap. We encode the abstract heap hierarchy into these pairs. Like, Root: [0, 32) Child1: [0, 20) Child2: [20, 32) Child11: [0, 4) Child12: [4, 20) This simplifies the representation of the abstract heap. And WebCore just tells pairs of integers and it does not tell any detailed hierarchy. So, DFG and FTL can optimize DOM operations without deep knowledge of the DOM abstract heap hierarchy. For example, WebCore will tell that firstChild will read Node_firstChild abstract heap. But this information is encoded to the pair and DFG does not know the details. But still DFG can understand the abstract heap hierarchy and can query whether the given abstract heap overlaps with some abstract heap. The heap range told by the WebCore is represented as DOMJIT::HeapRange. DFG will handle this under the DOMState abstract heap. DOMJIT::HeapRange is stored in DFG::AbstractHeap's Payload. We maintain the hierarchy by DOMJIT::HeapRange in the DOMState abstract heap. We add a necessary handling in DFG's AbstractHeap and ClobberSet. And we also introduce DOMStateLoc for HeapLocation. It is combined with DOMState AbstractHeap with DOMJIT::HeapRange. For example, we can represent Node.firstChild as `read(DOMState:Node_firstChild)` and `def(HeapLocation(node, DOMState:Node_firstChild))` thingy. This allows us to perform CSE onto DOM getters that will read some of DOM heap! For simplicity, we convert CallDOM from NodeVarArgs to the normal one. CallDOM is now just used for DOMJIT getter. So its children is at most 2. It may have either 1 or 2 children. If the global object is required by CallDOMPatchpoint, it has 2 children. And we changed the order of the children to further simplify the code. Before this change, the order is 1: globalObject 2: base. After this patch, the order becomes 1: base, and 2: globalObject. And the child2 may not exists if the global object is not required. We changed all the existing DOMJIT patchpoint to this form. * CMakeLists.txt: * JavaScriptCore.xcodeproj/project.pbxproj: * bytecode/PolymorphicAccess.cpp: (JSC::AccessCase::emitDOMJITGetter): * dfg/DFGAbstractHeap.cpp: (JSC::DFG::AbstractHeap::dump): * dfg/DFGAbstractHeap.h: (JSC::DFG::AbstractHeap::isStrictSubtypeOf): (JSC::DFG::AbstractHeap::overlaps): * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::blessCallDOM): (JSC::DFG::ByteCodeParser::handleDOMJITGetter): * dfg/DFGClobberSet.cpp: (JSC::DFG::ClobberSet::overlaps): * dfg/DFGClobberSet.h: * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): * dfg/DFGGraph.h: * dfg/DFGHeapLocation.cpp: (WTF::printInternal): * dfg/DFGHeapLocation.h: * dfg/DFGNode.h: (JSC::DFG::Node::hasCallDOMData): (JSC::DFG::Node::callDOMData): (JSC::DFG::Node::hasCallDOMPatchpoint): Deleted. (JSC::DFG::Node::callDOMPatchpoint): Deleted. * dfg/DFGNodeType.h: * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compileCallDOM): * domjit/DOMJITAbstractHeap.cpp: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. (JSC::DOMJIT::AbstractHeap::compute): (JSC::DOMJIT::AbstractHeap::dump): (JSC::DOMJIT::AbstractHeap::shallowDump): (JSC::DOMJIT::AbstractHeap::deepDump): * domjit/DOMJITAbstractHeap.h: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. (JSC::DOMJIT::AbstractHeap::AbstractHeap): (JSC::DOMJIT::AbstractHeap::setParent): (JSC::DOMJIT::AbstractHeap::isRoot): (JSC::DOMJIT::AbstractHeap::isComputed): (JSC::DOMJIT::AbstractHeap::range): * domjit/DOMJITCallDOMPatchpoint.h: * domjit/DOMJITEffect.h: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. (JSC::DOMJIT::Effect::forReadWrite): (JSC::DOMJIT::Effect::forPure): (JSC::DOMJIT::Effect::forDef): (JSC::DOMJIT::Effect::mustGenerate): * domjit/DOMJITHeapRange.cpp: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. (JSC::DOMJIT::HeapRange::dump): * domjit/DOMJITHeapRange.h: Added. (JSC::DOMJIT::HeapRange::HeapRange): (JSC::DOMJIT::HeapRange::fromRaw): (JSC::DOMJIT::HeapRange::begin): (JSC::DOMJIT::HeapRange::end): (JSC::DOMJIT::HeapRange::rawRepresentation): (JSC::DOMJIT::HeapRange::operator bool): (JSC::DOMJIT::HeapRange::operator==): (JSC::DOMJIT::HeapRange::top): (JSC::DOMJIT::HeapRange::none): (JSC::DOMJIT::HeapRange::isStrictSubtypeOf): (JSC::DOMJIT::HeapRange::isSubtypeOf): (JSC::DOMJIT::HeapRange::overlaps): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileCallDOM): * jsc.cpp: Source/WebCore: CallDOMPatchpoint now has the way to tell its effects to DFG and FTL compilers. WebCore DOMJIT::AbstractHeapRepository will construct the hierarchy of the abstract heap. And then it encodes these information into the pairs of the integers. And this patch also changes the DOMJIT::PatchpointParams' parameter order. So we change them in all the DOMJIT::CallDOMPatchpoint sites. * CMakeLists.txt: * ForwardingHeaders/domjit/DOMJITAbstractHeap.h: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. * ForwardingHeaders/domjit/DOMJITEffect.h: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. * ForwardingHeaders/domjit/DOMJITHeapRange.h: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. * WebCore.xcodeproj/project.pbxproj: * domjit/DOMJITAbstractHeapRepository.cpp: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. (WebCore::DOMJIT::AbstractHeapRepository::AbstractHeapRepository): (WebCore::DOMJIT::AbstractHeapRepository::instance): * domjit/DOMJITAbstractHeapRepository.h: Copied from Source/JavaScriptCore/domjit/DOMJITCallDOMPatchpoint.h. * domjit/DOMJITHelpers.h: (WebCore::DOMJITHelpers::branchIfNotWorldIsNormal): Deleted. (WebCore::DOMJITHelpers::branchIfNotWeakIsLive): Deleted. (WebCore::DOMJITHelpers::tryLookUpWrapperCache): Deleted. (WebCore::DOMJITHelpers::toWrapper): Deleted. (WebCore::DOMJITHelpers::branchIfDOMWrapper): Deleted. (WebCore::DOMJITHelpers::branchIfNotDOMWrapper): Deleted. (WebCore::DOMJITHelpers::branchIfNode): Deleted. (WebCore::DOMJITHelpers::branchIfNotNode): Deleted. (WebCore::DOMJITHelpers::branchIfElement): Deleted. (WebCore::DOMJITHelpers::branchIfNotElement): Deleted. (WebCore::DOMJITHelpers::branchIfDocumentWrapper): Deleted. (WebCore::DOMJITHelpers::branchIfNotDocumentWrapper): Deleted. * domjit/JSNodeDOMJIT.cpp: (WebCore::createCallDOMForOffsetAccess): (WebCore::checkNode): (WebCore::NodeFirstChildDOMJIT::checkDOM): (WebCore::NodeFirstChildDOMJIT::callDOM): (WebCore::NodeLastChildDOMJIT::checkDOM): (WebCore::NodeLastChildDOMJIT::callDOM): (WebCore::NodeNextSiblingDOMJIT::checkDOM): (WebCore::NodeNextSiblingDOMJIT::callDOM): (WebCore::NodePreviousSiblingDOMJIT::checkDOM): (WebCore::NodePreviousSiblingDOMJIT::callDOM): (WebCore::NodeParentNodeDOMJIT::checkDOM): (WebCore::NodeParentNodeDOMJIT::callDOM): (WebCore::NodeNodeTypeDOMJIT::checkDOM): (WebCore::NodeNodeTypeDOMJIT::callDOM): Source/WTF: Simplify nonEmptyRangesOverlap. * wtf/MathExtras.h: (WTF::nonEmptyRangesOverlap): LayoutTests: * js/dom/domjit-accessor-different-effect-expected.txt: Added. * js/dom/domjit-accessor-different-effect.html: Added. * js/dom/domjit-accessor-effect-expected.txt: Added. * js/dom/domjit-accessor-effect-should-overlap-with-call-expected.txt: Added. * js/dom/domjit-accessor-effect-should-overlap-with-call.html: Added. * js/dom/domjit-accessor-effect.html: Added. * js/dom/domjit-accessor-licm-expected.txt: Added. * js/dom/domjit-accessor-licm.html: Added. * js/dom/domjit-accessor-node-type-effect-should-not-overlap-with-call-since-pure-expected.txt: Added. * js/dom/domjit-accessor-node-type-effect-should-not-overlap-with-call-since-pure.html: Added. Canonical link: https://commits.webkit.org/181648@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@207787 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent fb0eae3 commit 4f35b95

50 files changed

Lines changed: 1295 additions & 78 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎LayoutTests/ChangeLog‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
2016-10-23 Yusuke Suzuki <utatane.tea@gmail.com>
2+
3+
[DOMJIT] Add a way for DOMJIT::Patchpoint to express effects
4+
https://bugs.webkit.org/show_bug.cgi?id=163657
5+
6+
Reviewed by Saam Barati.
7+
8+
* js/dom/domjit-accessor-different-effect-expected.txt: Added.
9+
* js/dom/domjit-accessor-different-effect.html: Added.
10+
* js/dom/domjit-accessor-effect-expected.txt: Added.
11+
* js/dom/domjit-accessor-effect-should-overlap-with-call-expected.txt: Added.
12+
* js/dom/domjit-accessor-effect-should-overlap-with-call.html: Added.
13+
* js/dom/domjit-accessor-effect.html: Added.
14+
* js/dom/domjit-accessor-licm-expected.txt: Added.
15+
* js/dom/domjit-accessor-licm.html: Added.
16+
* js/dom/domjit-accessor-node-type-effect-should-not-overlap-with-call-since-pure-expected.txt: Added.
17+
* js/dom/domjit-accessor-node-type-effect-should-not-overlap-with-call-since-pure.html: Added.
18+
119
2016-10-24 Myles C. Maxfield <mmaxfield@apple.com>
220

321
Font variations test gardening
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Test DOMJIT accessors will handle different heap accesses correctly.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
PASS test() is true
6+
PASS successfullyParsed is true
7+
8+
TEST COMPLETE
9+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="../../resources/js-test-pre.js"></script>
5+
</head>
6+
<body>
7+
<div id="description"></div>
8+
<div id="console"></div>
9+
10+
<div id="parentNode">
11+
<div id="previousSibling"></div><div id="target"><div id="firstChild"></div><div id="lastChild"></div></div><div id="nextSibling"></div>
12+
</div>
13+
14+
<script>
15+
description('Test DOMJIT accessors will handle different heap accesses correctly.');
16+
17+
function test()
18+
{
19+
var target = document.getElementById('target');
20+
var firstChild = document.getElementById('firstChild');
21+
var lastChild = document.getElementById('lastChild');
22+
for (var i = 0; i < 1e4; ++i) {
23+
var ret = target.firstChild === firstChild;
24+
ret &= target.lastChild === lastChild;
25+
ret &= target.firstChild === firstChild;
26+
ret &= target.lastChild === lastChild;
27+
if (!ret)
28+
return false;
29+
}
30+
return true;
31+
}
32+
33+
shouldBeTrue(`test()`);
34+
</script>
35+
<script src="../../resources/js-test-post.js"></script>
36+
</body>
37+
</html>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Test DOMJIT accessors will represent their heap accesses.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
PASS func("firstChild", document.getElementById("target"), document.getElementById("firstChild")) is true
6+
PASS func("lastChild", document.getElementById("target"), document.getElementById("lastChild")) is true
7+
PASS func("nextSibling", document.getElementById("target"), document.getElementById("nextSibling")) is true
8+
PASS func("previousSibling", document.getElementById("target"), document.getElementById("previousSibling")) is true
9+
PASS func("parentNode", document.getElementById("target"), document.getElementById("parentNode")) is true
10+
PASS successfullyParsed is true
11+
12+
TEST COMPLETE
13+
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Test function calls should overlap with effects of DOMJIT accessors.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
PASS test() is true
6+
PASS successfullyParsed is true
7+
8+
TEST COMPLETE
9+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="../../resources/js-test-pre.js"></script>
5+
</head>
6+
<body>
7+
<div id="description"></div>
8+
<div id="console"></div>
9+
10+
<div id="parentNode">
11+
<div id="previousSibling"></div><div id="target"><div id="firstChild"></div><div id="lastChild"></div></div><div id="nextSibling"></div>
12+
</div>
13+
14+
<script>
15+
description('Test function calls should overlap with effects of DOMJIT accessors.');
16+
17+
function test()
18+
{
19+
var target = document.getElementById('target');
20+
var firstChild = document.getElementById('firstChild');
21+
var lastChild = document.getElementById('lastChild');
22+
for (var i = 0; i < 1e4; ++i) {
23+
var ret = target.firstChild === firstChild;
24+
target.appendChild(lastChild);
25+
ret &= target.firstChild === firstChild;
26+
if (!ret)
27+
return false;
28+
}
29+
return true;
30+
}
31+
32+
shouldBeTrue(`test()`);
33+
</script>
34+
<script src="../../resources/js-test-post.js"></script>
35+
</body>
36+
</html>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<script src="../../resources/js-test-pre.js"></script>
5+
</head>
6+
<body>
7+
<div id="description"></div>
8+
<div id="console"></div>
9+
10+
<div id="parentNode">
11+
<div id="previousSibling"></div><div id="target"><div id="firstChild"></div><div id="lastChild"></div></div><div id="nextSibling"></div>
12+
</div>
13+
14+
<script>
15+
description('Test DOMJIT accessors will represent their heap accesses.');
16+
var tests = [
17+
"firstChild",
18+
"lastChild",
19+
"nextSibling",
20+
"previousSibling",
21+
"parentNode",
22+
].map(function (name) {
23+
var func = `
24+
return function ${name}(name, element, result) {
25+
for (var i = 0; i < 1e4; ++i) {
26+
if (element.${name} !== result)
27+
return false;
28+
if (element.${name} !== result)
29+
return false;
30+
if (element.${name} !== result)
31+
return false;
32+
if (element.${name} !== result)
33+
return false;
34+
if (element.${name} !== result)
35+
return false;
36+
}
37+
return true;
38+
};
39+
`;
40+
return [ name, Function(func)() ];
41+
});
42+
43+
for (var [name, func] of tests) {
44+
shouldBeTrue(`func("${name}", document.getElementById("target"), document.getElementById("${name}"))`);
45+
}
46+
</script>
47+
<script src="../../resources/js-test-post.js"></script>
48+
</body>
49+
</html>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Test DOMJIT accessor will be LICM-ed.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
PASS successfullyParsed is true
6+
7+
TEST COMPLETE
8+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE HTML>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
</head>
6+
<body>
7+
<script>
8+
9+
</script>
10+
</body>
11+
</html>
12+
<!DOCTYPE html>
13+
<html>
14+
<head>
15+
<script src="../../resources/js-test-pre.js"></script>
16+
</head>
17+
<body>
18+
<div id="description"></div>
19+
<div id="console"></div>
20+
21+
<div id="parentNode">
22+
<div id="previousSibling"></div><div id="target"><div id="firstChild"></div><div id="lastChild"></div></div><div id="nextSibling"></div>
23+
</div>
24+
25+
<script>
26+
description('Test DOMJIT accessor will be LICM-ed.');
27+
28+
function test() {
29+
var div = document.createElement('div');
30+
var ret = 0;
31+
for (var i = 0; i < 1e4; ++i)
32+
ret = div.nodeType;
33+
return ret;
34+
}
35+
var result;
36+
(function () {
37+
for (var i = 0; i < 100; ++i) {
38+
result = test();
39+
shouldBe(`result`, `Node.ELEMENT_NODE`, true);
40+
}
41+
}());
42+
</script>
43+
<script src="../../resources/js-test-post.js"></script>
44+
</body>
45+
</html>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Test function calls should not overlap with effects of DOMJIT's nodeType accessor since it is pure.
2+
3+
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
4+
5+
PASS test() is true
6+
PASS successfullyParsed is true
7+
8+
TEST COMPLETE
9+

0 commit comments

Comments
 (0)