Commit 10c38d3c authored by fpizlo@apple.com's avatar fpizlo@apple.com
Browse files

JSObject fast by-string access optimizations should work even on the prototype...

JSObject fast by-string access optimizations should work even on the prototype chain, and even when the result is undefined
https://bugs.webkit.org/show_bug.cgi?id=112233

Source/JavaScriptCore: 

Reviewed by Oliver Hunt.
        
Extended the existing fast access path for String keys to work over the entire prototype chain,
not just the self access case. This will fail as soon as it sees an object that intercepts
getOwnPropertySlot, so this patch also ensures that ObjectPrototype does not fall into that
category. This is accomplished by making ObjectPrototype eagerly reify all of its properties.
This is safe for ObjectPrototype because it's so common and we expect all of its properties to
be reified for any interesting programs anyway. A new idiom for adding native functions to
prototypes is introduced, which ought to work well for any other prototypes that we wish to do
this conversion for.
        
This is a >60% speed-up in the case that you frequently do by-string lookups that "miss", i.e.
they don't turn up anything.

* CMakeLists.txt:
* DerivedSources.make:
* DerivedSources.pri:
* GNUmakefile.list.am:
* dfg/DFGOperations.cpp:
* interpreter/CallFrame.h:
(JSC::ExecState::objectConstructorTable):
* jit/JITStubs.cpp:
(JSC::getByVal):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::getByVal):
* runtime/CommonIdentifiers.h:
* runtime/JSCell.cpp:
(JSC::JSCell::getByStringSlow):
(JSC):
* runtime/JSCell.h:
(JSCell):
* runtime/JSCellInlines.h:
(JSC):
(JSC::JSCell::getByStringAndKey):
(JSC::JSCell::getByString):
* runtime/JSGlobalData.cpp:
(JSC):
(JSC::JSGlobalData::JSGlobalData):
(JSC::JSGlobalData::~JSGlobalData):
* runtime/JSGlobalData.h:
(JSGlobalData):
* runtime/JSObject.cpp:
(JSC::JSObject::putDirectNativeFunction):
(JSC):
* runtime/JSObject.h:
(JSObject):
(JSC):
* runtime/Lookup.cpp:
(JSC::setUpStaticFunctionSlot):
* runtime/ObjectPrototype.cpp:
(JSC):
(JSC::ObjectPrototype::finishCreation):
(JSC::ObjectPrototype::create):
* runtime/ObjectPrototype.h:
(ObjectPrototype):
* runtime/PropertyMapHashTable.h:
(JSC::PropertyTable::findWithString):
* runtime/Structure.h:
(Structure):
* runtime/StructureInlines.h:
(JSC::Structure::get):
(JSC):

LayoutTests: 

Reviewed by Oliver Hunt.

* fast/js/regress/script-tests/string-lookup-hit-identifier.js: Added.
(result):
* fast/js/regress/script-tests/string-lookup-hit.js: Added.
(result):
* fast/js/regress/script-tests/string-lookup-miss.js: Added.
(result):
* fast/js/regress/string-lookup-hit-expected.txt: Added.
* fast/js/regress/string-lookup-hit-identifier-expected.txt: Added.
* fast/js/regress/string-lookup-hit-identifier.html: Added.
* fast/js/regress/string-lookup-hit.html: Added.
* fast/js/regress/string-lookup-miss-expected.txt: Added.
* fast/js/regress/string-lookup-miss.html: Added.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@145838 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 367b5105
2013-03-14 Filip Pizlo <fpizlo@apple.com>
JSObject fast by-string access optimizations should work even on the prototype chain, and even when the result is undefined
https://bugs.webkit.org/show_bug.cgi?id=112233
Reviewed by Oliver Hunt.
* fast/js/regress/script-tests/string-lookup-hit-identifier.js: Added.
(result):
* fast/js/regress/script-tests/string-lookup-hit.js: Added.
(result):
* fast/js/regress/script-tests/string-lookup-miss.js: Added.
(result):
* fast/js/regress/string-lookup-hit-expected.txt: Added.
* fast/js/regress/string-lookup-hit-identifier-expected.txt: Added.
* fast/js/regress/string-lookup-hit-identifier.html: Added.
* fast/js/regress/string-lookup-hit.html: Added.
* fast/js/regress/string-lookup-miss-expected.txt: Added.
* fast/js/regress/string-lookup-miss.html: Added.
2013-03-14 Kenneth Russell <kbr@google.com>
 
Unreviewed expectations updates for a couple of failing tests.
var result = (function(){
var o = {};
for (var i = 0; i < 100; ++i)
o["a" + i] = 42;
var result = [];
var strings = [];
for (var i = 0; i < 100; ++i)
strings.push("a" + i);
for (var j = 0; j < 1000; ++j) {
for (var i = 0; i < 100; ++i)
result.push(o[strings[i]]);
}
return result.length;
})();
if (result != 100000)
throw "Error: bad result: " + result;
var result = (function(){
var o = {};
for (var i = 0; i < 100; ++i)
o["a" + i] = 42;
var result = [];
for (var j = 0; j < 1000; ++j) {
for (var i = 0; i < 100; ++i)
result.push(o["a" + i]);
}
return result.length;
})();
if (result != 100000)
throw "Error: bad result: " + result;
var result = (function(){
var o = {};
for (var i = 0; i < 100; ++i)
o["a" + i] = 42;
var result = [];
for (var i = 0; i < 100000; ++i)
result.push(o["a" + i]);
return result.length;
})();
if (result != 100000)
throw "Error: bad result: " + result;
JSRegress/string-lookup-hit
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
JSRegress/string-lookup-hit-identifier
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/string-lookup-hit-identifier.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../resources/js-test-post.js"></script>
</body>
</html>
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/string-lookup-hit.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../resources/js-test-post.js"></script>
</body>
</html>
JSRegress/string-lookup-miss
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS no exception thrown
PASS successfullyParsed is true
TEST COMPLETE
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<script src="../resources/js-test-pre.js"></script>
</head>
<body>
<script src="resources/regress-pre.js"></script>
<script src="script-tests/string-lookup-miss.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../resources/js-test-post.js"></script>
</body>
</html>
......@@ -329,7 +329,6 @@ set(JavaScriptCore_LUT_FILES
runtime/NumberConstructor.cpp
runtime/NumberPrototype.cpp
runtime/ObjectConstructor.cpp
runtime/ObjectPrototype.cpp
runtime/RegExpConstructor.cpp
runtime/RegExpObject.cpp
runtime/RegExpPrototype.cpp
......
2013-03-13 Filip Pizlo <fpizlo@apple.com>
JSObject fast by-string access optimizations should work even on the prototype chain, and even when the result is undefined
https://bugs.webkit.org/show_bug.cgi?id=112233
Reviewed by Oliver Hunt.
Extended the existing fast access path for String keys to work over the entire prototype chain,
not just the self access case. This will fail as soon as it sees an object that intercepts
getOwnPropertySlot, so this patch also ensures that ObjectPrototype does not fall into that
category. This is accomplished by making ObjectPrototype eagerly reify all of its properties.
This is safe for ObjectPrototype because it's so common and we expect all of its properties to
be reified for any interesting programs anyway. A new idiom for adding native functions to
prototypes is introduced, which ought to work well for any other prototypes that we wish to do
this conversion for.
This is a >60% speed-up in the case that you frequently do by-string lookups that "miss", i.e.
they don't turn up anything.
* CMakeLists.txt:
* DerivedSources.make:
* DerivedSources.pri:
* GNUmakefile.list.am:
* dfg/DFGOperations.cpp:
* interpreter/CallFrame.h:
(JSC::ExecState::objectConstructorTable):
* jit/JITStubs.cpp:
(JSC::getByVal):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::getByVal):
* runtime/CommonIdentifiers.h:
* runtime/JSCell.cpp:
(JSC::JSCell::getByStringSlow):
(JSC):
* runtime/JSCell.h:
(JSCell):
* runtime/JSCellInlines.h:
(JSC):
(JSC::JSCell::getByStringAndKey):
(JSC::JSCell::getByString):
* runtime/JSGlobalData.cpp:
(JSC):
(JSC::JSGlobalData::JSGlobalData):
(JSC::JSGlobalData::~JSGlobalData):
* runtime/JSGlobalData.h:
(JSGlobalData):
* runtime/JSObject.cpp:
(JSC::JSObject::putDirectNativeFunction):
(JSC):
* runtime/JSObject.h:
(JSObject):
(JSC):
* runtime/Lookup.cpp:
(JSC::setUpStaticFunctionSlot):
* runtime/ObjectPrototype.cpp:
(JSC):
(JSC::ObjectPrototype::finishCreation):
(JSC::ObjectPrototype::create):
* runtime/ObjectPrototype.h:
(ObjectPrototype):
* runtime/PropertyMapHashTable.h:
(JSC::PropertyTable::findWithString):
* runtime/Structure.h:
(Structure):
* runtime/StructureInlines.h:
(JSC::Structure::get):
(JSC):
2013-03-13 Filip Pizlo <fpizlo@apple.com>
 
DFG bytecode parser is too aggressive about getting rid of GetLocals on captured variables
......
......@@ -50,7 +50,6 @@ all : \
NumberConstructor.lut.h \
NumberPrototype.lut.h \
ObjectConstructor.lut.h \
ObjectPrototype.lut.h \
RegExpConstructor.lut.h \
RegExpPrototype.lut.h \
RegExpJitTables.h \
......
......@@ -20,7 +20,6 @@ LUT_FILES += \
runtime/NumberConstructor.cpp \
runtime/NumberPrototype.cpp \
runtime/ObjectConstructor.cpp \
runtime/ObjectPrototype.cpp \
runtime/RegExpConstructor.cpp \
runtime/RegExpObject.cpp \
runtime/RegExpPrototype.cpp \
......
......@@ -24,7 +24,6 @@ javascriptcore_built_nosources += \
DerivedSources/JavaScriptCore/NumberConstructor.lut.h \
DerivedSources/JavaScriptCore/NumberPrototype.lut.h \
DerivedSources/JavaScriptCore/ObjectConstructor.lut.h \
DerivedSources/JavaScriptCore/ObjectPrototype.lut.h \
DerivedSources/JavaScriptCore/RegExpConstructor.lut.h \
DerivedSources/JavaScriptCore/RegExpObject.lut.h \
DerivedSources/JavaScriptCore/RegExpPrototype.lut.h \
......
......@@ -424,19 +424,17 @@ EncodedJSValue DFG_OPERATION operationGetByVal(ExecState* exec, EncodedJSValue e
if (LIKELY(baseValue.isCell())) {
JSCell* base = baseValue.asCell();
if (property.isUInt32()) {
if (property.isUInt32())
return getByVal(exec, base, property.asUInt32());
} else if (property.isDouble()) {
if (property.isDouble()) {
double propertyAsDouble = property.asDouble();
uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble);
if (propertyAsUInt32 == propertyAsDouble)
return getByVal(exec, base, propertyAsUInt32);
} else if (property.isString()) {
if (JSValue result = base->fastGetOwnProperty(exec, asString(property)->value(exec)))
return JSValue::encode(result);
}
} else if (property.isString())
return JSValue::encode(base->getByString(exec, asString(property)->value(exec)));
}
if (isName(property))
return JSValue::encode(baseValue.get(exec, jsCast<NameInstance*>(property.asCell())->privateName()));
......@@ -453,15 +451,13 @@ EncodedJSValue DFG_OPERATION operationGetByValCell(ExecState* exec, JSCell* base
if (property.isUInt32())
return getByVal(exec, base, property.asUInt32());
if (property.isDouble()) {
else if (property.isDouble()) {
double propertyAsDouble = property.asDouble();
uint32_t propertyAsUInt32 = static_cast<uint32_t>(propertyAsDouble);
if (propertyAsUInt32 == propertyAsDouble)
return getByVal(exec, base, propertyAsUInt32);
} else if (property.isString()) {
if (JSValue result = base->fastGetOwnProperty(exec, asString(property)->value(exec)))
return JSValue::encode(result);
}
} else if (property.isString())
return JSValue::encode(base->getByString(exec, asString(property)->value(exec)));
if (isName(property))
return JSValue::encode(JSValue(base).get(exec, jsCast<NameInstance*>(property.asCell())->privateName()));
......
......@@ -90,7 +90,6 @@ namespace JSC {
static const HashTable* numberConstructorTable(CallFrame* callFrame) { return callFrame->globalData().numberConstructorTable; }
static const HashTable* numberPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().numberPrototypeTable; }
static const HashTable* objectConstructorTable(CallFrame* callFrame) { return callFrame->globalData().objectConstructorTable; }
static const HashTable* objectPrototypeTable(CallFrame* callFrame) { return callFrame->globalData().objectPrototypeTable; }
static const HashTable* privateNamePrototypeTable(CallFrame* callFrame) { return callFrame->globalData().privateNamePrototypeTable; }
static const HashTable* regExpTable(CallFrame* callFrame) { return callFrame->globalData().regExpTable; }
static const HashTable* regExpConstructorTable(CallFrame* callFrame) { return callFrame->globalData().regExpConstructorTable; }
......
......@@ -2417,10 +2417,8 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_construct_NotJSConstruct)
static JSValue getByVal(
CallFrame* callFrame, JSValue baseValue, JSValue subscript, ReturnAddressPtr returnAddress)
{
if (LIKELY(baseValue.isCell() && subscript.isString())) {
if (JSValue result = baseValue.asCell()->fastGetOwnProperty(callFrame, asString(subscript)->value(callFrame)))
return result;
}
if (LIKELY(baseValue.isCell() && subscript.isString()))
return baseValue.asCell()->getByString(callFrame, asString(subscript)->value(callFrame));
if (subscript.isUInt32()) {
uint32_t i = subscript.asUInt32();
......
......@@ -1060,10 +1060,8 @@ LLINT_SLOW_PATH_DECL(slow_path_del_by_id)
inline JSValue getByVal(ExecState* exec, JSValue baseValue, JSValue subscript)
{
if (LIKELY(baseValue.isCell() && subscript.isString())) {
if (JSValue result = baseValue.asCell()->fastGetOwnProperty(exec, asString(subscript)->value(exec)))
return result;
}
if (LIKELY(baseValue.isCell() && subscript.isString()))
return baseValue.asCell()->getByString(exec, asString(subscript)->value(exec));
if (subscript.isUInt32()) {
uint32_t i = subscript.asUInt32();
......
......@@ -99,7 +99,11 @@
macro(valueOf) \
macro(writable) \
macro(displayName) \
macro(join)
macro(join) \
macro(__defineGetter__) \
macro(__defineSetter__) \
macro(__lookupGetter__) \
macro(__lookupSetter__)
#define JSC_COMMON_IDENTIFIERS_EACH_KEYWORD(macro) \
macro(null) \
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment