Optimize the allocation of small, non-nested literal
authorkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 17 Dec 2009 15:35:15 +0000 (15:35 +0000)
committerkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 17 Dec 2009 15:35:15 +0000 (15:35 +0000)
arrays and argument objects on IA-32.
Review URL: http://codereview.chromium.org/503042

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3485 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/code-stubs.h
src/codegen.h
src/heap.cc
src/heap.h
src/ia32/codegen-ia32.cc

index 04127ea52e7d9cad76986b1c008ba1af9bb3e4ef..fee92b9513766e31df2a7ac68bc8247d31ffc59d 100644 (file)
@@ -45,6 +45,7 @@ namespace internal {
   V(StackCheck)                          \
   V(FastNewClosure)                      \
   V(FastNewContext)                      \
+  V(FastCloneShallowArray)               \
   V(UnarySub)                            \
   V(RevertToNumber)                      \
   V(ToBoolean)                           \
index 411479264fd189d6d3652aebda2e5cdd97ff10d9..b3cf51ed5d8a64180c810a21fa4b883f1634a740 100644 (file)
@@ -263,6 +263,25 @@ class FastNewContextStub : public CodeStub {
 };
 
 
+class FastCloneShallowArrayStub : public CodeStub {
+ public:
+  static const int kMaximumLength = 8;
+
+  explicit FastCloneShallowArrayStub(int length) : length_(length) {
+    ASSERT(length >= 0 && length <= kMaximumLength);
+  }
+
+  void Generate(MacroAssembler* masm);
+
+ private:
+  int length_;
+
+  const char* GetName() { return "FastCloneShallowArrayStub"; }
+  Major MajorKey() { return FastCloneShallowArray; }
+  int MinorKey() { return length_; }
+};
+
+
 class InstanceofStub: public CodeStub {
  public:
   InstanceofStub() { }
index a4e9b653d8f521bfa3f23a0b82efc31120c64ae6..7a66038b6d9a9a9fc42e134a2b038704dee9fd56 100644 (file)
@@ -2261,10 +2261,14 @@ Object* Heap::AllocateArgumentsObject(Object* callee, int length) {
   JSObject* boilerplate =
       Top::context()->global_context()->arguments_boilerplate();
 
-  // Make the clone.
-  Map* map = boilerplate->map();
-  int object_size = map->instance_size();
-  Object* result = AllocateRaw(object_size, NEW_SPACE, OLD_POINTER_SPACE);
+  // Check that the size of the boilerplate matches our
+  // expectations. The ArgumentsAccessStub::GenerateNewObject relies
+  // on the size being a known constant.
+  ASSERT(kArgumentsObjectSize == boilerplate->map()->instance_size());
+
+  // Do the allocation.
+  Object* result =
+      AllocateRaw(kArgumentsObjectSize, NEW_SPACE, OLD_POINTER_SPACE);
   if (result->IsFailure()) return result;
 
   // Copy the content. The arguments boilerplate doesn't have any
@@ -2272,7 +2276,7 @@ Object* Heap::AllocateArgumentsObject(Object* callee, int length) {
   // barrier here.
   CopyBlock(reinterpret_cast<Object**>(HeapObject::cast(result)->address()),
             reinterpret_cast<Object**>(boilerplate->address()),
-            object_size);
+            kArgumentsObjectSize);
 
   // Set the two properties.
   JSObject::cast(result)->InObjectPropertyAtPut(arguments_callee_index,
index daac3c56ec581a58e0841bd602fdac3cb5766423..fdc04a8ea998e486386e2ea23889681d77589b95 100644 (file)
@@ -491,6 +491,8 @@ class Heap : public AllStatic {
                                   PretenureFlag pretenure = TENURED);
 
   // Indicies for direct access into argument objects.
+  static const int kArgumentsObjectSize =
+      JSObject::kHeaderSize + 2 * kPointerSize;
   static const int arguments_callee_index = 0;
   static const int arguments_length_index = 1;
 
index 5c3a2c64b9646d260a8d9fc0b7bec7fbf17432c9..69310d6b50ea5fd5e574e0102695128d0c68559c 100644 (file)
@@ -4320,18 +4320,23 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
 
   // Push the resulting array literal boilerplate on the stack.
   frame_->Push(&boilerplate);
+
   // Clone the boilerplate object.
-  Runtime::FunctionId clone_function_id = Runtime::kCloneLiteralBoilerplate;
-  if (node->depth() == 1) {
-    clone_function_id = Runtime::kCloneShallowLiteralBoilerplate;
+  int length = node->values()->length();
+  Result clone;
+  if (node->depth() == 1 &&
+      length <= FastCloneShallowArrayStub::kMaximumLength) {
+    FastCloneShallowArrayStub stub(length);
+    clone = frame_->CallStub(&stub, 1);
+  } else {
+    clone = frame_->CallRuntime(Runtime::kCloneLiteralBoilerplate, 1);
   }
-  Result clone = frame_->CallRuntime(clone_function_id, 1);
   // Push the newly cloned literal object as the result.
   frame_->Push(&clone);
 
   // Generate code to set the elements in the array that are not
   // literals.
-  for (int i = 0; i < node->values()->length(); i++) {
+  for (int i = 0; i < length; i++) {
     Expression* value = node->values()->at(i);
 
     // If value is a literal the property value is already set in the
@@ -6637,6 +6642,49 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
 }
 
 
+void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
+  int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
+  int size = JSArray::kSize + elements_size;
+
+  // Allocate both the JS array and the elements array in one big
+  // allocation. This avoid multiple limit checks.
+  Label gc;
+  __ AllocateInNewSpace(size, eax, ebx, ecx, &gc, TAG_OBJECT);
+
+  // Get the boilerplate from the stack.
+  __ mov(ecx, Operand(esp, 1 * kPointerSize));
+
+  // Copy the JS array part.
+  for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
+    if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
+      __ mov(ebx, FieldOperand(ecx, i));
+      __ mov(FieldOperand(eax, i), ebx);
+    }
+  }
+
+  if (length_ > 0) {
+    // Get hold of the elements array of the boilerplate and setup the
+    // elements pointer in the resulting object.
+    __ mov(ecx, FieldOperand(ecx, JSArray::kElementsOffset));
+    __ lea(edx, Operand(eax, JSArray::kSize));
+    __ mov(FieldOperand(eax, JSArray::kElementsOffset), edx);
+
+    // Copy the elements array.
+    for (int i = 0; i < elements_size; i += kPointerSize) {
+      __ mov(ebx, FieldOperand(ecx, i));
+      __ mov(FieldOperand(edx, i), ebx);
+    }
+  }
+
+  // Return and remove the on-stack parameter.
+  __ ret(1 * kPointerSize);
+
+  __ bind(&gc);
+  ExternalReference runtime(Runtime::kCloneShallowLiteralBoilerplate);
+  __ TailCallRuntime(runtime, 1, 1);
+}
+
+
 // NOTE: The stub does not handle the inlined cases (Smis, Booleans, undefined).
 void ToBooleanStub::Generate(MacroAssembler* masm) {
   Label false_result, true_result, not_string;
@@ -7549,18 +7597,90 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
   static const int kDisplacement = 2 * kPointerSize;
 
   // Check if the calling frame is an arguments adaptor frame.
-  Label runtime;
+  Label adaptor_frame, try_allocate, runtime;
   __ mov(edx, Operand(ebp, StandardFrameConstants::kCallerFPOffset));
   __ mov(ecx, Operand(edx, StandardFrameConstants::kContextOffset));
   __ cmp(Operand(ecx), Immediate(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
-  __ j(not_equal, &runtime);
+  __ j(equal, &adaptor_frame);
+
+  // Get the length from the frame.
+  __ mov(ecx, Operand(esp, 1 * kPointerSize));
+  __ jmp(&try_allocate);
 
   // Patch the arguments.length and the parameters pointer.
+  __ bind(&adaptor_frame);
   __ mov(ecx, Operand(edx, ArgumentsAdaptorFrameConstants::kLengthOffset));
   __ mov(Operand(esp, 1 * kPointerSize), ecx);
   __ lea(edx, Operand(edx, ecx, times_2, kDisplacement));
   __ mov(Operand(esp, 2 * kPointerSize), edx);
 
+  // Try the new space allocation. Start out with computing the size of
+  // the arguments object and the elements array.
+  Label add_arguments_object;
+  __ bind(&try_allocate);
+  __ test(ecx, Operand(ecx));
+  __ j(zero, &add_arguments_object);
+  __ lea(ecx, Operand(ecx, times_2, FixedArray::kHeaderSize));
+  __ bind(&add_arguments_object);
+  __ add(Operand(ecx), Immediate(Heap::kArgumentsObjectSize));
+
+  // Do the allocation of both objects in one go.
+  __ AllocateInNewSpace(ecx, eax, edx, ebx, &runtime, TAG_OBJECT);
+
+  // Get the arguments boilerplate from the current (global) context.
+  int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
+  __ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+  __ mov(edi, FieldOperand(edi, GlobalObject::kGlobalContextOffset));
+  __ mov(edi, Operand(edi, offset));
+
+  // Copy the JS object part.
+  for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+    __ mov(ebx, FieldOperand(edi, i));
+    __ mov(FieldOperand(eax, i), ebx);
+  }
+
+  // Setup the callee in-object property.
+  ASSERT(Heap::arguments_callee_index == 0);
+  __ mov(ebx, Operand(esp, 3 * kPointerSize));
+  __ mov(FieldOperand(eax, JSObject::kHeaderSize), ebx);
+
+  // Get the length (smi tagged) and set that as an in-object property too.
+  ASSERT(Heap::arguments_length_index == 1);
+  __ mov(ecx, Operand(esp, 1 * kPointerSize));
+  __ mov(FieldOperand(eax, JSObject::kHeaderSize + kPointerSize), ecx);
+
+  // If there are no actual arguments, we're done.
+  Label done;
+  __ test(ecx, Operand(ecx));
+  __ j(zero, &done);
+
+  // Get the parameters pointer from the stack and untag the length.
+  __ mov(edx, Operand(esp, 2 * kPointerSize));
+  __ sar(ecx, kSmiTagSize);
+
+  // Setup the elements pointer in the allocated arguments object and
+  // initialize the header in the elements fixed array.
+  __ lea(edi, Operand(eax, Heap::kArgumentsObjectSize));
+  __ mov(FieldOperand(eax, JSObject::kElementsOffset), edi);
+  __ mov(FieldOperand(edi, FixedArray::kMapOffset),
+         Immediate(Factory::fixed_array_map()));
+  __ mov(FieldOperand(edi, FixedArray::kLengthOffset), ecx);
+
+  // Copy the fixed array slots.
+  Label loop;
+  __ bind(&loop);
+  __ mov(ebx, Operand(edx, -1 * kPointerSize));  // Skip receiver.
+  __ mov(FieldOperand(edi, FixedArray::kHeaderSize), ebx);
+  __ add(Operand(edi), Immediate(kPointerSize));
+  __ sub(Operand(edx), Immediate(kPointerSize));
+  __ dec(ecx);
+  __ test(ecx, Operand(ecx));
+  __ j(not_zero, &loop);
+
+  // Return and remove the on-stack parameters.
+  __ bind(&done);
+  __ ret(3 * kPointerSize);
+
   // Do the runtime call to allocate the arguments object.
   __ bind(&runtime);
   __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);