Revision: 3477
Author: [email protected]
Date: Wed Dec 16 07:43:20 2009
Log: Improve performance of allocating closures for nested
functions by allocating them in new space without
entering the runtime system.
Review URL: http://codereview.chromium.org/506037
http://code.google.com/p/v8/source/detail?r=3477

Modified:
  /branches/bleeding_edge/src/code-stubs.h
  /branches/bleeding_edge/src/codegen.h
  /branches/bleeding_edge/src/factory.cc
  /branches/bleeding_edge/src/factory.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/ia32/stub-cache-ia32.cc
  /branches/bleeding_edge/src/objects-inl.h
  /branches/bleeding_edge/src/objects.cc
  /branches/bleeding_edge/src/objects.h
  /branches/bleeding_edge/src/runtime.cc

=======================================
--- /branches/bleeding_edge/src/code-stubs.h    Wed Dec  9 06:54:34 2009
+++ /branches/bleeding_edge/src/code-stubs.h    Wed Dec 16 07:43:20 2009
@@ -43,6 +43,7 @@
    V(ConvertToDouble)                     \
    V(WriteInt32ToHeapNumber)              \
    V(StackCheck)                          \
+  V(FastNewClosure)                      \
    V(UnarySub)                            \
    V(RevertToNumber)                      \
    V(ToBoolean)                           \
=======================================
--- /branches/bleeding_edge/src/codegen.h       Mon Nov 16 13:59:31 2009
+++ /branches/bleeding_edge/src/codegen.h       Wed Dec 16 07:43:20 2009
@@ -233,6 +233,17 @@
  };


+class FastNewClosureStub : public CodeStub {
+ public:
+  void Generate(MacroAssembler* masm);
+
+ private:
+  const char* GetName() { return "FastNewClosureStub"; }
+  Major MajorKey() { return FastNewClosure; }
+  int MinorKey() { return 0; }
+};
+
+
  class InstanceofStub: public CodeStub {
   public:
    InstanceofStub() { }
=======================================
--- /branches/bleeding_edge/src/factory.cc      Tue Dec  1 06:36:45 2009
+++ /branches/bleeding_edge/src/factory.cc      Wed Dec 16 07:43:20 2009
@@ -284,7 +284,8 @@

  Handle<JSFunction> Factory::BaseNewFunctionFromBoilerplate(
      Handle<JSFunction> boilerplate,
-    Handle<Map> function_map) {
+    Handle<Map> function_map,
+    PretenureFlag pretenure) {
    ASSERT(boilerplate->IsBoilerplate());
    ASSERT(!boilerplate->has_initial_map());
    ASSERT(!boilerplate->has_prototype());
@@ -292,20 +293,22 @@
    ASSERT(boilerplate->elements() == Heap::empty_fixed_array());
    CALL_HEAP_FUNCTION(Heap::AllocateFunction(*function_map,
                                              boilerplate->shared(),
-                                            Heap::the_hole_value()),
+                                            Heap::the_hole_value(),
+                                            pretenure),
                       JSFunction);
  }


  Handle<JSFunction> Factory::NewFunctionFromBoilerplate(
      Handle<JSFunction> boilerplate,
-    Handle<Context> context) {
-  Handle<JSFunction> result =
-      BaseNewFunctionFromBoilerplate(boilerplate, Top::function_map());
+    Handle<Context> context,
+    PretenureFlag pretenure) {
+  Handle<JSFunction> result = BaseNewFunctionFromBoilerplate(
+      boilerplate, Top::function_map(), pretenure);
    result->set_context(*context);
    int number_of_literals = boilerplate->NumberOfLiterals();
    Handle<FixedArray> literals =
-      Factory::NewFixedArray(number_of_literals, TENURED);
+      Factory::NewFixedArray(number_of_literals, pretenure);
    if (number_of_literals > 0) {
      // Store the object, regexp and array functions in the literals
      // array prefix.  These functions will be used when creating
=======================================
--- /branches/bleeding_edge/src/factory.h       Tue Nov 10 05:23:05 2009
+++ /branches/bleeding_edge/src/factory.h       Wed Dec 16 07:43:20 2009
@@ -219,7 +219,8 @@

    static Handle<JSFunction> NewFunctionFromBoilerplate(
        Handle<JSFunction> boilerplate,
-      Handle<Context> context);
+      Handle<Context> context,
+      PretenureFlag pretenure = TENURED);

    static Handle<Code> NewCode(const CodeDesc& desc,
                                ZoneScopeInfo* sinfo,
@@ -374,7 +375,8 @@

    static Handle<JSFunction> BaseNewFunctionFromBoilerplate(
        Handle<JSFunction> boilerplate,
-      Handle<Map> function_map);
+      Handle<Map> function_map,
+      PretenureFlag pretenure);

    // Create a new map cache.
    static Handle<MapCache> NewMapCache(int at_least_space_for);
=======================================
--- /branches/bleeding_edge/src/heap.cc Thu Dec 10 07:10:50 2009
+++ /branches/bleeding_edge/src/heap.cc Wed Dec 16 07:43:20 2009
@@ -2233,8 +2233,11 @@

  Object* Heap::AllocateFunction(Map* function_map,
                                 SharedFunctionInfo* shared,
-                               Object* prototype) {
-  Object* result = Allocate(function_map, OLD_POINTER_SPACE);
+                               Object* prototype,
+                               PretenureFlag pretenure) {
+  AllocationSpace space =
+      (pretenure == TENURED) ? OLD_POINTER_SPACE : NEW_SPACE;
+  Object* result = Allocate(function_map, space);
    if (result->IsFailure()) return result;
    return InitializeFunction(JSFunction::cast(result), shared, prototype);
  }
=======================================
--- /branches/bleeding_edge/src/heap.h  Thu Dec 10 07:10:50 2009
+++ /branches/bleeding_edge/src/heap.h  Wed Dec 16 07:43:20 2009
@@ -487,7 +487,8 @@
    // Please note this does not perform a garbage collection.
    static Object* AllocateFunction(Map* function_map,
                                    SharedFunctionInfo* shared,
-                                  Object* prototype);
+                                  Object* prototype,
+                                  PretenureFlag pretenure = TENURED);

    // Indicies for direct access into argument objects.
    static const int arguments_callee_index = 0;
=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Fri Dec 11 02:40:01  
2009
+++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc    Wed Dec 16 07:43:20  
2009
@@ -3593,18 +3593,28 @@


  void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate)  
{
-  // Call the runtime to instantiate the function boilerplate object.
-  // The inevitable call will sync frame elements to memory anyway, so
-  // we do it eagerly to allow us to push the arguments directly into
-  // place.
    ASSERT(boilerplate->IsBoilerplate());
-  frame_->SyncRange(0, frame_->element_count() - 1);
-
-  // Create a new closure.
-  frame_->EmitPush(esi);
-  frame_->EmitPush(Immediate(boilerplate));
-  Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
-  frame_->Push(&result);
+
+  // Use the fast case closure allocation code that allocated in new
+  // space for nested functions that don't need literals cloning.
+  if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() ==  
0) {
+    FastNewClosureStub stub;
+    frame_->Push(boilerplate);
+    Result answer = frame_->CallStub(&stub, 1);
+    frame_->Push(&answer);
+  } else {
+    // Call the runtime to instantiate the function boilerplate
+    // object.  The inevitable call will sync frame elements to memory
+    // anyway, so we do it eagerly to allow us to push the arguments
+    // directly into place.
+    frame_->SyncRange(0, frame_->element_count() - 1);
+
+    // Create a new closure.
+    frame_->EmitPush(esi);
+    frame_->EmitPush(Immediate(boilerplate));
+    Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
+    frame_->Push(&result);
+  }
  }


@@ -6535,6 +6545,49 @@
        UNREACHABLE();
    }
  }
+
+
+void FastNewClosureStub::Generate(MacroAssembler* masm) {
+  // Clone the boilerplate in new space. Set the context to the
+  // current context in esi.
+  Label gc;
+  __ AllocateInNewSpace(JSFunction::kSize, eax, ebx, ecx, &gc, TAG_OBJECT);
+
+  // Get the boilerplate function from the stack.
+  __ mov(edx, Operand(esp, 1 * kPointerSize));
+
+  // Compute the function map in the current global context and set that
+  // as the map of the allocated object.
+  __ mov(ecx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+  __ mov(ecx, FieldOperand(ecx, GlobalObject::kGlobalContextOffset));
+  __ mov(ecx, Operand(ecx,  
Context::SlotOffset(Context::FUNCTION_MAP_INDEX)));
+  __ mov(FieldOperand(eax, JSObject::kMapOffset), ecx);
+
+  // Clone the rest of the boilerplate fields. We don't have to update
+  // the write barrier because the allocated object is in new space.
+  for (int offset = kPointerSize;
+       offset < JSFunction::kSize;
+       offset += kPointerSize) {
+    if (offset == JSFunction::kContextOffset) {
+      __ mov(FieldOperand(eax, offset), esi);
+    } else {
+      __ mov(ebx, FieldOperand(edx, offset));
+      __ mov(FieldOperand(eax, offset), ebx);
+    }
+  }
+
+  // Return and remove the on-stack parameter.
+  __ ret(1 * kPointerSize);
+
+  // Create a new closure through the slower runtime call.
+  __ bind(&gc);
+  __ pop(ecx);  // Temporarily remove return address.
+  __ pop(edx);
+  __ push(esi);
+  __ push(edx);
+  __ push(ecx);  // Restore return address.
+  __ TailCallRuntime(ExternalReference(Runtime::kNewClosure), 2, 1);
+}


  // NOTE: The stub does not handle the inlined cases (Smis, Booleans,  
undefined).
=======================================
--- /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc Wed Dec  9 06:54:34  
2009
+++ /branches/bleeding_edge/src/ia32/stub-cache-ia32.cc Wed Dec 16 07:43:20  
2009
@@ -1154,8 +1154,25 @@
    __ mov(edi, FieldOperand(edi, JSGlobalPropertyCell::kValueOffset));

    // Check that the cell contains the same function.
-  __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
-  __ j(not_equal, &miss, not_taken);
+  if (Heap::InNewSpace(function)) {
+    // We can't embed a pointer to a function in new space so we have
+    // to verify that the shared function info is unchanged. This has
+    // the nice side effect that multiple closures based on the same
+    // function can all use this call IC. Before we load through the
+    // function, we have to verify that it still is a function.
+    __ test(edi, Immediate(kSmiTagMask));
+    __ j(zero, &miss, not_taken);
+    __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx);
+    __ j(not_equal, &miss, not_taken);
+
+    // Check the shared function info. Make sure it hasn't changed.
+    __ cmp(FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset),
+           Immediate(Handle<SharedFunctionInfo>(function->shared())));
+    __ j(not_equal, &miss, not_taken);
+  } else {
+    __ cmp(Operand(edi), Immediate(Handle<JSFunction>(function)));
+    __ j(not_equal, &miss, not_taken);
+  }

    // Patch the receiver on the stack with the global proxy.
    if (object->IsGlobalObject()) {
=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Tue Dec  1 06:36:45 2009
+++ /branches/bleeding_edge/src/objects-inl.h   Wed Dec 16 07:43:20 2009
@@ -1499,7 +1499,7 @@
    // Range check.
    ASSERT(descriptor_number < number_of_descriptors());

-  // Make sure non of the elements in desc are in new space.
+  // Make sure none of the elements in desc are in new space.
    ASSERT(!Heap::InNewSpace(desc->GetKey()));
    ASSERT(!Heap::InNewSpace(desc->GetValue()));

=======================================
--- /branches/bleeding_edge/src/objects.cc      Tue Dec  1 02:25:29 2009
+++ /branches/bleeding_edge/src/objects.cc      Wed Dec 16 07:43:20 2009
@@ -1351,6 +1351,8 @@
  Object* JSObject::AddConstantFunctionProperty(String* name,
                                                JSFunction* function,
                                                PropertyAttributes  
attributes) {
+  ASSERT(!Heap::InNewSpace(function));
+
    // Allocate new instance descriptors with (name, function) added
    ConstantFunctionDescriptor d(name, function, attributes);
    Object* new_descriptors =
@@ -1437,7 +1439,7 @@
      // Ensure the descriptor array does not get too big.
      if (map()->instance_descriptors()->number_of_descriptors() <
          DescriptorArray::kMaxNumberOfDescriptors) {
-      if (value->IsJSFunction()) {
+      if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
          return AddConstantFunctionProperty(name,
                                             JSFunction::cast(value),
                                             attributes);
@@ -3254,7 +3256,8 @@
      return Heap::empty_descriptor_array();
    }
    // Allocate the array of keys.
-  Object* array =  
Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
+  Object* array =
+      Heap::AllocateFixedArray(ToKeyIndex(number_of_descriptors));
    if (array->IsFailure()) return array;
    // Do not use DescriptorArray::cast on incomplete object.
    FixedArray* result = FixedArray::cast(array);
@@ -7962,7 +7965,10 @@
        PropertyType type = DetailsAt(i).type();
        ASSERT(type != FIELD);
        instance_descriptor_length++;
-      if (type == NORMAL && !value->IsJSFunction()) number_of_fields += 1;
+      if (type == NORMAL &&
+          (!value->IsJSFunction() || Heap::InNewSpace(value))) {
+        number_of_fields += 1;
+      }
      }
    }

@@ -7993,7 +7999,7 @@
        PropertyDetails details = DetailsAt(i);
        PropertyType type = details.type();

-      if (value->IsJSFunction()) {
+      if (value->IsJSFunction() && !Heap::InNewSpace(value)) {
          ConstantFunctionDescriptor d(String::cast(key),
                                       JSFunction::cast(value),
                                       details.attributes(),
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Dec  1 06:36:45 2009
+++ /branches/bleeding_edge/src/objects.h       Wed Dec 16 07:43:20 2009
@@ -1662,6 +1662,7 @@
   public:
    // Is this the singleton empty_descriptor_array?
    inline bool IsEmpty();
+
    // Returns the number of descriptors in the array.
    int number_of_descriptors() {
      return IsEmpty() ? 0 : length() - kFirstIndex;
@@ -1801,12 +1802,14 @@
    static int ToKeyIndex(int descriptor_number) {
      return descriptor_number+kFirstIndex;
    }
-  static int ToValueIndex(int descriptor_number) {
-    return descriptor_number << 1;
-  }
+
    static int ToDetailsIndex(int descriptor_number) {
      return( descriptor_number << 1) + 1;
    }
+
+  static int ToValueIndex(int descriptor_number) {
+    return descriptor_number << 1;
+  }

    bool is_null_descriptor(int descriptor_number) {
      return PropertyDetails(GetDetails(descriptor_number)).type() ==
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Thu Dec 10 10:33:34 2009
+++ /branches/bleeding_edge/src/runtime.cc      Wed Dec 16 07:43:20 2009
@@ -720,7 +720,7 @@
        // Copy the function and update its context. Use it as value.
        Handle<JSFunction> boilerplate = Handle<JSFunction>::cast(value);
        Handle<JSFunction> function =
-          Factory::NewFunctionFromBoilerplate(boilerplate, context);
+          Factory::NewFunctionFromBoilerplate(boilerplate, context,  
TENURED);
        value = function;
      }

@@ -4502,8 +4502,11 @@
    CONVERT_ARG_CHECKED(Context, context, 0);
    CONVERT_ARG_CHECKED(JSFunction, boilerplate, 1);

+  PretenureFlag pretenure = (context->global_context() == *context)
+      ? TENURED       // Allocate global closures in old space.
+      : NOT_TENURED;  // Allocate local closures in new space.
    Handle<JSFunction> result =
-      Factory::NewFunctionFromBoilerplate(boilerplate, context);
+      Factory::NewFunctionFromBoilerplate(boilerplate, context, pretenure);
    return *result;
  }

@@ -5219,7 +5222,7 @@
                                                           validate);
    if (boilerplate.is_null()) return Failure::Exception();
    Handle<JSFunction> fun =
-      Factory::NewFunctionFromBoilerplate(boilerplate, context);
+      Factory::NewFunctionFromBoilerplate(boilerplate, context,  
NOT_TENURED);
    return *fun;
  }

@@ -5247,7 +5250,7 @@
        Compiler::DONT_VALIDATE_JSON);
    if (boilerplate.is_null()) return Failure::Exception();
    Handle<JSFunction> fun =
-    Factory::NewFunctionFromBoilerplate(boilerplate, context);
+      Factory::NewFunctionFromBoilerplate(boilerplate, context,  
NOT_TENURED);
    return *fun;
  }

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

Reply via email to