Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
[JSC] __proto__ getter should be fast
https://bugs.webkit.org/show_bug.cgi?id=178067 Reviewed by Saam Barati. JSTests: * stress/dfg-object-proto-accessor.js: Added. (shouldBe): (shouldThrow): (target): * stress/dfg-object-proto-getter.js: Added. (shouldBe): (shouldThrow): (target): * stress/dfg-object-prototype-of.js: Added. (shouldBe): (shouldThrow): (target): * stress/dfg-reflect-get-prototype-of.js: Added. (shouldBe): (shouldThrow): (target): * stress/object-get-prototype-of-filtered.js: Added. (shouldBe): (shouldThrow): (target): (i.Cocoa): * stress/object-get-prototype-of-mono-proto.js: Added. (shouldBe): (makePolyProtoObject.foo.C): (makePolyProtoObject.foo): (makePolyProtoObject): (target): * stress/object-get-prototype-of-poly-mono-proto.js: Added. (shouldBe): (makePolyProtoObject.foo.C): (makePolyProtoObject.foo): (makePolyProtoObject): (target): * stress/object-get-prototype-of-poly-proto.js: Added. (shouldBe): (makePolyProtoObject.foo.C): (makePolyProtoObject.foo): (makePolyProtoObject): (target): * stress/object-proto-getter-filtered.js: Added. (shouldBe): (shouldThrow): (target): (i.Cocoa): * stress/object-proto-getter-poly-mono-proto.js: Added. (shouldBe): (makePolyProtoObject.foo.C): (makePolyProtoObject.foo): (makePolyProtoObject): (target): * stress/object-proto-getter-poly-proto.js: Added. (shouldBe): (makePolyProtoObject.foo.C): (makePolyProtoObject.foo): (makePolyProtoObject): (target): * stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js: * stress/string-proto.js: Added. (shouldBe): (target): Source/JavaScriptCore: In our ES6 class implementation, we access __proto__ field to retrieve super constructor. Currently, it is handled as an usual getter call to a generic function. And DFG just emits Call node for this. It is inefficient since typically we know the `prototype` of the given object when accessing `object.__proto__` since we emit CheckStructure for this `object`. If Structure has mono proto, we can immediately fold it to constant value. If it is poly proto, we can still change this to efficient access to poly proto slot. This patch implements GetPrototypeOf DFG node. This node efficiently accesses to prototype of the given object. And in AI and ByteCodeParser phase, we attempt to fold it to constant. ByteCodeParser's folding is a bit important since we have `callee.__proto__` code to get super constructor. If we can change this to constant, we can reify CallLinkInfo with this constant. This paves the way to optimizing ArrayConstructor super calls[1], which is particularly important for ARES-6 ML. And we also optimize Reflect.getPrototypeOf and Object.getPrototypeOf with this GetPrototypeOf node. Currently, __proto__ access for poly proto object is not handled well in IC. But we add code handling poly proto in GetPrototypeOf since Reflect.getPrototypeOf and Object.getPrototypeOf can use it. Once IC starts handling poly proto & intrinsic getter well, this code will be used for that too. This patch improves SixSpeed super.es6 by 3.42x. baseline patched super.es6 123.6666+-3.9917 ^ 36.1684+-1.0351 ^ definitely 3.4192x faster [1]: https://bugs.webkit.org/show_bug.cgi?id=178064 * dfg/DFGAbstractInterpreterInlines.h: (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): * dfg/DFGByteCodeParser.cpp: (JSC::DFG::ByteCodeParser::handleIntrinsicCall): (JSC::DFG::ByteCodeParser::handleIntrinsicGetter): (JSC::DFG::ByteCodeParser::handleGetById): * dfg/DFGClobberize.h: (JSC::DFG::clobberize): * dfg/DFGDoesGC.cpp: (JSC::DFG::doesGC): * dfg/DFGFixupPhase.cpp: (JSC::DFG::FixupPhase::fixupNode): (JSC::DFG::FixupPhase::fixupGetPrototypeOf): * dfg/DFGHeapLocation.cpp: (WTF::printInternal): * dfg/DFGHeapLocation.h: * dfg/DFGNode.h: (JSC::DFG::Node::hasHeapPrediction): (JSC::DFG::Node::shouldSpeculateFunction): * dfg/DFGNodeType.h: * dfg/DFGOperations.cpp: * dfg/DFGOperations.h: * dfg/DFGPredictionPropagationPhase.cpp: * dfg/DFGSafeToExecute.h: (JSC::DFG::safeToExecute): * dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::speculateFunction): (JSC::DFG::SpeculativeJIT::speculateFinalObject): (JSC::DFG::SpeculativeJIT::compileGetPrototypeOf): * dfg/DFGSpeculativeJIT.h: * dfg/DFGSpeculativeJIT32_64.cpp: (JSC::DFG::SpeculativeJIT::compile): * dfg/DFGSpeculativeJIT64.cpp: (JSC::DFG::SpeculativeJIT::compile): * ftl/FTLCapabilities.cpp: (JSC::FTL::canCompile): * ftl/FTLLowerDFGToB3.cpp: (JSC::FTL::DFG::LowerDFGToB3::compileNode): (JSC::FTL::DFG::LowerDFGToB3::compileGetPrototypeOf): (JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf): * jit/IntrinsicEmitter.cpp: (JSC::IntrinsicGetterAccessCase::canEmitIntrinsicGetter): (JSC::IntrinsicGetterAccessCase::emitIntrinsicGetter): * runtime/Intrinsic.cpp: (JSC::intrinsicName): * runtime/Intrinsic.h: * runtime/JSGlobalObject.cpp: (JSC::JSGlobalObject::init): * runtime/JSGlobalObjectFunctions.cpp: (JSC::globalFuncProtoGetter): * runtime/JSGlobalObjectFunctions.h: * runtime/ObjectConstructor.cpp: * runtime/ReflectObject.cpp: LayoutTests: * js/object-literal-shorthand-construction-expected.txt: * js/script-tests/object-literal-shorthand-construction.js: (set 2): (get 1): * js/script-tests/sloppy-getter-setter-global-object.js: * js/sloppy-getter-setter-global-object-expected.txt: Canonical link: https://commits.webkit.org/194609@main git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223523 268f45cc-cd09-0410-ab3c-d52691b4dbfc
- Loading branch information
Showing
with
1,400 additions
and 21 deletions.
- +68 −0 JSTests/ChangeLog
- +115 −0 JSTests/stress/dfg-object-proto-accessor.js
- +117 −0 JSTests/stress/dfg-object-proto-getter.js
- +115 −0 JSTests/stress/dfg-object-prototype-of.js
- +58 −0 JSTests/stress/dfg-reflect-get-prototype-of.js
- +31 −0 JSTests/stress/intrinsic-getter-with-poly-proto.js
- +64 −0 JSTests/stress/object-get-prototype-of-filtered.js
- +34 −0 JSTests/stress/object-get-prototype-of-mono-proto.js
- +33 −0 JSTests/stress/object-get-prototype-of-poly-mono-proto.js
- +30 −0 JSTests/stress/object-get-prototype-of-poly-proto.js
- +62 −0 JSTests/stress/object-proto-getter-filtered.js
- +34 −0 JSTests/stress/object-proto-getter-poly-mono-proto.js
- +29 −0 JSTests/stress/object-proto-getter-poly-proto.js
- +1 −1 JSTests/stress/object-prototype-proto-accessors-should-throw-on-undefined-this.js
- +12 −0 JSTests/stress/string-proto.js
- +14 −0 LayoutTests/ChangeLog
- +1 −1 LayoutTests/js/object-literal-shorthand-construction-expected.txt
- +1 −1 LayoutTests/js/script-tests/object-literal-shorthand-construction.js
- +2 −2 LayoutTests/js/script-tests/sloppy-getter-setter-global-object.js
- +2 −2 LayoutTests/js/sloppy-getter-setter-global-object-expected.txt
- +89 −0 Source/JavaScriptCore/ChangeLog
- +42 −0 Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
- +55 −3 Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
- +17 −0 Source/JavaScriptCore/dfg/DFGClobberize.h
- +1 −0 Source/JavaScriptCore/dfg/DFGDoesGC.cpp
- +58 −0 Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
- +4 −0 Source/JavaScriptCore/dfg/DFGHeapLocation.cpp
- +1 −0 Source/JavaScriptCore/dfg/DFGHeapLocation.h
- +6 −0 Source/JavaScriptCore/dfg/DFGNode.h
- +1 −0 Source/JavaScriptCore/dfg/DFGNodeType.h
- +31 −0 Source/JavaScriptCore/dfg/DFGOperations.cpp
- +2 −0 Source/JavaScriptCore/dfg/DFGOperations.h
- +2 −1 Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
- +1 −0 Source/JavaScriptCore/dfg/DFGSafeToExecute.h
- +131 −2 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
- +8 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h
- +5 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
- +5 −0 Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
- +1 −0 Source/JavaScriptCore/ftl/FTLCapabilities.cpp
- +81 −2 Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
- +18 −0 Source/JavaScriptCore/jit/IntrinsicEmitter.cpp
- +1 −0 Source/JavaScriptCore/jit/JITOperations.h
- +6 −0 Source/JavaScriptCore/runtime/Intrinsic.cpp
- +3 −0 Source/JavaScriptCore/runtime/Intrinsic.h
- +1 −1 Source/JavaScriptCore/runtime/JSGlobalObject.cpp
- +3 −3 Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp
- +2 −0 Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h
- +1 −1 Source/JavaScriptCore/runtime/ObjectConstructor.cpp
- +1 −1 Source/JavaScriptCore/runtime/ReflectObject.cpp
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,115 @@ | ||
function shouldBe(actual, expected) | ||
{ | ||
if (actual !== expected) | ||
throw new Error('bad value: ' + 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 () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target({}), Object.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) { | ||
shouldThrow(() => target(null), `TypeError: null is not an object (evaluating 'object.__proto__')`); | ||
shouldThrow(() => target(undefined), `TypeError: undefined is not an object (evaluating 'object.__proto__')`); | ||
} | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target("Cocoa"), String.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(42), Number.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(42.195), Number.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(true), Boolean.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(Symbol("Cocoa")), Symbol.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return object.__proto__; | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) { | ||
shouldBe(target("Cocoa"), String.prototype); | ||
shouldBe(target(42), Number.prototype); | ||
shouldBe(target(42.195), Number.prototype); | ||
shouldBe(target(true), Boolean.prototype); | ||
shouldBe(target(Symbol("Cocoa")), Symbol.prototype); | ||
} | ||
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -0,0 +1,117 @@ | ||
function shouldBe(actual, expected) | ||
{ | ||
if (actual !== expected) | ||
throw new Error('bad value: ' + 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)}`); | ||
} | ||
|
||
var protoFunction = Object.getOwnPropertyDescriptor(Object.prototype, "__proto__").get; | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target({}), Object.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) { | ||
shouldThrow(() => target(null), `TypeError: null is not an object (evaluating 'protoFunction.call(object)')`); | ||
shouldThrow(() => target(undefined), `TypeError: undefined is not an object (evaluating 'protoFunction.call(object)')`); | ||
} | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target("Cocoa"), String.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(42), Number.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(42.195), Number.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(true), Boolean.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) | ||
shouldBe(target(Symbol("Cocoa")), Symbol.prototype); | ||
}()); | ||
|
||
(function () { | ||
function target(object) | ||
{ | ||
return protoFunction.call(object); | ||
} | ||
noInline(target); | ||
|
||
for (var i = 0; i < 1e3; ++i) { | ||
shouldBe(target("Cocoa"), String.prototype); | ||
shouldBe(target(42), Number.prototype); | ||
shouldBe(target(42.195), Number.prototype); | ||
shouldBe(target(true), Boolean.prototype); | ||
shouldBe(target(Symbol("Cocoa")), Symbol.prototype); | ||
} | ||
}()); |
Oops, something went wrong.