Commit 3d42314b authored by fpizlo@apple.com's avatar fpizlo@apple.com

Object properties added using dot syntax (o.f = ...) from code that isn't in...

Object properties added using dot syntax (o.f = ...) from code that isn't in eval should be less likely to cause an object to become a dictionary
https://bugs.webkit.org/show_bug.cgi?id=119897

Source/JavaScriptCore: 

Reviewed by Oliver Hunt.
        
6-10x speed-up on microbenchmarks that create large static objects. 40-65% speed-up
on Octane/gbemu. 3% overall speed-up on Octane. No slow-downs anywhere; our ability
to turn objects into dictionaries when you're storing using bracket syntax or using
eval is still in place.

* bytecode/CodeBlock.h:
(JSC::CodeBlock::putByIdContext):
* dfg/DFGOperations.cpp:
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* runtime/JSObject.h:
(JSC::JSObject::putDirectInternal):
* runtime/PutPropertySlot.h:
(JSC::PutPropertySlot::PutPropertySlot):
(JSC::PutPropertySlot::context):
* runtime/Structure.cpp:
(JSC::Structure::addPropertyTransition):
* runtime/Structure.h:

LayoutTests: 

Reviewed by Oliver Hunt.

* fast/js/regress/lots-of-fields-expected.txt: Added.
* fast/js/regress/lots-of-fields.html: Added.
* fast/js/regress/script-tests/lots-of-fields.js: Added.
(foo):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@154199 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3ba5e00c
2013-08-16 Filip Pizlo <fpizlo@apple.com>
Object properties added using dot syntax (o.f = ...) from code that isn't in eval should be less likely to cause an object to become a dictionary
https://bugs.webkit.org/show_bug.cgi?id=119897
Reviewed by Oliver Hunt.
* fast/js/regress/lots-of-fields-expected.txt: Added.
* fast/js/regress/lots-of-fields.html: Added.
* fast/js/regress/script-tests/lots-of-fields.js: Added.
(foo):
2013-08-16 Jer Noble <jer.noble@apple.com>
[Mac] Unreviewed gardening. Rebaseline after r154124.
JSRegress/lots-of-fields
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/lots-of-fields.js"></script>
<script src="resources/regress-post.js"></script>
<script src="../resources/js-test-post.js"></script>
</body>
</html>
function foo() {
var result = 0;
for (var i = 0; i < 5000; ++i) {
var o = {};
o.i0 = 0;
o.i1 = 1;
o.i2 = 2;
o.i3 = 3;
o.i4 = 4;
o.i5 = 5;
o.i6 = 6;
o.i7 = 7;
o.i8 = 8;
o.i9 = 9;
o.i10 = 10;
o.i11 = 11;
o.i12 = 12;
o.i13 = 13;
o.i14 = 14;
o.i15 = 15;
o.i16 = 16;
o.i17 = 17;
o.i18 = 18;
o.i19 = 19;
o.i20 = 20;
o.i21 = 21;
o.i22 = 22;
o.i23 = 23;
o.i24 = 24;
o.i25 = 25;
o.i26 = 26;
o.i27 = 27;
o.i28 = 28;
o.i29 = 29;
o.i30 = 30;
o.i31 = 31;
o.i32 = 32;
o.i33 = 33;
o.i34 = 34;
o.i35 = 35;
o.i36 = 36;
o.i37 = 37;
o.i38 = 38;
o.i39 = 39;
o.i40 = 40;
o.i41 = 41;
o.i42 = 42;
o.i43 = 43;
o.i44 = 44;
o.i45 = 45;
o.i46 = 46;
o.i47 = 47;
o.i48 = 48;
o.i49 = 49;
o.i50 = 50;
o.i51 = 51;
o.i52 = 52;
o.i53 = 53;
o.i54 = 54;
o.i55 = 55;
o.i56 = 56;
o.i57 = 57;
o.i58 = 58;
o.i59 = 59;
o.i60 = 60;
o.i61 = 61;
o.i62 = 62;
o.i63 = 63;
o.i64 = 64;
o.i65 = 65;
o.i66 = 66;
o.i67 = 67;
o.i68 = 68;
o.i69 = 69;
o.i70 = 70;
o.i71 = 71;
o.i72 = 72;
o.i73 = 73;
o.i74 = 74;
o.i75 = 75;
o.i76 = 76;
o.i77 = 77;
o.i78 = 78;
o.i79 = 79;
o.i80 = 80;
o.i81 = 81;
o.i82 = 82;
o.i83 = 83;
o.i84 = 84;
o.i85 = 85;
o.i86 = 86;
o.i87 = 87;
o.i88 = 88;
o.i89 = 89;
o.i90 = 90;
o.i91 = 91;
o.i92 = 92;
o.i93 = 93;
o.i94 = 94;
o.i95 = 95;
o.i96 = 96;
o.i97 = 97;
o.i98 = 98;
o.i99 = 99;
o.i100 = 100;
o.i101 = 101;
o.i102 = 102;
o.i103 = 103;
o.i104 = 104;
o.i105 = 105;
o.i106 = 106;
o.i107 = 107;
o.i108 = 108;
o.i109 = 109;
o.i110 = 110;
o.i111 = 111;
o.i112 = 112;
o.i113 = 113;
o.i114 = 114;
o.i115 = 115;
o.i116 = 116;
o.i117 = 117;
o.i118 = 118;
o.i119 = 119;
o.i120 = 120;
o.i121 = 121;
o.i122 = 122;
o.i123 = 123;
o.i124 = 124;
o.i125 = 125;
o.i126 = 126;
o.i127 = 127;
o.i128 = 128;
o.i129 = 129;
o.i130 = 130;
o.i131 = 131;
o.i132 = 132;
o.i133 = 133;
o.i134 = 134;
o.i135 = 135;
o.i136 = 136;
o.i137 = 137;
o.i138 = 138;
o.i139 = 139;
o.i140 = 140;
o.i141 = 141;
o.i142 = 142;
o.i143 = 143;
o.i144 = 144;
o.i145 = 145;
o.i146 = 146;
o.i147 = 147;
o.i148 = 148;
o.i149 = 149;
o.i150 = 150;
o.i151 = 151;
o.i152 = 152;
o.i153 = 153;
o.i154 = 154;
o.i155 = 155;
o.i156 = 156;
o.i157 = 157;
o.i158 = 158;
o.i159 = 159;
o.i160 = 160;
o.i161 = 161;
o.i162 = 162;
o.i163 = 163;
o.i164 = 164;
o.i165 = 165;
o.i166 = 166;
o.i167 = 167;
o.i168 = 168;
o.i169 = 169;
o.i170 = 170;
o.i171 = 171;
o.i172 = 172;
o.i173 = 173;
o.i174 = 174;
o.i175 = 175;
o.i176 = 176;
o.i177 = 177;
o.i178 = 178;
o.i179 = 179;
o.i180 = 180;
o.i181 = 181;
o.i182 = 182;
o.i183 = 183;
o.i184 = 184;
o.i185 = 185;
o.i186 = 186;
o.i187 = 187;
o.i188 = 188;
o.i189 = 189;
o.i190 = 190;
o.i191 = 191;
o.i192 = 192;
o.i193 = 193;
o.i194 = 194;
o.i195 = 195;
o.i196 = 196;
o.i197 = 197;
o.i198 = 198;
o.i199 = 199;
for (var j = 0; j < 100; ++j)
result += o.i100;
}
return result;
}
var result = foo();
if (result != 50000000)
throw "Error: bad result: " + result;
2013-08-16 Filip Pizlo <fpizlo@apple.com>
Object properties added using dot syntax (o.f = ...) from code that isn't in eval should be less likely to cause an object to become a dictionary
https://bugs.webkit.org/show_bug.cgi?id=119897
Reviewed by Oliver Hunt.
6-10x speed-up on microbenchmarks that create large static objects. 40-65% speed-up
on Octane/gbemu. 3% overall speed-up on Octane. No slow-downs anywhere; our ability
to turn objects into dictionaries when you're storing using bracket syntax or using
eval is still in place.
* bytecode/CodeBlock.h:
(JSC::CodeBlock::putByIdContext):
* dfg/DFGOperations.cpp:
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* runtime/JSObject.h:
(JSC::JSObject::putDirectInternal):
* runtime/PutPropertySlot.h:
(JSC::PutPropertySlot::PutPropertySlot):
(JSC::PutPropertySlot::context):
* runtime/Structure.cpp:
(JSC::Structure::addPropertyTransition):
* runtime/Structure.h:
2013-08-16 Balazs Kilvady <kilvadyb@homejinni.com>
<https://webkit.org/b/119742> REGRESSION(FTL): Fix register usage in mips implementation of ctiVMHandleException
......
......@@ -55,6 +55,7 @@
#include "ObjectAllocationProfile.h"
#include "Options.h"
#include "Operations.h"
#include "PutPropertySlot.h"
#include "Instruction.h"
#include "JITCode.h"
#include "JITWriteBarrier.h"
......@@ -398,6 +399,12 @@ public:
}
CodeType codeType() const { return m_unlinkedCode->codeType(); }
PutPropertySlot::Context putByIdContext() const
{
if (codeType() == EvalCode)
return PutPropertySlot::PutByIdEval;
return PutPropertySlot::PutById;
}
SourceProvider* source() const { return m_source.get(); }
unsigned sourceOffset() const { return m_sourceOffset; }
......
......@@ -832,7 +832,7 @@ void DFG_OPERATION operationPutByIdStrict(ExecState* exec, EncodedJSValue encode
NativeCallFrameTracer tracer(vm, exec);
Identifier ident(vm, uid);
PutPropertySlot slot(true);
PutPropertySlot slot(true, exec->codeBlock()->putByIdContext());
base->methodTable()->put(base, exec, ident, JSValue::decode(encodedValue), slot);
}
......@@ -842,7 +842,7 @@ void DFG_OPERATION operationPutByIdNonStrict(ExecState* exec, EncodedJSValue enc
NativeCallFrameTracer tracer(vm, exec);
Identifier ident(vm, uid);
PutPropertySlot slot(false);
PutPropertySlot slot(false, exec->codeBlock()->putByIdContext());
base->methodTable()->put(base, exec, ident, JSValue::decode(encodedValue), slot);
}
......@@ -852,7 +852,7 @@ void DFG_OPERATION operationPutByIdDirectStrict(ExecState* exec, EncodedJSValue
NativeCallFrameTracer tracer(vm, exec);
Identifier ident(vm, uid);
PutPropertySlot slot(true);
PutPropertySlot slot(true, exec->codeBlock()->putByIdContext());
ASSERT(base->isObject());
asObject(base)->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot);
}
......@@ -863,7 +863,7 @@ void DFG_OPERATION operationPutByIdDirectNonStrict(ExecState* exec, EncodedJSVal
NativeCallFrameTracer tracer(vm, exec);
Identifier ident(vm, uid);
PutPropertySlot slot(false);
PutPropertySlot slot(false, exec->codeBlock()->putByIdContext());
ASSERT(base->isObject());
asObject(base)->putDirect(exec->vm(), ident, JSValue::decode(encodedValue), slot);
}
......@@ -880,7 +880,7 @@ void DFG_OPERATION operationPutByIdStrictOptimizeWithReturnAddress(ExecState* ex
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(true);
PutPropertySlot slot(true, exec->codeBlock()->putByIdContext());
baseValue.put(exec, ident, value, slot);
......@@ -905,7 +905,7 @@ void DFG_OPERATION operationPutByIdNonStrictOptimizeWithReturnAddress(ExecState*
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(false);
PutPropertySlot slot(false, exec->codeBlock()->putByIdContext());
baseValue.put(exec, ident, value, slot);
......@@ -929,7 +929,7 @@ void DFG_OPERATION operationPutByIdDirectStrictOptimizeWithReturnAddress(ExecSta
AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(true);
PutPropertySlot slot(true, exec->codeBlock()->putByIdContext());
ASSERT(base->isObject());
asObject(base)->putDirect(exec->vm(), ident, value, slot);
......@@ -954,7 +954,7 @@ void DFG_OPERATION operationPutByIdDirectNonStrictOptimizeWithReturnAddress(Exec
AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(false);
PutPropertySlot slot(false, exec->codeBlock()->putByIdContext());
ASSERT(base->isObject());
asObject(base)->putDirect(exec->vm(), ident, value, slot);
......@@ -980,7 +980,7 @@ void DFG_OPERATION operationPutByIdStrictBuildListWithReturnAddress(ExecState* e
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(true);
PutPropertySlot slot(true, exec->codeBlock()->putByIdContext());
baseValue.put(exec, ident, value, slot);
......@@ -1002,7 +1002,7 @@ void DFG_OPERATION operationPutByIdNonStrictBuildListWithReturnAddress(ExecState
JSValue value = JSValue::decode(encodedValue);
JSValue baseValue(base);
PutPropertySlot slot(false);
PutPropertySlot slot(false, exec->codeBlock()->putByIdContext());
baseValue.put(exec, ident, value, slot);
......@@ -1023,7 +1023,7 @@ void DFG_OPERATION operationPutByIdDirectStrictBuildListWithReturnAddress(ExecSt
AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(true);
PutPropertySlot slot(true, exec->codeBlock()->putByIdContext());
ASSERT(base->isObject());
asObject(base)->putDirect(exec->vm(), ident, value, slot);
......@@ -1045,7 +1045,7 @@ void DFG_OPERATION operationPutByIdDirectNonStrictBuildListWithReturnAddress(Exe
AccessType accessType = static_cast<AccessType>(stubInfo.accessType);
JSValue value = JSValue::decode(encodedValue);
PutPropertySlot slot(false);
PutPropertySlot slot(false, exec->codeBlock()->putByIdContext());
ASSERT(base->isObject());
asObject(base)->putDirect(exec->vm(), ident, value, slot);
......
......@@ -475,7 +475,9 @@ DEFINE_STUB_FUNCTION(void, op_put_by_id_generic)
{
STUB_INIT_STACK_FRAME(stackFrame);
PutPropertySlot slot(stackFrame.callFrame->codeBlock()->isStrictMode());
PutPropertySlot slot(
stackFrame.callFrame->codeBlock()->isStrictMode(),
stackFrame.callFrame->codeBlock()->putByIdContext());
stackFrame.args[0].jsValue().put(stackFrame.callFrame, stackFrame.args[1].identifier(), stackFrame.args[2].jsValue(), slot);
CHECK_FOR_EXCEPTION_AT_END();
}
......@@ -484,7 +486,9 @@ DEFINE_STUB_FUNCTION(void, op_put_by_id_direct_generic)
{
STUB_INIT_STACK_FRAME(stackFrame);
PutPropertySlot slot(stackFrame.callFrame->codeBlock()->isStrictMode());
PutPropertySlot slot(
stackFrame.callFrame->codeBlock()->isStrictMode(),
stackFrame.callFrame->codeBlock()->putByIdContext());
JSValue baseValue = stackFrame.args[0].jsValue();
ASSERT(baseValue.isObject());
asObject(baseValue)->putDirect(stackFrame.callFrame->vm(), stackFrame.args[1].identifier(), stackFrame.args[2].jsValue(), slot);
......@@ -516,7 +520,9 @@ DEFINE_STUB_FUNCTION(void, op_put_by_id)
StructureStubInfo* stubInfo = &codeBlock->getStubInfo(STUB_RETURN_ADDRESS);
AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
PutPropertySlot slot(callFrame->codeBlock()->isStrictMode());
PutPropertySlot slot(
callFrame->codeBlock()->isStrictMode(),
callFrame->codeBlock()->putByIdContext());
stackFrame.args[0].jsValue().put(callFrame, ident, stackFrame.args[2].jsValue(), slot);
if (accessType == static_cast<AccessType>(stubInfo->accessType)) {
......@@ -537,7 +543,9 @@ DEFINE_STUB_FUNCTION(void, op_put_by_id_direct)
StructureStubInfo* stubInfo = &codeBlock->getStubInfo(STUB_RETURN_ADDRESS);
AccessType accessType = static_cast<AccessType>(stubInfo->accessType);
PutPropertySlot slot(callFrame->codeBlock()->isStrictMode());
PutPropertySlot slot(
callFrame->codeBlock()->isStrictMode(),
callFrame->codeBlock()->putByIdContext());
JSValue baseValue = stackFrame.args[0].jsValue();
ASSERT(baseValue.isObject());
......@@ -558,7 +566,9 @@ DEFINE_STUB_FUNCTION(void, op_put_by_id_fail)
CallFrame* callFrame = stackFrame.callFrame;
Identifier& ident = stackFrame.args[1].identifier();
PutPropertySlot slot(callFrame->codeBlock()->isStrictMode());
PutPropertySlot slot(
callFrame->codeBlock()->isStrictMode(),
callFrame->codeBlock()->putByIdContext());
stackFrame.args[0].jsValue().put(callFrame, ident, stackFrame.args[2].jsValue(), slot);
CHECK_FOR_EXCEPTION_AT_END();
......@@ -571,7 +581,9 @@ DEFINE_STUB_FUNCTION(void, op_put_by_id_direct_fail)
CallFrame* callFrame = stackFrame.callFrame;
Identifier& ident = stackFrame.args[1].identifier();
PutPropertySlot slot(callFrame->codeBlock()->isStrictMode());
PutPropertySlot slot(
callFrame->codeBlock()->isStrictMode(),
callFrame->codeBlock()->putByIdContext());
JSValue baseValue = stackFrame.args[0].jsValue();
ASSERT(baseValue.isObject());
asObject(baseValue)->putDirect(callFrame->vm(), ident, stackFrame.args[2].jsValue(), slot);
......
......@@ -570,7 +570,7 @@ LLINT_SLOW_PATH_DECL(slow_path_put_by_id)
const Identifier& ident = codeBlock->identifier(pc[2].u.operand);
JSValue baseValue = LLINT_OP_C(1).jsValue();
PutPropertySlot slot(codeBlock->isStrictMode());
PutPropertySlot slot(codeBlock->isStrictMode(), codeBlock->putByIdContext());
if (pc[8].u.operand)
asObject(baseValue)->putDirect(vm, ident, LLINT_OP_C(3).jsValue(), slot);
else
......
......@@ -1347,7 +1347,7 @@ inline bool JSObject::putDirectInternal(VM& vm, PropertyName propertyName, JSVal
if ((mode == PutModePut) && !isExtensible())
return false;
Structure* structure = Structure::addPropertyTransition(vm, this->structure(), propertyName, attributes, specificFunction, offset);
Structure* structure = Structure::addPropertyTransition(vm, this->structure(), propertyName, attributes, specificFunction, offset, slot.context());
validateOffset(offset);
ASSERT(structure->isValidOffset(offset));
......
......@@ -37,11 +37,13 @@ namespace JSC {
class PutPropertySlot {
public:
enum Type { Uncachable, ExistingProperty, NewProperty };
enum Context { UnknownContext, PutById, PutByIdEval };
PutPropertySlot(bool isStrictMode = false)
PutPropertySlot(bool isStrictMode = false, Context context = UnknownContext)
: m_type(Uncachable)
, m_base(0)
, m_isStrictMode(isStrictMode)
, m_context(context)
{
}
......@@ -58,6 +60,8 @@ namespace JSC {
m_base = base;
m_offset = offset;
}
Context context() const { return static_cast<Context>(m_context); }
Type type() const { return m_type; }
JSObject* base() const { return m_base; }
......@@ -75,6 +79,7 @@ namespace JSC {
JSObject* m_base;
PropertyOffset m_offset;
bool m_isStrictMode;
uint8_t m_context;
};
} // namespace JSC
......
......@@ -380,7 +380,7 @@ NonPropertyTransition Structure::suggestedArrayStorageTransition() const
return AllocateArrayStorage;
}
Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset)
Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset, PutPropertySlot::Context context)
{
// If we have a specific function, we may have got to this point if there is
// already a transition with the correct property name and attributes, but
......@@ -399,7 +399,12 @@ Structure* Structure::addPropertyTransition(VM& vm, Structure* structure, Proper
if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount)
specificValue = 0;
if (structure->transitionCount() > s_maxTransitionLength) {
int maxTransitionLength;
if (context == PutPropertySlot::PutById)
maxTransitionLength = s_maxTransitionLengthForNonEvalPutById;
else
maxTransitionLength = s_maxTransitionLength;
if (structure->transitionCount() > maxTransitionLength) {
Structure* transition = toCacheableDictionaryTransition(vm, structure);
ASSERT(structure != transition);
offset = transition->putSpecificValue(vm, propertyName, attributes, specificValue);
......
......@@ -36,6 +36,7 @@
#include "PropertyNameArray.h"
#include "PropertyOffset.h"
#include "Protect.h"
#include "PutPropertySlot.h"
#include "StructureRareData.h"
#include "StructureTransitionTable.h"
#include "JSTypeInfo.h"
......@@ -95,7 +96,7 @@ protected:
public:
static void dumpStatistics();
JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
JS_EXPORT_PRIVATE static Structure* addPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext);
static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, StringImpl* uid, unsigned attributes, JSCell* specificValue, PropertyOffset&);
JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, JSCell* specificValue, PropertyOffset&);
static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
......@@ -464,6 +465,7 @@ private:
void cloneRareDataFrom(VM&, const Structure*);
static const int s_maxTransitionLength = 64;
static const int s_maxTransitionLengthForNonEvalPutById = 512;
static const unsigned maxSpecificFunctionThrashCount = 3;
......
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