Revision: 3485
Author: [email protected]
Date: Thu Dec 17 07:35:15 2009
Log: Optimize the allocation of small, non-nested literal
arrays and argument objects on IA-32.
Review URL: http://codereview.chromium.org/503042
http://code.google.com/p/v8/source/detail?r=3485

Modified:
  /branches/bleeding_edge/src/code-stubs.h
  /branches/bleeding_edge/src/codegen.h
  /branches/bleeding_edge/src/heap.cc
  /branches/bleeding_edge/src/heap.h
  /branches/bleeding_edge/src/ia32/codegen-ia32.cc

=======================================
--- /branches/bleeding_edge/src/code-stubs.h    Thu Dec 17 00:53:19 2009
+++ /branches/bleeding_edge/src/code-stubs.h    Thu Dec 17 07:35:15 2009
@@ -45,6 +45,7 @@
    V(StackCheck)                          \
    V(FastNewClosure)                      \
    V(FastNewContext)                      \
+  V(FastCloneShallowArray)               \
    V(UnarySub)                            \
    V(RevertToNumber)                      \
    V(ToBoolean)                           \
=======================================
--- /branches/bleeding_edge/src/codegen.h       Thu Dec 17 00:53:19 2009
+++ /branches/bleeding_edge/src/codegen.h       Thu Dec 17 07:35:15 2009
@@ -263,6 +263,25 @@
  };


+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() { }
=======================================
--- /branches/bleeding_edge/src/heap.cc Thu Dec 17 00:53:18 2009
+++ /branches/bleeding_edge/src/heap.cc Thu Dec 17 07:35:15 2009
@@ -2261,10 +2261,14 @@
    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 @@
    // 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,
=======================================
--- /branches/bleeding_edge/src/heap.h  Thu Dec 17 00:53:18 2009
+++ /branches/bleeding_edge/src/heap.h  Thu Dec 17 07:35:15 2009
@@ -491,6 +491,8 @@
                                    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;

=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Thu Dec 17 02:23:20  
2009
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Thu Dec 17 07:35:15  
2009
@@ -4320,18 +4320,23 @@

    // 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;
-  }
-  Result clone = frame_->CallRuntime(clone_function_id, 1);
+  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);
+  }
    // 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
@@ -6635,6 +6640,49 @@
    __ bind(&gc);
    __ TailCallRuntime(ExternalReference(Runtime::kNewContext), 1, 1);
  }
+
+
+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).
@@ -7549,18 +7597,90 @@
    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);

-- 
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to