Revision: 2710
Author: [email protected]
Date: Wed Aug 19 00:30:20 2009
Log: Analyze functions for assignment to this properties.

During parsing functions are analyzed for statements of the form this.x  
= ...;. These assignments are categorized in two types: simple and non  
simple. The simple ones are where the right hand side is known to be either  
a constant or an argument to the function. If a function only contains  
statements of this type the property names are collected and for the simple  
assignments the index of the argument or the constant value assigned are  
stored as well.

When the initial map for a function is created and the function consists of  
only this type of assignemnts the initial map is created with a descriptor  
array describing these properties which will be known to always exist in an  
object created from the function.

The information on this property assignments is not collected during  
pre-parsing so if compiling using pre-parse data these optimization hints  
are not available.

Next step will be to use the information collected for the simple  
assignments to generate constructor code which will create and initialize  
the object from this information without calling the code for the function.
Review URL: http://codereview.chromium.org/172088
http://code.google.com/p/v8/source/detail?r=2710

Added:
  /branches/bleeding_edge/test/mjsunit/simple-constructor.js
Modified:
  /branches/bleeding_edge/src/ast.h
  /branches/bleeding_edge/src/codegen.cc
  /branches/bleeding_edge/src/heap.cc
  /branches/bleeding_edge/src/ia32/builtins-ia32.cc
  /branches/bleeding_edge/src/objects-debug.cc
  /branches/bleeding_edge/src/objects-inl.h
  /branches/bleeding_edge/src/objects.cc
  /branches/bleeding_edge/src/objects.h
  /branches/bleeding_edge/src/parser.cc
  /branches/bleeding_edge/src/runtime.cc
  /branches/bleeding_edge/src/v8-counters.h
  /branches/bleeding_edge/src/x64/builtins-x64.cc

=======================================
--- /dev/null
+++ /branches/bleeding_edge/test/mjsunit/simple-constructor.js  Wed Aug 19  
00:30:20 2009
@@ -0,0 +1,78 @@
+// Copyright 2008 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+function props(x) {
+  var array = [];
+  for (var p in x) array.push(p);
+  return array.sort();
+}
+
+function f1() {
+  this.x = 1;
+}
+
+function f2(x) {
+  this.x = x;
+}
+
+function f3(x) {
+  this.x = x;
+  this.y = 1;
+  this.z = f1;
+}
+
+function f4(x) {
+  this.x = x;
+  this.y = 1;
+  if (x == 1) return;
+  this.z = f1;
+}
+
+o1_1 = new f1();
+o1_2 = new f1();
+assertArrayEquals(["x"], props(o1_1));
+assertArrayEquals(["x"], props(o1_2));
+
+o2_1 = new f2(0);
+o2_2 = new f2(0);
+assertArrayEquals(["x"], props(o2_1));
+assertArrayEquals(["x"], props(o2_2));
+
+o3_1 = new f3(0);
+o3_2 = new f3(0);
+assertArrayEquals(["x", "y", "z"], props(o3_1));
+assertArrayEquals(["x", "y", "z"], props(o3_2));
+
+o4_0_1 = new f4(0);
+o4_0_2 = new f4(0);
+assertArrayEquals(["x", "y", "z"], props(o4_0_1));
+assertArrayEquals(["x", "y", "z"], props(o4_0_2));
+
+o4_1_1 = new f4(1);
+o4_1_2 = new f4(1);
+assertArrayEquals(["x", "y"], props(o4_1_1));
+assertArrayEquals(["x", "y"], props(o4_1_2));
=======================================
--- /branches/bleeding_edge/src/ast.h   Mon Aug  3 03:59:00 2009
+++ /branches/bleeding_edge/src/ast.h   Wed Aug 19 00:30:20 2009
@@ -111,6 +111,7 @@
  // Typedef only introduced to avoid unreadable code.
  // Please do appreciate the required space in "> >".
  typedef ZoneList<Handle<String> > ZoneStringList;
+typedef ZoneList<Handle<Object> > ZoneObjectList;


  class AstNode: public ZoneObject {
@@ -1226,6 +1227,9 @@
                    int materialized_literal_count,
                    bool contains_array_literal,
                    int expected_property_count,
+                  bool has_only_this_property_assignments,
+                  bool has_only_simple_this_property_assignments,
+                  Handle<FixedArray> this_property_assignments,
                    int num_parameters,
                    int start_position,
                    int end_position,
@@ -1236,6 +1240,10 @@
          materialized_literal_count_(materialized_literal_count),
          contains_array_literal_(contains_array_literal),
          expected_property_count_(expected_property_count),
+         
has_only_this_property_assignments_(has_only_this_property_assignments),
+        has_only_simple_this_property_assignments_(
+            has_only_simple_this_property_assignments),
+        this_property_assignments_(this_property_assignments),
          num_parameters_(num_parameters),
          start_position_(start_position),
          end_position_(end_position),
@@ -1265,6 +1273,15 @@
    int materialized_literal_count() { return materialized_literal_count_; }
    bool contains_array_literal() { return contains_array_literal_; }
    int expected_property_count() { return expected_property_count_; }
+  bool has_only_this_property_assignments() {
+      return has_only_this_property_assignments_;
+  }
+  bool has_only_simple_this_property_assignments() {
+      return has_only_simple_this_property_assignments_;
+  }
+  Handle<FixedArray> this_property_assignments() {
+      return this_property_assignments_;
+  }
    int num_parameters() { return num_parameters_; }

    bool AllowsLazyCompilation();
@@ -1291,6 +1308,9 @@
    int materialized_literal_count_;
    bool contains_array_literal_;
    int expected_property_count_;
+  bool has_only_this_property_assignments_;
+  bool has_only_simple_this_property_assignments_;
+  Handle<FixedArray> this_property_assignments_;
    int num_parameters_;
    int start_position_;
    int end_position_;
=======================================
--- /branches/bleeding_edge/src/codegen.cc      Fri Aug 14 04:05:42 2009
+++ /branches/bleeding_edge/src/codegen.cc      Wed Aug 19 00:30:20 2009
@@ -255,6 +255,10 @@
    fun->shared()->set_is_expression(lit->is_expression());
    fun->shared()->set_is_toplevel(is_toplevel);
    fun->shared()->set_inferred_name(*lit->inferred_name());
+  fun->shared()->SetThisPropertyAssignmentsInfo(
+      lit->has_only_this_property_assignments(),
+      lit->has_only_simple_this_property_assignments(),
+      *lit->this_property_assignments());
  }


=======================================
--- /branches/bleeding_edge/src/heap.cc Tue Aug 18 04:26:14 2009
+++ /branches/bleeding_edge/src/heap.cc Wed Aug 19 00:30:20 2009
@@ -1054,6 +1054,7 @@
    map->set_constructor(null_value());
    map->set_instance_size(instance_size);
    map->set_inobject_properties(0);
+  map->set_pre_allocated_property_fields(0);
    map->set_instance_descriptors(empty_descriptor_array());
    map->set_code_cache(empty_fixed_array());
    map->set_unused_property_fields(0);
@@ -1611,6 +1612,9 @@
    share->set_start_position_and_type(0);
    share->set_debug_info(undefined_value());
    share->set_inferred_name(empty_string());
+  share->set_compiler_hints(0);
+  share->set_this_property_assignments_count(0);
+  share->set_this_property_assignments(undefined_value());
    return result;
  }

@@ -2050,16 +2054,10 @@
  Object* Heap::AllocateInitialMap(JSFunction* fun) {
    ASSERT(!fun->has_initial_map());

-  // First create a new map with the expected number of properties being
-  // allocated in-object.
-  int expected_nof_properties = fun->shared()->expected_nof_properties();
-  int instance_size = JSObject::kHeaderSize +
-                      expected_nof_properties * kPointerSize;
-  if (instance_size > JSObject::kMaxInstanceSize) {
-    instance_size = JSObject::kMaxInstanceSize;
-    expected_nof_properties = (instance_size - JSObject::kHeaderSize) /
-                              kPointerSize;
-  }
+  // First create a new map with the size and number of in-object  
properties
+  // suggested by the function.
+  int instance_size = fun->shared()->CalculateInstanceSize();
+  int in_object_properties = fun->shared()->CalculateInObjectProperties();
    Object* map_obj = Heap::AllocateMap(JS_OBJECT_TYPE, instance_size);
    if (map_obj->IsFailure()) return map_obj;

@@ -2072,9 +2070,33 @@
      if (prototype->IsFailure()) return prototype;
    }
    Map* map = Map::cast(map_obj);
-  map->set_inobject_properties(expected_nof_properties);
-  map->set_unused_property_fields(expected_nof_properties);
+  map->set_inobject_properties(in_object_properties);
+  map->set_unused_property_fields(in_object_properties);
    map->set_prototype(prototype);
+
+  // If the function has only simple this property assignments add field
+  // descriptors for these to the initial map as the object cannot be
+  // constructed without having these properties.
+  ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields);
+  if (fun->shared()->has_only_this_property_assignments() &&
+      fun->shared()->this_property_assignments_count() > 0) {
+    int count = fun->shared()->this_property_assignments_count();
+    if (count > in_object_properties) {
+      count = in_object_properties;
+    }
+    DescriptorArray* descriptors = *Factory::NewDescriptorArray(count);
+    if (descriptors->IsFailure()) return descriptors;
+    for (int i = 0; i < count; i++) {
+      String* name = fun->shared()->GetThisPropertyAssignmentName(i);
+      ASSERT(name->IsSymbol());
+      FieldDescriptor field(name, i, NONE);
+      descriptors->Set(i, &field);
+    }
+    descriptors->Sort();
+    map->set_instance_descriptors(descriptors);
+    map->set_pre_allocated_property_fields(count);
+    map->set_unused_property_fields(in_object_properties - count);
+  }
    return map;
  }

@@ -2106,7 +2128,11 @@
    ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE);

    // Allocate the backing storage for the properties.
-  int prop_size = map->unused_property_fields() -  
map->inobject_properties();
+  int prop_size =
+      map->pre_allocated_property_fields() +
+      map->unused_property_fields() -
+      map->inobject_properties();
+  ASSERT(prop_size >= 0);
    Object* properties = AllocateFixedArray(prop_size, pretenure);
    if (properties->IsFailure()) return properties;

@@ -2599,6 +2625,7 @@


  Object* Heap::AllocateFixedArray(int length) {
+  ASSERT(length >= 0);
    if (length == 0) return empty_fixed_array();
    Object* result = AllocateRawFixedArray(length);
    if (!result->IsFailure()) {
=======================================
--- /branches/bleeding_edge/src/ia32/builtins-ia32.cc   Tue Aug 18 03:52:14  
2009
+++ /branches/bleeding_edge/src/ia32/builtins-ia32.cc   Wed Aug 19 00:30:20  
2009
@@ -180,12 +180,16 @@
      // eax: initial map
      // ebx: JSObject
      // edi: start of next object
+    // Calculate the total number of properties described by the map.
      __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset));
-    __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
+    __ movzx_b(ecx, FieldOperand(eax,  
Map::kPreAllocatedPropertyFieldsOffset));
+    __ add(edx, Operand(ecx));
      // Calculate unused properties past the end of the in-object  
properties.
+    __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset));
      __ sub(edx, Operand(ecx));
      // Done if no extra properties are to be allocated.
      __ j(zero, &allocated);
+    __ Assert(positive, "Property allocation count failed.");

      // Scale the number of elements by pointer size and add the header for
      // FixedArrays to the start of the next object calculation from above.
@@ -321,6 +325,7 @@
    __ pop(ecx);
    __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize));  // 1 ~  
receiver
    __ push(ecx);
+  __ IncrementCounter(&Counters::constructed_objects, 1);
    __ ret(0);
  }

=======================================
--- /branches/bleeding_edge/src/objects-debug.cc        Wed Jul 29 05:34:21 2009
+++ /branches/bleeding_edge/src/objects-debug.cc        Wed Aug 19 00:30:20 2009
@@ -371,9 +371,9 @@
    VerifyHeapPointer(properties());
    VerifyHeapPointer(elements());
    if (HasFastProperties()) {
-    CHECK(map()->unused_property_fields() ==
-          (map()->inobject_properties() + properties()->length() -
-           map()->NextFreePropertyIndex()));
+    CHECK_EQ(map()->unused_property_fields(),
+             (map()->inobject_properties() + properties()->length() -
+              map()->NextFreePropertyIndex()));
    }
  }

@@ -462,6 +462,7 @@
    HeapObject::PrintHeader("Map");
    PrintF(" - type: %s\n", TypeToString(instance_type()));
    PrintF(" - instance size: %d\n", instance_size());
+  PrintF(" - inobject properties: %d\n", inobject_properties());
    PrintF(" - unused property fields: %d\n", unused_property_fields());
    if (is_hidden_prototype()) {
      PrintF(" - hidden_prototype\n");
@@ -619,6 +620,12 @@
    PrintF("\n - debug info = ");
    debug_info()->ShortPrint();
    PrintF("\n - length = %d", length());
+  PrintF("\n - has_only_this_property_assignments = %d",
+         has_only_this_property_assignments());
+  PrintF("\n - has_only_simple_this_property_assignments = %d",
+         has_only_simple_this_property_assignments());
+  PrintF("\n - this_property_assignments = ");
+  this_property_assignments()->ShortPrint();
    PrintF("\n");
  }

=======================================
--- /branches/bleeding_edge/src/objects-inl.h   Thu Aug  6 06:35:21 2009
+++ /branches/bleeding_edge/src/objects-inl.h   Wed Aug 19 00:30:20 2009
@@ -91,10 +91,16 @@
    }


-#define BOOL_ACCESSORS(holder, field, name, offset) \
+#define BOOL_GETTER(holder, field, name, offset)           \
    bool holder::name() {                                    \
      return BooleanBit::get(field(), offset);               \
    }                                                        \
+
+
+#define BOOL_ACCESSORS(holder, field, name, offset)        \
+  bool holder::name() {                                    \
+    return BooleanBit::get(field(), offset);               \
+  }                                                        \
    void holder::set_##name(bool value) {                    \
      set_##field(BooleanBit::set(field(), offset, value));  \
    }
@@ -1935,6 +1941,11 @@
  int Map::inobject_properties() {
    return READ_BYTE_FIELD(this, kInObjectPropertiesOffset);
  }
+
+
+int Map::pre_allocated_property_fields() {
+  return READ_BYTE_FIELD(this, kPreAllocatedPropertyFieldsOffset);
+}


  int HeapObject::SizeFromMap(Map* map) {
@@ -1967,6 +1978,14 @@
    ASSERT(0 <= value && value < 256);
    WRITE_BYTE_FIELD(this, kInObjectPropertiesOffset,  
static_cast<byte>(value));
  }
+
+
+void Map::set_pre_allocated_property_fields(int value) {
+  ASSERT(0 <= value && value < 256);
+  WRITE_BYTE_FIELD(this,
+                   kPreAllocatedPropertyFieldsOffset,
+                   static_cast<byte>(value));
+}


  InstanceType Map::instance_type() {
@@ -2298,6 +2317,8 @@
  ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset)
  ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset)
  ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset)
+ACCESSORS(SharedFunctionInfo, this_property_assignments, Object,
+          kThisPropertyAssignmentsOffset)

  BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype,
                 kHiddenPrototypeBit)
@@ -2308,6 +2329,13 @@
                 kIsExpressionBit)
  BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel,
                 kIsTopLevelBit)
+BOOL_GETTER(SharedFunctionInfo, compiler_hints,
+            has_only_this_property_assignments,
+            kHasOnlyThisPropertyAssignments)
+BOOL_GETTER(SharedFunctionInfo, compiler_hints,
+            has_only_simple_this_property_assignments,
+            kHasOnlySimpleThisPropertyAssignments)
+

  INT_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
  INT_ACCESSORS(SharedFunctionInfo, formal_parameter_count,
@@ -2319,6 +2347,10 @@
  INT_ACCESSORS(SharedFunctionInfo, end_position, kEndPositionOffset)
  INT_ACCESSORS(SharedFunctionInfo, function_token_position,
                kFunctionTokenPositionOffset)
+INT_ACCESSORS(SharedFunctionInfo, compiler_hints,
+              kCompilerHintsOffset)
+INT_ACCESSORS(SharedFunctionInfo, this_property_assignments_count,
+              kThisPropertyAssignmentsCountOffset)


  void SharedFunctionInfo::DontAdaptArguments() {
=======================================
--- /branches/bleeding_edge/src/objects.cc      Thu Aug  6 06:35:21 2009
+++ /branches/bleeding_edge/src/objects.cc      Wed Aug 19 00:30:20 2009
@@ -4778,6 +4778,48 @@
    return *SubString(Handle<String>(String::cast(source)),
                      start_position(), end_position());
  }
+
+
+int SharedFunctionInfo::CalculateInstanceSize() {
+  int instance_size =
+      JSObject::kHeaderSize +
+      expected_nof_properties() * kPointerSize;
+  if (instance_size > JSObject::kMaxInstanceSize) {
+    instance_size = JSObject::kMaxInstanceSize;
+  }
+  return instance_size;
+}
+
+
+int SharedFunctionInfo::CalculateInObjectProperties() {
+  return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize;
+}
+
+
+void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
+    bool only_this_property_assignments,
+    bool only_simple_this_property_assignments,
+    FixedArray* assignments) {
+  ASSERT(this_property_assignments()->IsUndefined());
+  set_compiler_hints(BooleanBit::set(compiler_hints(),
+                                     kHasOnlyThisPropertyAssignments,
+                                     only_this_property_assignments));
+  set_compiler_hints(BooleanBit::set(compiler_hints(),
+                                     kHasOnlySimpleThisPropertyAssignments,
+                                      
only_simple_this_property_assignments));
+  set_this_property_assignments(assignments);
+  set_this_property_assignments_count(assignments->length() / 3);
+}
+
+
+String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) {
+  Object* obj = this_property_assignments();
+  ASSERT(obj->IsFixedArray());
+  ASSERT(index < this_property_assignments_count());
+  obj = FixedArray::cast(obj)->get(index * 3);
+  ASSERT(obj->IsString());
+  return String::cast(obj);
+}


  // Support function for printing the source code to a StringStream
@@ -4826,6 +4868,8 @@
    IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize);
    IteratePointers(v, kInstanceClassNameOffset, kScriptOffset +  
kPointerSize);
    IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize);
+  IteratePointers(v, kThisPropertyAssignmentsOffset,
+      kThisPropertyAssignmentsOffset + kPointerSize);
  }


=======================================
--- /branches/bleeding_edge/src/objects.h       Thu Aug  6 06:35:21 2009
+++ /branches/bleeding_edge/src/objects.h       Wed Aug 19 00:30:20 2009
@@ -2704,6 +2704,10 @@
    inline int inobject_properties();
    inline void set_inobject_properties(int value);

+  // Count of property fields pre-allocated in the object when first  
allocated.
+  inline int pre_allocated_property_fields();
+  inline void set_pre_allocated_property_fields(int value);
+
    // Instance type.
    inline InstanceType instance_type();
    inline void set_instance_type(InstanceType value);
@@ -2869,6 +2873,8 @@
    void MapVerify();
  #endif

+  static const int kMaxPreAllocatedPropertyFields = 255;
+
    // Layout description.
    static const int kInstanceSizesOffset = HeapObject::kHeaderSize;
    static const int kInstanceAttributesOffset = kInstanceSizesOffset +  
kIntSize;
@@ -2882,7 +2888,8 @@
    // Byte offsets within kInstanceSizesOffset.
    static const int kInstanceSizeOffset = kInstanceSizesOffset + 0;
    static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1;
-  // The bytes at positions 2 and 3 are not in use at the moment.
+  static const int kPreAllocatedPropertyFieldsOffset =  
kInstanceSizesOffset + 2;
+  // The byte at position 3 is not in use at the moment.

    // Byte offsets within kInstanceAttributesOffset attributes.
    static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0;
@@ -3090,10 +3097,42 @@
    inline bool is_toplevel();
    inline void set_is_toplevel(bool value);

+  // Bit field containing various information collected by the compiler to
+  // drive optimization.
+  inline int compiler_hints();
+  inline void set_compiler_hints(int value);
+
+  // Add information on assignments of the form this.x = ...;
+  void SetThisPropertyAssignmentsInfo(
+      bool has_only_this_property_assignments,
+      bool has_only_simple_this_property_assignments,
+      FixedArray* this_property_assignments);
+
+  // Indicate that this function only consists of assignments of the form
+  // this.x = ...;.
+  inline bool has_only_this_property_assignments();
+
+  // Indicate that this function only consists of assignments of the form
+  // this.x = y; where y is either a constant or refers to an argument.
+  inline bool has_only_simple_this_property_assignments();
+
+  // For functions which only contains this property assignments this  
provides
+  // access to the names for the properties assigned.
+  DECL_ACCESSORS(this_property_assignments, Object)
+  inline int this_property_assignments_count();
+  inline void set_this_property_assignments_count(int value);
+  String* GetThisPropertyAssignmentName(int index);
+
    // [source code]: Source code for the function.
    bool HasSourceCode();
    Object* GetSourceCode();

+  // Calculate the instance size.
+  int CalculateInstanceSize();
+
+  // Calculate the number of in-object properties.
+  int CalculateInObjectProperties();
+
    // Dispatched behavior.
    void SharedFunctionInfoIterateBody(ObjectVisitor* v);
    // Set max_length to -1 for unlimited length.
@@ -3129,7 +3168,12 @@
    static const int kScriptOffset = kExternalReferenceDataOffset +  
kPointerSize;
    static const int kDebugInfoOffset = kScriptOffset + kPointerSize;
    static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize;
-  static const int kSize = kInferredNameOffset + kPointerSize;
+  static const int kCompilerHintsOffset = kInferredNameOffset +  
kPointerSize;
+  static const int kThisPropertyAssignmentsOffset =
+      kCompilerHintsOffset + kPointerSize;
+  static const int kThisPropertyAssignmentsCountOffset =
+      kThisPropertyAssignmentsOffset + kPointerSize;
+  static const int kSize = kThisPropertyAssignmentsCountOffset +  
kPointerSize;

   private:
    // Bit positions in length_and_flg.
@@ -3146,6 +3190,10 @@
    static const int kStartPositionShift = 2;
    static const int kStartPositionMask = ~((1 << kStartPositionShift) - 1);

+  // Bit positions in compiler_hints.
+  static const int kHasOnlyThisPropertyAssignments = 0;
+  static const int kHasOnlySimpleThisPropertyAssignments = 1;
+
    DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
  };

=======================================
--- /branches/bleeding_edge/src/parser.cc       Tue Aug 18 00:14:02 2009
+++ /branches/bleeding_edge/src/parser.cc       Wed Aug 19 00:30:20 2009
@@ -677,6 +677,25 @@

    void set_contains_array_literal() { contains_array_literal_ = true; }
    bool contains_array_literal() { return contains_array_literal_; }
+
+  void SetThisPropertyAssignmentInfo(
+      bool only_this_property_assignments,
+      bool only_simple_this_property_assignments,
+      Handle<FixedArray> this_property_assignments) {
+    only_this_property_assignments_ = only_this_property_assignments;
+    only_simple_this_property_assignments_ =
+        only_simple_this_property_assignments;
+    this_property_assignments_ = this_property_assignments;
+  }
+  bool only_this_property_assignments() {
+    return only_this_property_assignments_;
+  }
+  bool only_simple_this_property_assignments() {
+    return only_simple_this_property_assignments_;
+  }
+  Handle<FixedArray> this_property_assignments() {
+    return this_property_assignments_;
+  }

    void AddProperty() { expected_property_count_++; }
    int expected_property_count() { return expected_property_count_; }
@@ -695,6 +714,10 @@
    // Properties count estimation.
    int expected_property_count_;

+  bool only_this_property_assignments_;
+  bool only_simple_this_property_assignments_;
+  Handle<FixedArray> this_property_assignments_;
+
    // Bookkeeping
    Parser* parser_;
    TemporaryScope* parent_;
@@ -707,6 +730,9 @@
    : materialized_literal_count_(0),
      contains_array_literal_(false),
      expected_property_count_(0),
+    only_this_property_assignments_(false),
+    only_simple_this_property_assignments_(false),
+    this_property_assignments_(Factory::empty_fixed_array()),
      parser_(parser),
      parent_(parser->temp_scope_) {
    parser->temp_scope_ = this;
@@ -1218,12 +1244,20 @@
      bool ok = true;
      ParseSourceElements(&body, Token::EOS, &ok);
      if (ok) {
-      result = NEW(FunctionLiteral(no_name, top_scope_,
-                                   body.elements(),
-                                   temp_scope.materialized_literal_count(),
-                                   temp_scope.contains_array_literal(),
-                                   temp_scope.expected_property_count(),
-                                   0, 0, source->length(), false));
+      result = NEW(FunctionLiteral(
+          no_name,
+          top_scope_,
+          body.elements(),
+          temp_scope.materialized_literal_count(),
+          temp_scope.contains_array_literal(),
+          temp_scope.expected_property_count(),
+          temp_scope.only_this_property_assignments(),
+          temp_scope.only_simple_this_property_assignments(),
+          temp_scope.this_property_assignments(),
+          0,
+          0,
+          source->length(),
+          false));
      } else if (scanner().stack_overflow()) {
        Top::StackOverflow();
      }
@@ -1312,11 +1346,25 @@
                                  Vector<const char*> args) {
    recorder()->LogMessage(source_location, type, args);
  }
+
+
+// Base class containing common code for the different finder classes used  
by
+// the parser.
+class ParserFinder {
+ protected:
+  ParserFinder() {}
+  static Assignment* AsAssignment(Statement* stat) {
+    if (stat == NULL) return NULL;
+    ExpressionStatement* exp_stat = stat->AsExpressionStatement();
+    if (exp_stat == NULL) return NULL;
+    return exp_stat->expression()->AsAssignment();
+  }
+};


  // An InitializationBlockFinder finds and marks sequences of statements of  
the
  // form x.y.z.a = ...; x.y.z.b = ...; etc.
-class InitializationBlockFinder {
+class InitializationBlockFinder : public ParserFinder {
   public:
    InitializationBlockFinder()
      : first_in_block_(NULL), last_in_block_(NULL), block_size_(0) {}
@@ -1341,13 +1389,6 @@
    }

   private:
-  static Assignment* AsAssignment(Statement* stat) {
-    if (stat == NULL) return NULL;
-    ExpressionStatement* exp_stat = stat->AsExpressionStatement();
-    if (exp_stat == NULL) return NULL;
-    return exp_stat->expression()->AsAssignment();
-  }
-
    // Returns true if the expressions appear to denote the same object.
    // In the context of initialization blocks, we only consider expressions
    // of the form 'x.y.z'.
@@ -1418,6 +1459,161 @@
  };


+// A ThisNamedPropertyAssigmentFinder finds and marks statements of the  
form
+// this.x = ...;, where x is a named property. It also determines whether a
+// function contains only assignments of this type.
+class ThisNamedPropertyAssigmentFinder : public ParserFinder {
+ public:
+  ThisNamedPropertyAssigmentFinder()
+      : only_this_property_assignments_(true),
+        only_simple_this_property_assignments_(true),
+        names_(NULL),
+        assigned_arguments_(NULL),
+        assigned_constants_(NULL) {}
+
+  void Update(Scope* scope, Statement* stat) {
+    // Bail out if function already has non this property assignment
+    // statements.
+    if (!only_this_property_assignments_) {
+      return;
+    }
+
+    // Check whether this statement is of the form this.x = ...;
+    Assignment* assignment = AsAssignment(stat);
+    if (IsThisPropertyAssignment(assignment)) {
+      HandleThisPropertyAssignment(scope, assignment);
+    } else {
+      only_this_property_assignments_ = false;
+      only_simple_this_property_assignments_ = false;
+    }
+  }
+
+  // Returns whether only statements of the form this.x = ...; was  
encountered.
+  bool only_this_property_assignments() {
+    return only_this_property_assignments_;
+  }
+
+  // Returns whether only statements of the form this.x = y; where y is  
either a
+  // constant or a function argument was encountered.
+  bool only_simple_this_property_assignments() {
+    return only_simple_this_property_assignments_;
+  }
+
+  // Returns a fixed array containing three elements for each assignment  
of the
+  // form this.x = y;
+  Handle<FixedArray> GetThisPropertyAssignments() {
+    if (names_ == NULL) {
+      return Factory::empty_fixed_array();
+    }
+    ASSERT(names_ != NULL);
+    ASSERT(assigned_arguments_ != NULL);
+    ASSERT_EQ(names_->length(), assigned_arguments_->length());
+    ASSERT_EQ(names_->length(), assigned_constants_->length());
+    Handle<FixedArray> assignments =
+        Factory::NewFixedArray(names_->length() * 3);
+    for (int i = 0; i < names_->length(); i++) {
+      assignments->set(i * 3, *names_->at(i));
+      assignments->set(i * 3 + 1,  
Smi::FromInt(assigned_arguments_->at(i)));
+      assignments->set(i * 3 + 2, *assigned_constants_->at(i));
+    }
+    return assignments;
+  }
+
+ private:
+  bool IsThisPropertyAssignment(Assignment* assignment) {
+    if (assignment != NULL) {
+      Property* property = assignment->target()->AsProperty();
+      return assignment->op() == Token::ASSIGN
+             && property != NULL
+             && property->obj()->AsVariableProxy() != NULL
+             && property->obj()->AsVariableProxy()->is_this();
+    }
+    return false;
+  }
+
+  void HandleThisPropertyAssignment(Scope* scope, Assignment* assignment) {
+    // Check that the property assigned to is a named property.
+    Property* property = assignment->target()->AsProperty();
+    ASSERT(property != NULL);
+    Literal* literal = property->key()->AsLiteral();
+    uint32_t dummy;
+    if (literal != NULL &&
+        literal->handle()->IsString() &&
+        !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) {
+      Handle<String> key = Handle<String>::cast(literal->handle());
+
+      // Check whether the value assigned is either a constant or matches  
the
+      // name of one of the arguments to the function.
+      if (assignment->value()->AsLiteral() != NULL) {
+        // Constant assigned.
+        Literal* literal = assignment->value()->AsLiteral();
+        AssignmentFromConstant(key, literal->handle());
+      } else if (assignment->value()->AsVariableProxy() != NULL) {
+        // Variable assigned.
+        Handle<String> name =
+            assignment->value()->AsVariableProxy()->name();
+        // Check whether the variable assigned matches an argument name.
+        int index = -1;
+        for (int i = 0; i < scope->num_parameters(); i++) {
+          if (*scope->parameter(i)->name() == *name) {
+            // Assigned from function argument.
+            index = i;
+            break;
+          }
+        }
+        if (index != -1) {
+          AssignmentFromParameter(key, index);
+        } else {
+          AssignmentFromSomethingElse(key);
+        }
+      } else {
+        AssignmentFromSomethingElse(key);
+      }
+    }
+  }
+
+  void AssignmentFromParameter(Handle<String> name, int index) {
+    EnsureAllocation();
+    names_->Add(name);
+    assigned_arguments_->Add(index);
+    assigned_constants_->Add(Factory::undefined_value());
+  }
+
+  void AssignmentFromConstant(Handle<String> name, Handle<Object> value) {
+    EnsureAllocation();
+    names_->Add(name);
+    assigned_arguments_->Add(-1);
+    assigned_constants_->Add(value);
+  }
+
+  void AssignmentFromSomethingElse(Handle<String> name) {
+    EnsureAllocation();
+    names_->Add(name);
+    assigned_arguments_->Add(-1);
+    assigned_constants_->Add(Factory::undefined_value());
+
+    // The this assignment is not a simple one.
+    only_simple_this_property_assignments_ = false;
+  }
+
+  void EnsureAllocation() {
+    if (names_ == NULL) {
+      ASSERT(assigned_arguments_ == NULL);
+      ASSERT(assigned_constants_ == NULL);
+      names_ = new ZoneStringList(4);
+      assigned_arguments_ = new ZoneList<int>(4);
+      assigned_constants_ = new ZoneObjectList(4);
+    }
+  }
+
+  bool only_this_property_assignments_;
+  bool only_simple_this_property_assignments_;
+  ZoneStringList* names_;
+  ZoneList<int>* assigned_arguments_;
+  ZoneObjectList* assigned_constants_;
+};
+
+
  void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
                                    int end_token,
                                    bool* ok) {
@@ -1432,15 +1628,33 @@

    ASSERT(processor != NULL);
    InitializationBlockFinder block_finder;
+  ThisNamedPropertyAssigmentFinder this_property_assignment_finder;
    while (peek() != end_token) {
      Statement* stat = ParseStatement(NULL, CHECK_OK);
      if (stat == NULL || stat->IsEmpty()) continue;
      // We find and mark the initialization blocks on top level code only.
      // This is because the optimization prevents reuse of the map  
transitions,
      // so it should be used only for code that will only be run once.
-    if (top_scope_->is_global_scope()) block_finder.Update(stat);
+    if (top_scope_->is_global_scope()) {
+      block_finder.Update(stat);
+    }
+    // Find and mark all assignments to named properties in this (this.x =)
+    if (top_scope_->is_function_scope()) {
+      this_property_assignment_finder.Update(top_scope_, stat);
+    }
      processor->Add(stat);
    }
+
+  // Propagate the collected information on this property assignments.
+  if (top_scope_->is_function_scope()) {
+    if (this_property_assignment_finder.only_this_property_assignments()) {
+      temp_scope_->SetThisPropertyAssignmentInfo(
+          this_property_assignment_finder.only_this_property_assignments(),
+          this_property_assignment_finder.
+              only_simple_this_property_assignments(),
+          this_property_assignment_finder.GetThisPropertyAssignments());
+    }
+  }
    return 0;
  }

@@ -3507,6 +3721,9 @@
      int materialized_literal_count;
      int expected_property_count;
      bool contains_array_literal;
+    bool only_this_property_assignments;
+    bool only_simple_this_property_assignments;
+    Handle<FixedArray> this_property_assignments;
      if (is_lazily_compiled && pre_data() != NULL) {
        FunctionEntry entry = pre_data()->GetFunctionEnd(start_pos);
        int end_pos = entry.end_pos();
@@ -3514,12 +3731,20 @@
        scanner_.SeekForward(end_pos);
        materialized_literal_count = entry.literal_count();
        expected_property_count = entry.property_count();
+      only_this_property_assignments = false;
+      only_simple_this_property_assignments = false;
+      this_property_assignments = Factory::empty_fixed_array();
        contains_array_literal = entry.contains_array_literal();
      } else {
        ParseSourceElements(&body, Token::RBRACE, CHECK_OK);
        materialized_literal_count = temp_scope.materialized_literal_count();
        expected_property_count = temp_scope.expected_property_count();
        contains_array_literal = temp_scope.contains_array_literal();
+      only_this_property_assignments =
+          temp_scope.only_this_property_assignments();
+      only_simple_this_property_assignments =
+          temp_scope.only_simple_this_property_assignments();
+      this_property_assignments = temp_scope.this_property_assignments();
      }

      Expect(Token::RBRACE, CHECK_OK);
@@ -3534,10 +3759,18 @@
      }

      FunctionLiteral* function_literal =
-        NEW(FunctionLiteral(name, top_scope_,
-                            body.elements(), materialized_literal_count,
-                            contains_array_literal,  
expected_property_count,
-                            num_parameters, start_pos, end_pos,
+        NEW(FunctionLiteral(name,
+                            top_scope_,
+                            body.elements(),
+                            materialized_literal_count,
+                            contains_array_literal,
+                            expected_property_count,
+                            only_this_property_assignments,
+                            only_simple_this_property_assignments,
+                            this_property_assignments,
+                            num_parameters,
+                            start_pos,
+                            end_pos,
                              function_name->length() > 0));
      if (!is_pre_parsing_) {
         
function_literal->set_function_token_position(function_token_position);
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Mon Aug 17 03:19:00 2009
+++ /branches/bleeding_edge/src/runtime.cc      Wed Aug 19 00:30:20 2009
@@ -4380,6 +4380,8 @@
      Handle<Code> stub = ComputeConstructStub(map);
      function->shared()->set_construct_stub(*stub);
    }
+  Counters::constructed_objects.Increment();
+  Counters::constructed_objects_runtime.Increment();
    return *result;
  }

=======================================
--- /branches/bleeding_edge/src/v8-counters.h   Tue Jun 30 03:05:36 2009
+++ /branches/bleeding_edge/src/v8-counters.h   Wed Aug 19 00:30:20 2009
@@ -139,6 +139,8 @@
    SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
    SC(call_global_inline, V8.CallGlobalInline)                       \
    SC(call_global_inline_miss, V8.CallGlobalInlineMiss)              \
+  SC(constructed_objects, V8.ConstructedObjects)                    \
+  SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime)     \
    SC(for_in, V8.ForIn)                                              \
    SC(enum_cache_hits, V8.EnumCacheHits)                             \
    SC(enum_cache_misses, V8.EnumCacheMisses)                         \
=======================================
--- /branches/bleeding_edge/src/x64/builtins-x64.cc     Tue Aug 18 03:52:14 2009
+++ /branches/bleeding_edge/src/x64/builtins-x64.cc     Wed Aug 19 00:30:20 2009
@@ -585,12 +585,16 @@
      // rax: initial map
      // rbx: JSObject
      // rdi: start of next object
+    // Calculate total properties described map.
      __ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset));
-    __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset));
+    __ movzxbq(rcx, FieldOperand(rax,  
Map::kPreAllocatedPropertyFieldsOffset));
+    __ addq(rdx, rcx);
      // Calculate unused properties past the end of the in-object  
properties.
+    __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset));
      __ subq(rdx, rcx);
      // Done if no extra properties are to be allocated.
      __ j(zero, &allocated);
+    __ Assert(positive, "Property allocation count failed.");

      // Scale the number of elements by pointer size and add the header for
      // FixedArrays to the start of the next object calculation from above.
@@ -726,6 +730,7 @@
    __ pop(rcx);
    __ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize));  // 1 ~  
receiver
    __ push(rcx);
+  __ IncrementCounter(&Counters::constructed_objects, 1);
    __ ret(0);
  }


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

Reply via email to