Do not waste space for the fast-case elements backing storage for

object-literals with few elements but large element indices.

We can decide at parse time whether the created object literal should
have fast-case of slow-case elements.

Remove unused runtime function.
Review URL: http://codereview.chromium.org/805004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@4100 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
ager@chromium.org 2010-03-11 10:34:29 +00:00
parent 7d933936d3
commit ef371f3f24
11 changed files with 99 additions and 56 deletions

View File

@ -2709,18 +2709,20 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
Comment cmnt(masm_, "[ ObjectLiteral");
// Load the function of this activation.
__ ldr(r2, frame_->Function());
__ ldr(r3, frame_->Function());
// Literal array.
__ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset));
__ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
// Literal index.
__ mov(r1, Operand(Smi::FromInt(node->literal_index())));
__ mov(r2, Operand(Smi::FromInt(node->literal_index())));
// Constant properties.
__ mov(r0, Operand(node->constant_properties()));
frame_->EmitPushMultiple(3, r2.bit() | r1.bit() | r0.bit());
__ mov(r1, Operand(node->constant_properties()));
// Should the object literal have fast elements?
__ mov(r0, Operand(Smi::FromInt(node->fast_elements() ? 1 : 0)));
frame_->EmitPushMultiple(4, r3.bit() | r2.bit() | r1.bit() | r0.bit());
if (node->depth() > 1) {
frame_->CallRuntime(Runtime::kCreateObjectLiteral, 3);
frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
frame_->EmitPush(r0); // save the result
for (int i = 0; i < node->properties()->length(); i++) {

View File

@ -783,15 +783,16 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Comment cmnt(masm_, "[ ObjectLiteral");
__ ldr(r2, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset));
__ mov(r1, Operand(Smi::FromInt(expr->literal_index())));
__ mov(r0, Operand(expr->constant_properties()));
__ stm(db_w, sp, r2.bit() | r1.bit() | r0.bit());
__ ldr(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
__ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
__ mov(r2, Operand(Smi::FromInt(expr->literal_index())));
__ mov(r1, Operand(expr->constant_properties()));
__ mov(r0, Operand(Smi::FromInt(expr->fast_elements() ? 1 : 0)));
__ stm(db_w, sp, r3.bit() | r2.bit() | r1.bit() | r0.bit());
if (expr->depth() > 1) {
__ CallRuntime(Runtime::kCreateObjectLiteral, 3);
__ CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
__ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
__ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
// If result_saved is true the result is on top of the stack. If

View File

@ -871,10 +871,12 @@ class ObjectLiteral: public MaterializedLiteral {
ZoneList<Property*>* properties,
int literal_index,
bool is_simple,
bool fast_elements,
int depth)
: MaterializedLiteral(literal_index, is_simple, depth),
constant_properties_(constant_properties),
properties_(properties) {}
properties_(properties),
fast_elements_(fast_elements) {}
virtual ObjectLiteral* AsObjectLiteral() { return this; }
virtual void Accept(AstVisitor* v);
@ -886,9 +888,12 @@ class ObjectLiteral: public MaterializedLiteral {
}
ZoneList<Property*>* properties() const { return properties_; }
bool fast_elements() const { return fast_elements_; }
private:
Handle<FixedArray> constant_properties_;
ZoneList<Property*>* properties_;
bool fast_elements_;
};

View File

@ -4780,11 +4780,13 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
frame_->Push(Smi::FromInt(node->literal_index()));
// Constant properties.
frame_->Push(node->constant_properties());
// Should the object literal have fast elements?
frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
Result clone;
if (node->depth() > 1) {
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 3);
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
frame_->Push(&clone);

View File

@ -901,10 +901,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ push(FieldOperand(edi, JSFunction::kLiteralsOffset));
__ push(Immediate(Smi::FromInt(expr->literal_index())));
__ push(Immediate(expr->constant_properties()));
__ push(Immediate(Smi::FromInt(expr->fast_elements() ? 1 : 0)));
if (expr->depth() > 1) {
__ CallRuntime(Runtime::kCreateObjectLiteral, 3);
__ CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
__ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
__ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
// If result_saved is true the result is on top of the stack. If

View File

@ -212,6 +212,7 @@ class Parser {
ZoneList<ObjectLiteral::Property*>* properties,
Handle<FixedArray> constants,
bool* is_simple,
bool* fast_elements,
int* depth);
// Populate the literals fixed array for a materialized array literal.
@ -3446,7 +3447,11 @@ Handle<FixedArray> CompileTimeValue::GetValue(Expression* expression) {
ObjectLiteral* object_literal = expression->AsObjectLiteral();
if (object_literal != NULL) {
ASSERT(object_literal->is_simple());
result->set(kTypeSlot, Smi::FromInt(OBJECT_LITERAL));
if (object_literal->fast_elements()) {
result->set(kTypeSlot, Smi::FromInt(OBJECT_LITERAL_FAST_ELEMENTS));
} else {
result->set(kTypeSlot, Smi::FromInt(OBJECT_LITERAL_SLOW_ELEMENTS));
}
result->set(kElementsSlot, *object_literal->constant_properties());
} else {
ArrayLiteral* array_literal = expression->AsArrayLiteral();
@ -3484,11 +3489,14 @@ void Parser::BuildObjectLiteralConstantProperties(
ZoneList<ObjectLiteral::Property*>* properties,
Handle<FixedArray> constant_properties,
bool* is_simple,
bool* fast_elements,
int* depth) {
int position = 0;
// Accumulate the value in local variables and store it at the end.
bool is_simple_acc = true;
int depth_acc = 1;
uint32_t max_element_index = 0;
uint32_t elements = 0;
for (int i = 0; i < properties->length(); i++) {
ObjectLiteral::Property* property = properties->at(i);
if (!IsBoilerplateProperty(property)) {
@ -3507,11 +3515,31 @@ void Parser::BuildObjectLiteralConstantProperties(
Handle<Object> value = GetBoilerplateValue(property->value());
is_simple_acc = is_simple_acc && !value->IsUndefined();
// Keep track of the number of elements in the object literal and
// the largest element index. If the largest element index is
// much larger than the number of elements, creating an object
// literal with fast elements will be a waste of space.
uint32_t element_index = 0;
if (key->IsString()
&& Handle<String>::cast(key)->AsArrayIndex(&element_index)
&& element_index > max_element_index) {
max_element_index = element_index;
elements++;
} else if (key->IsSmi()) {
int key_value = Smi::cast(*key)->value();
if (key_value > 0
&& static_cast<uint32_t>(key_value) > max_element_index) {
max_element_index = key_value;
}
elements++;
}
// Add name, value pair to the fixed array.
constant_properties->set(position++, *key);
constant_properties->set(position++, *value);
}
*fast_elements =
(max_element_index <= 32) || ((2 * elements) >= max_element_index);
*is_simple = is_simple_acc;
*depth = depth_acc;
}
@ -3609,15 +3637,18 @@ Expression* Parser::ParseObjectLiteral(bool* ok) {
Factory::NewFixedArray(number_of_boilerplate_properties * 2, TENURED);
bool is_simple = true;
bool fast_elements = true;
int depth = 1;
BuildObjectLiteralConstantProperties(properties.elements(),
constant_properties,
&is_simple,
&fast_elements,
&depth);
return new ObjectLiteral(constant_properties,
properties.elements(),
literal_index,
is_simple,
fast_elements,
depth);
}
@ -4144,15 +4175,18 @@ Expression* Parser::ParseJsonObject(bool* ok) {
Handle<FixedArray> constant_properties =
Factory::NewFixedArray(boilerplate_properties * 2, TENURED);
bool is_simple = true;
bool fast_elements = true;
int depth = 1;
BuildObjectLiteralConstantProperties(properties.elements(),
constant_properties,
&is_simple,
&fast_elements,
&depth);
return new ObjectLiteral(constant_properties,
properties.elements(),
literal_index,
is_simple,
fast_elements,
depth);
}

View File

@ -168,7 +168,8 @@ FunctionLiteral* MakeLazyAST(Handle<Script> script,
class CompileTimeValue: public AllStatic {
public:
enum Type {
OBJECT_LITERAL,
OBJECT_LITERAL_FAST_ELEMENTS,
OBJECT_LITERAL_SLOW_ELEMENTS,
ARRAY_LITERAL
};

View File

@ -248,7 +248,8 @@ static Handle<Object> CreateLiteralBoilerplate(
static Handle<Object> CreateObjectLiteralBoilerplate(
Handle<FixedArray> literals,
Handle<FixedArray> constant_properties) {
Handle<FixedArray> constant_properties,
bool should_have_fast_elements) {
// Get the global context from the literals array. This is the
// context in which the function was created and we use the object
// function from this context to create the object literal. We do
@ -264,6 +265,10 @@ static Handle<Object> CreateObjectLiteralBoilerplate(
&is_result_from_cache);
Handle<JSObject> boilerplate = Factory::NewJSObjectFromMap(map);
// Normalize the elements of the boilerplate to save space if needed.
if (!should_have_fast_elements) NormalizeElements(boilerplate);
{ // Add the constant properties to the boilerplate.
int length = constant_properties->length();
OptimizedObjectForAddingMultipleProperties opt(boilerplate,
@ -345,8 +350,10 @@ static Handle<Object> CreateLiteralBoilerplate(
Handle<FixedArray> array) {
Handle<FixedArray> elements = CompileTimeValue::GetElements(array);
switch (CompileTimeValue::GetType(array)) {
case CompileTimeValue::OBJECT_LITERAL:
return CreateObjectLiteralBoilerplate(literals, elements);
case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS:
return CreateObjectLiteralBoilerplate(literals, elements, true);
case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS:
return CreateObjectLiteralBoilerplate(literals, elements, false);
case CompileTimeValue::ARRAY_LITERAL:
return CreateArrayLiteralBoilerplate(literals, elements);
default:
@ -356,26 +363,6 @@ static Handle<Object> CreateLiteralBoilerplate(
}
static Object* Runtime_CreateObjectLiteralBoilerplate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
// Copy the arguments.
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
CONVERT_SMI_CHECKED(literals_index, args[1]);
CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
Handle<Object> result =
CreateObjectLiteralBoilerplate(literals, constant_properties);
if (result.is_null()) return Failure::Exception();
// Update the functions literal and return the boilerplate.
literals->set(literals_index, *result);
return *result;
}
static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
// Takes a FixedArray of elements containing the literal elements of
// the array literal and produces JSArray with those elements.
@ -399,15 +386,19 @@ static Object* Runtime_CreateArrayLiteralBoilerplate(Arguments args) {
static Object* Runtime_CreateObjectLiteral(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
ASSERT(args.length() == 4);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
CONVERT_SMI_CHECKED(literals_index, args[1]);
CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
CONVERT_SMI_CHECKED(fast_elements, args[3]);
bool should_have_fast_elements = fast_elements == 1;
// Check if boilerplate exists. If not, create it first.
Handle<Object> boilerplate(literals->get(literals_index));
if (*boilerplate == Heap::undefined_value()) {
boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
boilerplate = CreateObjectLiteralBoilerplate(literals,
constant_properties,
should_have_fast_elements);
if (boilerplate.is_null()) return Failure::Exception();
// Update the functions literal and return the boilerplate.
literals->set(literals_index, *boilerplate);
@ -418,15 +409,19 @@ static Object* Runtime_CreateObjectLiteral(Arguments args) {
static Object* Runtime_CreateObjectLiteralShallow(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 3);
ASSERT(args.length() == 4);
CONVERT_ARG_CHECKED(FixedArray, literals, 0);
CONVERT_SMI_CHECKED(literals_index, args[1]);
CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2);
CONVERT_SMI_CHECKED(fast_elements, args[3]);
bool should_have_fast_elements = fast_elements == 1;
// Check if boilerplate exists. If not, create it first.
Handle<Object> boilerplate(literals->get(literals_index));
if (*boilerplate == Heap::undefined_value()) {
boilerplate = CreateObjectLiteralBoilerplate(literals, constant_properties);
boilerplate = CreateObjectLiteralBoilerplate(literals,
constant_properties,
should_have_fast_elements);
if (boilerplate.is_null()) return Failure::Exception();
// Update the functions literal and return the boilerplate.
literals->set(literals_index, *boilerplate);

View File

@ -233,11 +233,10 @@ namespace internal {
/* Literals */ \
F(MaterializeRegExpLiteral, 4, 1)\
F(CreateArrayLiteralBoilerplate, 3, 1) \
F(CreateObjectLiteralBoilerplate, 3, 1) \
F(CloneLiteralBoilerplate, 1, 1) \
F(CloneShallowLiteralBoilerplate, 1, 1) \
F(CreateObjectLiteral, 3, 1) \
F(CreateObjectLiteralShallow, 3, 1) \
F(CreateObjectLiteral, 4, 1) \
F(CreateObjectLiteralShallow, 4, 1) \
F(CreateArrayLiteral, 3, 1) \
F(CreateArrayLiteralShallow, 3, 1) \
\

View File

@ -2424,11 +2424,13 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
frame_->Push(Smi::FromInt(node->literal_index()));
// Constant properties.
frame_->Push(node->constant_properties());
// Should the object literal have fast elements?
frame_->Push(Smi::FromInt(node->fast_elements() ? 1 : 0));
Result clone;
if (node->depth() > 1) {
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 3);
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
clone = frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
frame_->Push(&clone);

View File

@ -903,10 +903,11 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
__ push(FieldOperand(rdi, JSFunction::kLiteralsOffset));
__ Push(Smi::FromInt(expr->literal_index()));
__ Push(expr->constant_properties());
__ Push(Smi::FromInt(expr->fast_elements() ? 1 : 0));
if (expr->depth() > 1) {
__ CallRuntime(Runtime::kCreateObjectLiteral, 3);
__ CallRuntime(Runtime::kCreateObjectLiteral, 4);
} else {
__ CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
__ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
}
// If result_saved is true the result is on top of the stack. If