Do not waste space for the fast-case elements backing storage for
authorager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 11 Mar 2010 10:34:29 +0000 (10:34 +0000)
committerager@chromium.org <ager@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 11 Mar 2010 10:34:29 +0000 (10:34 +0000)
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

src/arm/codegen-arm.cc
src/arm/full-codegen-arm.cc
src/ast.h
src/ia32/codegen-ia32.cc
src/ia32/full-codegen-ia32.cc
src/parser.cc
src/parser.h
src/runtime.cc
src/runtime.h
src/x64/codegen-x64.cc
src/x64/full-codegen-x64.cc

index eceba55e2a89a5bf0ae1f5bae5ba87667e58e6e6..3bf3fb8ccf91d9edf7fcad5313b970d08aa0c5f0 100644 (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++) {
index 230818f5d13de828e06e9f0fd1334308f49bae13..a70cf44f800ebdafec74d06e28372680fa8a75db 100644 (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
index d5c85bf3327adeb2a2b125172ac0242fc6430c45..174679ab83c138e3190e6bda3a158baabb89a9e3 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -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_;
 };
 
 
index 5c356a16b2b40d68ab845bde26d24decaa148145..ca4bb1927caeec66b38efeac163386fbcb9e29c9 100644 (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);
 
index 6e3ae105d1ab94997481c8888ebd08e8539a4bcf..cedf9c95fd3b80de27701654ab22a62b4f796fbf 100644 (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
index 57cb0b5666ce8eab33eccf8f49c69015db98fef7..4c5a5299694b09886c90b1083f2b38d22d335c9e 100644 (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);
 }
 
index 0f808d7262ccce706fdd685a167b2efda340cfb6..2e5daf961cc2fd00de1dfe590d1f316c4d14aa7c 100644 (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
   };
 
index c88bb85c95a0e686fa04664700b38c53b5ed01f2..36a2e7189557ad15c777a22c6a74fd3c0beec87e 100644 (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);
index b8891bd1243b722866010904cf6cfe02b9634759..fa5506320f52219d0d6339a7348d3cc26896359a 100644 (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) \
   \
index c8abb66c8cb1abc0604edc79ad32526da5e308b1..f5a8d3c07f09936b1575983650007499767ef802 100644 (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);
 
index bfa1a44f9df1ece6b80c3ebc8e1165e425c92e34..65f99a3fe24c359970d4cf6e48a353b0dedfa8ff 100644 (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