Revision: 17228
Author:   [email protected]
Date:     Wed Oct 16 08:10:36 2013 UTC
Log:      AllocationSites for all literals

[email protected]

Review URL: https://codereview.chromium.org/24250005


Review URL: https://codereview.chromium.org/27366003
http://code.google.com/p/v8/source/detail?r=17228

Added:
 /branches/bleeding_edge/src/allocation-site-scopes.cc
 /branches/bleeding_edge/src/allocation-site-scopes.h
Modified:
 /branches/bleeding_edge/src/flag-definitions.h
 /branches/bleeding_edge/src/hydrogen.cc
 /branches/bleeding_edge/src/hydrogen.h
 /branches/bleeding_edge/src/objects.cc
 /branches/bleeding_edge/src/objects.h
 /branches/bleeding_edge/src/runtime.cc
 /branches/bleeding_edge/test/mjsunit/allocation-site-info.js
 /branches/bleeding_edge/tools/gyp/v8.gyp

=======================================
--- /dev/null
+++ /branches/bleeding_edge/src/allocation-site-scopes.cc Wed Oct 16 08:10:36 2013 UTC
@@ -0,0 +1,108 @@
+// Copyright 2013 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.
+
+#include "allocation-site-scopes.h"
+
+namespace v8 {
+namespace internal {
+
+
+Handle<AllocationSite> AllocationSiteCreationContext::EnterNewScope() {
+  Handle<AllocationSite> scope_site;
+  if (top().is_null()) {
+    // We are creating the top level AllocationSite as opposed to a nested
+    // AllocationSite.
+    InitializeTraversal(isolate()->factory()->NewAllocationSite());
+    scope_site = Handle<AllocationSite>(*top(), isolate());
+    if (FLAG_trace_creation_allocation_sites) {
+      PrintF("*** Creating top level AllocationSite %p\n",
+             static_cast<void*>(*scope_site));
+    }
+  } else {
+    ASSERT(!current().is_null());
+    scope_site = isolate()->factory()->NewAllocationSite();
+    if (FLAG_trace_creation_allocation_sites) {
+      PrintF("Creating nested site (top, current, new) (%p, %p, %p)\n",
+             static_cast<void*>(*top()),
+             static_cast<void*>(*current()),
+             static_cast<void*>(*scope_site));
+    }
+    current()->set_nested_site(*scope_site);
+    update_current_site(*scope_site);
+  }
+  ASSERT(!scope_site.is_null());
+  return scope_site;
+}
+
+
+void AllocationSiteCreationContext::ExitScope(
+    Handle<AllocationSite> scope_site,
+    Handle<JSObject> object) {
+  if (!object.is_null() && !object->IsFailure()) {
+    bool top_level = !scope_site.is_null() &&
+        top().is_identical_to(scope_site);
+
+    scope_site->set_transition_info(*object);
+    if (FLAG_trace_creation_allocation_sites) {
+      if (top_level) {
+        PrintF("*** Setting AllocationSite %p transition_info %p\n",
+               static_cast<void*>(*scope_site),
+               static_cast<void*>(*object));
+      } else {
+        PrintF("Setting AllocationSite (%p, %p) transition_info %p\n",
+               static_cast<void*>(*top()),
+               static_cast<void*>(*scope_site),
+               static_cast<void*>(*object));
+      }
+    }
+  }
+}
+
+
+Handle<AllocationSite> AllocationSiteUsageContext::EnterNewScope() {
+  if (top().is_null()) {
+    InitializeTraversal(top_site_);
+  } else {
+    // Advance current site
+    Object* nested_site = current()->nested_site();
+    // Something is wrong if we advance to the end of the list here.
+    ASSERT(nested_site->IsAllocationSite());
+    update_current_site(AllocationSite::cast(nested_site));
+  }
+  return Handle<AllocationSite>(*current(), isolate());
+}
+
+
+void AllocationSiteUsageContext::ExitScope(
+    Handle<AllocationSite> scope_site,
+    Handle<JSObject> object) {
+  // This assert ensures that we are pointing at the right sub-object in a
+  // recursive walk of a nested literal.
+  ASSERT(object.is_null() || *object == scope_site->transition_info());
+}
+
+} }  // namespace v8::internal
=======================================
--- /dev/null
+++ /branches/bleeding_edge/src/allocation-site-scopes.h Wed Oct 16 08:10:36 2013 UTC
@@ -0,0 +1,115 @@
+// Copyright 2013 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.
+
+#ifndef V8_ALLOCATION_SITE_SCOPES_H_
+#define V8_ALLOCATION_SITE_SCOPES_H_
+
+#include "ast.h"
+#include "handles.h"
+#include "objects.h"
+#include "zone.h"
+
+namespace v8 {
+namespace internal {
+
+
+// AllocationSiteContext is the base class for walking and copying a nested
+// boilerplate with AllocationSite and AllocationMemento support.
+class AllocationSiteContext {
+ public:
+  AllocationSiteContext(Isolate* isolate, bool activated) {
+    isolate_ = isolate;
+    activated_ = activated;
+  };
+  virtual ~AllocationSiteContext() {}
+
+  Handle<AllocationSite> top() { return top_; }
+  Handle<AllocationSite> current() { return current_; }
+
+  // If activated, then recursively create mementos
+  bool activated() const { return activated_; }
+
+  // Returns the AllocationSite that matches this scope.
+  virtual Handle<AllocationSite> EnterNewScope() = 0;
+
+ // scope_site should be the handle returned by the matching EnterNewScope()
+  virtual void ExitScope(Handle<AllocationSite> scope_site,
+                         Handle<JSObject> object) = 0;
+
+ protected:
+  void update_current_site(AllocationSite* site) {
+    *(current_.location()) = site;
+  }
+
+  Isolate* isolate() { return isolate_; }
+  void InitializeTraversal(Handle<AllocationSite> site) {
+    top_ = site;
+    current_ = Handle<AllocationSite>(*top_, isolate());
+  }
+
+ private:
+  Isolate* isolate_;
+  Handle<AllocationSite> top_;
+  Handle<AllocationSite> current_;
+  bool activated_;
+};
+
+
+// AllocationSiteCreationContext aids in the creation of AllocationSites to
+// accompany object literals.
+class AllocationSiteCreationContext : public AllocationSiteContext {
+ public:
+  explicit AllocationSiteCreationContext(Isolate* isolate)
+      : AllocationSiteContext(isolate, true) { }
+
+  virtual Handle<AllocationSite> EnterNewScope() V8_OVERRIDE;
+  virtual void ExitScope(Handle<AllocationSite> site,
+                         Handle<JSObject> object) V8_OVERRIDE;
+};
+
+
+// AllocationSiteUsageContext aids in the creation of AllocationMementos placed
+// behind some/all components of a copied object literal.
+class AllocationSiteUsageContext : public AllocationSiteContext {
+ public:
+  AllocationSiteUsageContext(Isolate* isolate, Handle<AllocationSite> site,
+                             bool activated)
+      : AllocationSiteContext(isolate, activated),
+        top_site_(site) { }
+
+  virtual Handle<AllocationSite> EnterNewScope() V8_OVERRIDE;
+  virtual void ExitScope(Handle<AllocationSite> site,
+                         Handle<JSObject> object) V8_OVERRIDE;
+
+ private:
+  Handle<AllocationSite> top_site_;
+};
+
+
+} }  // namespace v8::internal
+
+#endif  // V8_ALLOCATION_SITE_SCOPES_H_
=======================================
--- /branches/bleeding_edge/src/flag-definitions.h Tue Oct 15 15:35:23 2013 UTC +++ /branches/bleeding_edge/src/flag-definitions.h Wed Oct 16 08:10:36 2013 UTC
@@ -806,6 +806,9 @@
 // elements.cc
DEFINE_bool(trace_elements_transitions, false, "trace elements transitions")

+DEFINE_bool(trace_creation_allocation_sites, false,
+            "trace the creation of allocation sites")
+
 // code-stubs.cc
 DEFINE_bool(print_code_stubs, false, "print code stubs")
 DEFINE_bool(test_secondary_stub_cache,
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc     Tue Oct 15 15:35:23 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen.cc     Wed Oct 16 08:10:36 2013 UTC
@@ -30,6 +30,7 @@
 #include <algorithm>

 #include "v8.h"
+#include "allocation-site-scopes.h"
 #include "codegen.h"
 #include "full-codegen.h"
 #include "hashmap.h"
@@ -4298,7 +4299,10 @@

   if (!boilerplate.is_null() &&
       IsFastLiteral(boilerplate, kMaxFastLiteralDepth, &max_properties)) {
-    literal = BuildFastLiteral(boilerplate);
+    AllocationSiteUsageContext usage_context(isolate(), site, false);
+    usage_context.EnterNewScope();
+    literal = BuildFastLiteral(boilerplate, &usage_context);
+    usage_context.ExitScope(site, boilerplate);
   } else {
     NoObservableSideEffectsScope no_effects(this);
     Handle<FixedArray> closure_literals(closure->literals(), isolate());
@@ -4314,6 +4318,9 @@
     Add<HPushArgument>(Add<HConstant>(constant_properties));
     Add<HPushArgument>(Add<HConstant>(flags));

+    // TODO(mvstanton): Add a flag to turn off creation of any
+ // AllocationMementos for this call: we are in crankshaft and should have
+    // learned enough about transition behavior to stop emitting mementos.
     Runtime::FunctionId function_id = Runtime::kCreateObjectLiteral;
     literal = Add<HCallRuntime>(isolate()->factory()->empty_string(),
                                 Runtime::FunctionForId(function_id),
@@ -4404,45 +4411,50 @@
   bool uninitialized = false;
   Handle<Object> literals_cell(literals->get(expr->literal_index()),
                                isolate());
-  Handle<Object> raw_boilerplate;
+  Handle<JSObject> boilerplate_object;
   if (literals_cell->IsUndefined()) {
     uninitialized = true;
-    raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
+ Handle<Object> raw_boilerplate = Runtime::CreateArrayLiteralBoilerplate(
         isolate(), literals, expr->constant_elements());
     if (raw_boilerplate.is_null()) {
       return Bailout(kArrayBoilerplateCreationFailed);
     }

-    site = isolate()->factory()->NewAllocationSite();
-    site->set_transition_info(*raw_boilerplate);
+    boilerplate_object = Handle<JSObject>::cast(raw_boilerplate);
+    AllocationSiteCreationContext creation_context(isolate());
+    site = creation_context.EnterNewScope();
+ if (JSObject::DeepWalk(boilerplate_object, &creation_context).is_null()) {
+      return Bailout(kArrayBoilerplateCreationFailed);
+    }
+    creation_context.ExitScope(site, boilerplate_object);
     literals->set(expr->literal_index(), *site);

-    if (JSObject::cast(*raw_boilerplate)->elements()->map() ==
+    if (boilerplate_object->elements()->map() ==
         isolate()->heap()->fixed_cow_array_map()) {
       isolate()->counters()->cow_arrays_created_runtime()->Increment();
     }
   } else {
     ASSERT(literals_cell->IsAllocationSite());
     site = Handle<AllocationSite>::cast(literals_cell);
-    raw_boilerplate = Handle<Object>(site->transition_info(), isolate());
+    boilerplate_object = Handle<JSObject>(
+        JSObject::cast(site->transition_info()), isolate());
   }

-  ASSERT(!raw_boilerplate.is_null());
-  ASSERT(site->IsLiteralSite());
+  ASSERT(!boilerplate_object.is_null());
+  ASSERT(site->SitePointsToLiteral());

-  Handle<JSObject> boilerplate_object =
-      Handle<JSObject>::cast(raw_boilerplate);
   ElementsKind boilerplate_elements_kind =
-      Handle<JSObject>::cast(boilerplate_object)->GetElementsKind();
-
- ASSERT(AllocationSite::CanTrack(boilerplate_object->map()->instance_type()));
+      boilerplate_object->GetElementsKind();

   // Check whether to use fast or slow deep-copying for boilerplate.
   int max_properties = kMaxFastLiteralProperties;
   if (IsFastLiteral(boilerplate_object,
                     kMaxFastLiteralDepth,
                     &max_properties)) {
-    literal = BuildFastLiteral(boilerplate_object);
+    AllocationSiteUsageContext usage_context(isolate(), site, false);
+    usage_context.EnterNewScope();
+    literal = BuildFastLiteral(boilerplate_object, &usage_context);
+    usage_context.ExitScope(site, boilerplate_object);
   } else {
     NoObservableSideEffectsScope no_effects(this);
     // Boilerplate already exists and constant elements are never accessed,
@@ -4454,6 +4466,9 @@
     Add<HPushArgument>(Add<HConstant>(literal_index));
     Add<HPushArgument>(Add<HConstant>(constants));

+    // TODO(mvstanton): Consider a flag to turn off creation of any
+ // AllocationMementos for this call: we are in crankshaft and should have
+    // learned enough about transition behavior to stop emitting mementos.
     Runtime::FunctionId function_id = (expr->depth() > 1)
? Runtime::kCreateArrayLiteral : Runtime::kCreateArrayLiteralShallow;
     literal = Add<HCallRuntime>(isolate()->factory()->empty_string(),
@@ -8342,7 +8357,8 @@


 HInstruction* HOptimizedGraphBuilder::BuildFastLiteral(
-    Handle<JSObject> boilerplate_object) {
+    Handle<JSObject> boilerplate_object,
+    AllocationSiteContext* site_context) {
   NoObservableSideEffectsScope no_effects(this);
   InstanceType instance_type = boilerplate_object->map()->instance_type();
ASSERT(instance_type == JS_ARRAY_TYPE || instance_type == JS_OBJECT_TYPE);
@@ -8374,15 +8390,15 @@
   }
BuildInitElementsInObjectHeader(boilerplate_object, object, object_elements);

-
   // Copy object elements if non-COW.
   if (object_elements != NULL) {
-    BuildEmitElements(boilerplate_object, elements, object_elements);
+    BuildEmitElements(boilerplate_object, elements, object_elements,
+                      site_context);
   }

   // Copy in-object properties.
   if (boilerplate_object->map()->NumberOfFields() != 0) {
-    BuildEmitInObjectProperties(boilerplate_object, object);
+    BuildEmitInObjectProperties(boilerplate_object, object, site_context);
   }
   return object;
 }
@@ -8434,7 +8450,8 @@

 void HOptimizedGraphBuilder::BuildEmitInObjectProperties(
     Handle<JSObject> boilerplate_object,
-    HInstruction* object) {
+    HInstruction* object,
+    AllocationSiteContext* site_context) {
   Handle<DescriptorArray> descriptors(
       boilerplate_object->map()->instance_descriptors());
   int limit = boilerplate_object->map()->NumberOfOwnDescriptors();
@@ -8458,7 +8475,10 @@

     if (value->IsJSObject()) {
       Handle<JSObject> value_object = Handle<JSObject>::cast(value);
-      HInstruction* result = BuildFastLiteral(value_object);
+      Handle<AllocationSite> current_site = site_context->EnterNewScope();
+      HInstruction* result =
+          BuildFastLiteral(value_object, site_context);
+      site_context->ExitScope(current_site, value_object);
       Add<HStoreNamedField>(object, access, result);
     } else {
       Representation representation = details.representation();
@@ -8467,6 +8487,12 @@
       if (representation.IsDouble()) {
         // Allocate a HeapNumber box and store the value into it.
         HValue* heap_number_constant = Add<HConstant>(HeapNumber::kSize);
+ // TODO(mvstanton): This heap number alloc does not have a corresponding
+        // AllocationSite. That is okay because
+ // 1) it's a child object of another object with a valid allocation site
+        // 2) we can just use the mode of the parent object for pretenuring
+        // The todo is replace GetPretenureMode() with
+        // site_context->top()->GetPretenureMode().
         HInstruction* double_box =
             Add<HAllocate>(heap_number_constant, HType::HeapNumber(),
                 isolate()->heap()->GetPretenureMode(), HEAP_NUMBER_TYPE);
@@ -8496,7 +8522,8 @@
 void HOptimizedGraphBuilder::BuildEmitElements(
     Handle<JSObject> boilerplate_object,
     Handle<FixedArrayBase> elements,
-    HValue* object_elements) {
+    HValue* object_elements,
+    AllocationSiteContext* site_context) {
   ElementsKind kind = boilerplate_object->map()->elements_kind();
   int elements_length = elements->length();
   HValue* object_elements_length = Add<HConstant>(elements_length);
@@ -8506,7 +8533,8 @@
   if (elements->IsFixedDoubleArray()) {
     BuildEmitFixedDoubleArray(elements, kind, object_elements);
   } else if (elements->IsFixedArray()) {
-    BuildEmitFixedArray(elements, kind, object_elements);
+    BuildEmitFixedArray(elements, kind, object_elements,
+                        site_context);
   } else {
     UNREACHABLE();
   }
@@ -8535,7 +8563,8 @@
 void HOptimizedGraphBuilder::BuildEmitFixedArray(
     Handle<FixedArrayBase> elements,
     ElementsKind kind,
-    HValue* object_elements) {
+    HValue* object_elements,
+    AllocationSiteContext* site_context) {
   HInstruction* boilerplate_elements = Add<HConstant>(elements);
   int elements_length = elements->length();
   Handle<FixedArray> fast_elements = Handle<FixedArray>::cast(elements);
@@ -8544,7 +8573,10 @@
     HValue* key_constant = Add<HConstant>(i);
     if (value->IsJSObject()) {
       Handle<JSObject> value_object = Handle<JSObject>::cast(value);
-      HInstruction* result = BuildFastLiteral(value_object);
+      Handle<AllocationSite> current_site = site_context->EnterNewScope();
+      HInstruction* result =
+          BuildFastLiteral(value_object, site_context);
+      site_context->ExitScope(current_site, value_object);
       Add<HStoreKeyed>(object_elements, key_constant, result, kind);
     } else {
       HInstruction* value_instruction =
=======================================
--- /branches/bleeding_edge/src/hydrogen.h      Tue Oct 15 15:35:23 2013 UTC
+++ /branches/bleeding_edge/src/hydrogen.h      Wed Oct 16 08:10:36 2013 UTC
@@ -2248,7 +2248,8 @@

   HInstruction* BuildThisFunction();

-  HInstruction* BuildFastLiteral(Handle<JSObject> boilerplate_object);
+  HInstruction* BuildFastLiteral(Handle<JSObject> boilerplate_object,
+                                 AllocationSiteContext* site_context);

   void BuildEmitObjectHeader(Handle<JSObject> boilerplate_object,
                              HInstruction* object);
@@ -2258,11 +2259,13 @@
                                        HInstruction* object_elements);

   void BuildEmitInObjectProperties(Handle<JSObject> boilerplate_object,
-                                   HInstruction* object);
+                                   HInstruction* object,
+                                   AllocationSiteContext* site_context);

   void BuildEmitElements(Handle<JSObject> boilerplate_object,
                          Handle<FixedArrayBase> elements,
-                         HValue* object_elements);
+                         HValue* object_elements,
+                         AllocationSiteContext* site_context);

   void BuildEmitFixedDoubleArray(Handle<FixedArrayBase> elements,
                                  ElementsKind kind,
@@ -2270,7 +2273,8 @@

   void BuildEmitFixedArray(Handle<FixedArrayBase> elements,
                            ElementsKind kind,
-                           HValue* object_elements);
+                           HValue* object_elements,
+                           AllocationSiteContext* site_context);

   void AddCheckPrototypeMaps(Handle<JSObject> holder,
                              Handle<Map> receiver_map);
=======================================
--- /branches/bleeding_edge/src/objects.cc      Tue Oct 15 19:28:11 2013 UTC
+++ /branches/bleeding_edge/src/objects.cc      Wed Oct 16 08:10:36 2013 UTC
@@ -28,6 +28,7 @@
 #include "v8.h"

 #include "accessors.h"
+#include "allocation-site-scopes.h"
 #include "api.h"
 #include "arguments.h"
 #include "bootstrapper.h"
@@ -5611,6 +5612,14 @@
   object->set_map(*new_map);
 }

+
+Handle<JSObject> JSObject::Copy(Handle<JSObject> object,
+                                Handle<AllocationSite> site) {
+  Isolate* isolate = object->GetIsolate();
+  CALL_HEAP_FUNCTION(isolate,
+ isolate->heap()->CopyJSObject(*object, *site), JSObject);
+}
+

 Handle<JSObject> JSObject::Copy(Handle<JSObject> object) {
   Isolate* isolate = object->GetIsolate();
@@ -5621,45 +5630,93 @@

 class JSObjectWalkVisitor {
  public:
-  explicit JSObjectWalkVisitor() {}
+  explicit JSObjectWalkVisitor(AllocationSiteContext* site_context) :
+      site_context_(site_context) {}
   virtual ~JSObjectWalkVisitor() {}

   Handle<JSObject> Visit(Handle<JSObject> object) {
     return StructureWalk(object);
   }

-  // Returns true if the visitor is a copying visitor.
   virtual bool is_copying() = 0;

  protected:
   Handle<JSObject> StructureWalk(Handle<JSObject> object);

-  // The returned handle should point to a new object if the visitor is a
-  // copying visitor, otherwise it should be the same as the input object.
+ // The returned handle will be used for the object in all subsequent usages.
+  // This allows VisitObject to make a copy of the object if desired.
   virtual Handle<JSObject> VisitObject(Handle<JSObject> object) = 0;
-
-  // The returned handle should point to a new value if the visitor is a
-  // copying visitor, otherwise it should be the same as the input value.
   virtual Handle<JSObject> VisitElementOrProperty(Handle<JSObject> object,
Handle<JSObject> value) = 0;
+
+  AllocationSiteContext* site_context() { return site_context_; }
+
+ private:
+  AllocationSiteContext* site_context_;
 };


 class JSObjectCopyVisitor: public JSObjectWalkVisitor {
  public:
-  explicit JSObjectCopyVisitor() {}
+  explicit JSObjectCopyVisitor(AllocationSiteContext* site_context)
+      : JSObjectWalkVisitor(site_context) {}

   virtual bool is_copying() V8_OVERRIDE { return true; }

- protected:
+  // The returned handle will be used for the object in all
+  // subsequent usages. This allows VisitObject to make a copy
+  // of the object if desired.
+ virtual Handle<JSObject> VisitObject(Handle<JSObject> object) V8_OVERRIDE {
+    // Only create a memento if
+    // 1) we have a JSArray, and
+    // 2) the elements kind is palatable
+    // 3) allow_mementos is true
+    Handle<JSObject> copy;
+    if (site_context()->activated() &&
+        AllocationSite::CanTrack(object->map()->instance_type()) &&
+        AllocationSite::GetMode(object->GetElementsKind()) ==
+        TRACK_ALLOCATION_SITE) {
+      copy = JSObject::Copy(object, site_context()->current());
+    } else {
+      copy = JSObject::Copy(object);
+    }
+
+    return copy;
+  }
+
+  virtual Handle<JSObject> VisitElementOrProperty(
+      Handle<JSObject> object,
+      Handle<JSObject> value) V8_OVERRIDE {
+    Handle<AllocationSite> current_site = site_context()->EnterNewScope();
+    Handle<JSObject> copy_of_value = StructureWalk(value);
+    site_context()->ExitScope(current_site, value);
+    return copy_of_value;
+  }
+};
+
+
+class JSObjectCreateAllocationSitesVisitor: public JSObjectWalkVisitor {
+ public:
+  explicit JSObjectCreateAllocationSitesVisitor(
+      AllocationSiteContext* site_context)
+      : JSObjectWalkVisitor(site_context) {}
+
+  virtual bool is_copying() V8_OVERRIDE { return false; }
+
+  // The returned handle will be used for the object in all
+  // subsequent usages. This allows VisitObject to make a copy
+  // of the object if desired.
virtual Handle<JSObject> VisitObject(Handle<JSObject> object) V8_OVERRIDE {
-    return JSObject::Copy(object);
+    return object;
   }

   virtual Handle<JSObject> VisitElementOrProperty(
       Handle<JSObject> object,
       Handle<JSObject> value) V8_OVERRIDE {
-    return StructureWalk(value);
+    Handle<AllocationSite> current_site = site_context()->EnterNewScope();
+    value = StructureWalk(value);
+    site_context()->ExitScope(current_site, value);
+    return value;
   }
 };

@@ -5806,8 +5863,19 @@
 }


-Handle<JSObject> JSObject::DeepCopy(Handle<JSObject> object) {
-  JSObjectCopyVisitor v;
+Handle<JSObject> JSObject::DeepWalk(Handle<JSObject> object,
+                                    AllocationSiteContext* site_context) {
+  JSObjectCreateAllocationSitesVisitor v(site_context);
+  Handle<JSObject> result = v.Visit(object);
+  ASSERT(!v.is_copying() &&
+         (result.is_null() || result.is_identical_to(object)));
+  return result;
+}
+
+
+Handle<JSObject> JSObject::DeepCopy(Handle<JSObject> object,
+                                    AllocationSiteContext* site_context) {
+  JSObjectCopyVisitor v(site_context);
   Handle<JSObject> copy = v.Visit(object);
   ASSERT(v.is_copying() && !copy.is_identical_to(object));
   return copy;
@@ -12575,6 +12643,20 @@
   CALL_HEAP_FUNCTION_VOID(object->GetIsolate(),
                           object->TransitionElementsKind(to_kind));
 }
+
+
+bool AllocationSite::IsNestedSite() {
+  ASSERT(FLAG_trace_track_allocation_sites);
+  Object* current = GetHeap()->allocation_sites_list();
+  while (current != NULL && current->IsAllocationSite()) {
+    AllocationSite* current_site = AllocationSite::cast(current);
+    if (current_site->nested_site() == this) {
+      return true;
+    }
+    current = current_site->weak_next();
+  }
+  return false;
+}


 MaybeObject* JSObject::UpdateAllocationSite(ElementsKind to_kind) {
@@ -12589,7 +12671,8 @@

   // Walk through to the Allocation Site
   AllocationSite* site = memento->GetAllocationSite();
-  if (site->IsLiteralSite()) {
+  if (site->SitePointsToLiteral() &&
+      site->transition_info()->IsJSArray()) {
     JSArray* transition_info = JSArray::cast(site->transition_info());
     ElementsKind kind = transition_info->GetElementsKind();
     // if kind is holey ensure that to_kind is as well.
@@ -12603,9 +12686,11 @@
       CHECK(transition_info->length()->ToArrayIndex(&length));
       if (length <= AllocationSite::kMaximumArrayBytesToPretransition) {
         if (FLAG_trace_track_allocation_sites) {
+          bool is_nested = site->IsNestedSite();
           PrintF(
-              "AllocationSite: JSArray %p boilerplate updated %s->%s\n",
+              "AllocationSite: JSArray %p boilerplate %s updated %s->%s\n",
               reinterpret_cast<void*>(this),
+              is_nested ? "(nested)" : "",
               ElementsKindToString(kind),
               ElementsKindToString(to_kind));
         }
=======================================
--- /branches/bleeding_edge/src/objects.h       Tue Oct 15 19:28:11 2013 UTC
+++ /branches/bleeding_edge/src/objects.h       Wed Oct 16 08:10:36 2013 UTC
@@ -865,8 +865,9 @@
   inline void set_##name(type* value,                                   \
                          WriteBarrierMode mode = UPDATE_WRITE_BARRIER); \

-
 class AccessorPair;
+class AllocationSite;
+class AllocationSiteContext;
 class DictionaryElementsAccessor;
 class ElementsAccessor;
 class Failure;
@@ -2544,8 +2545,13 @@
   static void SetObserved(Handle<JSObject> object);

   // Copy object.
+  static Handle<JSObject> Copy(Handle<JSObject> object,
+                               Handle<AllocationSite> site);
   static Handle<JSObject> Copy(Handle<JSObject> object);
-  static Handle<JSObject> DeepCopy(Handle<JSObject> object);
+  static Handle<JSObject> DeepCopy(Handle<JSObject> object,
+                                   AllocationSiteContext* site_context);
+  static Handle<JSObject> DeepWalk(Handle<JSObject> object,
+                                   AllocationSiteContext* site_context);

   // Casting.
   static inline JSObject* cast(Object* obj);
@@ -8007,8 +8013,15 @@

   inline void Initialize();

+  bool HasNestedSites() {
+    return nested_site()->IsAllocationSite();
+  }
+
+  // This method is expensive, it should only be called for reporting.
+  bool IsNestedSite();
+
   ElementsKind GetElementsKind() {
-    ASSERT(!IsLiteralSite());
+    ASSERT(!SitePointsToLiteral());
return static_cast<ElementsKind>(Smi::cast(transition_info())->value());
   }

@@ -8016,11 +8029,11 @@
     set_transition_info(Smi::FromInt(static_cast<int>(kind)));
   }

-  bool IsLiteralSite() {
+  bool SitePointsToLiteral() {
     // If transition_info is a smi, then it represents an ElementsKind
     // for a constructed array. Otherwise, it must be a boilerplate
-    // for an array literal
-    return transition_info()->IsJSArray();
+    // for an object or array literal.
+ return transition_info()->IsJSArray() || transition_info()->IsJSObject();
   }

   DECLARE_PRINTER(AllocationSite)
=======================================
--- /branches/bleeding_edge/src/runtime.cc      Tue Oct 15 19:28:11 2013 UTC
+++ /branches/bleeding_edge/src/runtime.cc      Wed Oct 16 08:10:36 2013 UTC
@@ -31,6 +31,7 @@
 #include "v8.h"

 #include "accessors.h"
+#include "allocation-site-scopes.h"
 #include "api.h"
 #include "arguments.h"
 #include "bootstrapper.h"
@@ -488,25 +489,35 @@
   // Check if boilerplate exists. If not, create it first.
   Handle<Object> literal_site(literals->get(literals_index), isolate);
   Handle<AllocationSite> site;
-  Handle<Object> boilerplate;
+  Handle<JSObject> boilerplate;
   if (*literal_site == isolate->heap()->undefined_value()) {
-    boilerplate = CreateObjectLiteralBoilerplate(isolate,
-                                                 literals,
-                                                 constant_properties,
-                                                 should_have_fast_elements,
-                                                 has_function_literal);
-    RETURN_IF_EMPTY_HANDLE(isolate, boilerplate);
-    site = isolate->factory()->NewAllocationSite();
-    site->set_transition_info(*boilerplate);
+    Handle<Object> raw_boilerplate = CreateObjectLiteralBoilerplate(
+        isolate,
+        literals,
+        constant_properties,
+        should_have_fast_elements,
+        has_function_literal);
+    RETURN_IF_EMPTY_HANDLE(isolate, raw_boilerplate);
+    boilerplate = Handle<JSObject>::cast(raw_boilerplate);
+
+    AllocationSiteCreationContext creation_context(isolate);
+    site = creation_context.EnterNewScope();
+    RETURN_IF_EMPTY_HANDLE(isolate,
+ JSObject::DeepWalk(boilerplate, &creation_context));
+    creation_context.ExitScope(site, boilerplate);

     // Update the functions literal and return the boilerplate.
     literals->set(literals_index, *site);
   } else {
     site = Handle<AllocationSite>::cast(literal_site);
- boilerplate = Handle<JSObject>(JSObject::cast(site->transition_info()));
+    boilerplate = Handle<JSObject>(JSObject::cast(site->transition_info()),
+                                   isolate);
   }

- Handle<Object> copy = JSObject::DeepCopy(Handle<JSObject>::cast(boilerplate));
+  AllocationSiteUsageContext usage_context(isolate, site, true);
+  usage_context.EnterNewScope();
+  Handle<Object> copy = JSObject::DeepCopy(boilerplate, &usage_context);
+  usage_context.ExitScope(site, boilerplate);
   RETURN_IF_EMPTY_HANDLE(isolate, copy);
   return *copy;
 }
@@ -524,12 +535,16 @@
     ASSERT(*elements != isolate->heap()->empty_fixed_array());
     Handle<Object> boilerplate =
Runtime::CreateArrayLiteralBoilerplate(isolate, literals, elements);
-    if (boilerplate.is_null()) {
-      ASSERT(site.is_null());
-      return site;
+    if (boilerplate.is_null()) return Handle<AllocationSite>::null();
+
+    AllocationSiteCreationContext creation_context(isolate);
+    site = creation_context.EnterNewScope();
+    if (JSObject::DeepWalk(Handle<JSObject>::cast(boilerplate),
+                           &creation_context).is_null()) {
+      return Handle<AllocationSite>::null();
     }
-    site = isolate->factory()->NewAllocationSite();
-    site->set_transition_info(*boilerplate);
+    creation_context.ExitScope(site, Handle<JSObject>::cast(boilerplate));
+
     literals->set(literals_index, *site);
   } else {
     site = Handle<AllocationSite>::cast(literal_site);
@@ -551,7 +566,10 @@
   RETURN_IF_EMPTY_HANDLE(isolate, site);

   Handle<JSObject> boilerplate(JSObject::cast(site->transition_info()));
-  Handle<JSObject> copy = JSObject::DeepCopy(boilerplate);
+  AllocationSiteUsageContext usage_context(isolate, site, true);
+  usage_context.EnterNewScope();
+  Handle<JSObject> copy = JSObject::DeepCopy(boilerplate, &usage_context);
+  usage_context.ExitScope(site, boilerplate);
   RETURN_IF_EMPTY_HANDLE(isolate, copy);
   return *copy;
 }
@@ -574,9 +592,8 @@
     isolate->counters()->cow_arrays_created_runtime()->Increment();
   }

-  AllocationSiteMode mode = AllocationSite::GetMode(
-      boilerplate->GetElementsKind());
-  if (mode == TRACK_ALLOCATION_SITE) {
+  if (AllocationSite::GetMode(boilerplate->GetElementsKind()) ==
+      TRACK_ALLOCATION_SITE) {
     return isolate->heap()->CopyJSObject(boilerplate, *site);
   }

@@ -14686,7 +14703,7 @@
     Handle<Cell> cell = Handle<Cell>::cast(type_info);
     Handle<AllocationSite> site = Handle<AllocationSite>(
         AllocationSite::cast(cell->value()), isolate);
-    ASSERT(!site->IsLiteralSite());
+    ASSERT(!site->SitePointsToLiteral());
     ElementsKind to_kind = site->GetElementsKind();
     if (holey && !IsFastHoleyElementsKind(to_kind)) {
       to_kind = GetHoleyElementsKind(to_kind);
=======================================
--- /branches/bleeding_edge/test/mjsunit/allocation-site-info.js Tue Oct 15 15:35:23 2013 UTC +++ /branches/bleeding_edge/test/mjsunit/allocation-site-info.js Wed Oct 16 08:10:36 2013 UTC
@@ -383,4 +383,114 @@

   instanceof_check(realmBArray);
   assertUnoptimized(instanceof_check);
+
+  // Case: make sure nested arrays benefit from allocation site feedback as
+  // well.
+  (function() {
+    // Make sure we handle nested arrays
+   function get_nested_literal() {
+     var literal = [[1,2,3,4], [2], [3]];
+     return literal;
+   }
+
+   obj = get_nested_literal();
+   assertKind(elements_kind.fast, obj);
+   obj[0][0] = 3.5;
+   obj[2][0] = "hello";
+   obj = get_nested_literal();
+   assertKind(elements_kind.fast_double, obj[0]);
+   assertKind(elements_kind.fast_smi_only, obj[1]);
+   assertKind(elements_kind.fast, obj[2]);
+
+   // A more complex nested literal case.
+   function get_deep_nested_literal() {
+     var literal = [[1], [[2], "hello"], 3, [4]];
+     return literal;
+   }
+
+   obj = get_deep_nested_literal();
+   assertKind(elements_kind.fast_smi_only, obj[1][0]);
+   obj[0][0] = 3.5;
+   obj[1][0][0] = "goodbye";
+   assertKind(elements_kind.fast_double, obj[0]);
+   assertKind(elements_kind.fast, obj[1][0]);
+
+   obj = get_deep_nested_literal();
+   assertKind(elements_kind.fast_double, obj[0]);
+   assertKind(elements_kind.fast, obj[1][0]);
+  })();
+
+
+ // Make sure object literals with array fields benefit from the type feedback
+  // that allocation mementos provide.
+  (function() {
+    // A literal in an object
+    function get_object_literal() {
+      var literal = {
+        array: [1,2,3],
+        data: 3.5
+      };
+      return literal;
+    }
+
+    obj = get_object_literal();
+    assertKind(elements_kind.fast_smi_only, obj.array);
+    obj.array[1] = 3.5;
+    assertKind(elements_kind.fast_double, obj.array);
+    obj = get_object_literal();
+    assertKind(elements_kind.fast_double, obj.array);
+
+    function get_nested_object_literal() {
+      var literal = {
+        array: [[1],[2],[3]],
+        data: 3.5
+      };
+      return literal;
+    }
+
+    obj = get_nested_object_literal();
+    assertKind(elements_kind.fast, obj.array);
+    assertKind(elements_kind.fast_smi_only, obj.array[1]);
+    obj.array[1][0] = 3.5;
+    assertKind(elements_kind.fast_double, obj.array[1]);
+    obj = get_nested_object_literal();
+    assertKind(elements_kind.fast_double, obj.array[1]);
+
+    %OptimizeFunctionOnNextCall(get_nested_object_literal);
+    get_nested_object_literal();
+    obj = get_nested_object_literal();
+    assertKind(elements_kind.fast_double, obj.array[1]);
+
+    // Make sure we handle nested arrays
+    function get_nested_literal() {
+      var literal = [[1,2,3,4], [2], [3]];
+      return literal;
+    }
+
+    obj = get_nested_literal();
+    assertKind(elements_kind.fast, obj);
+    obj[0][0] = 3.5;
+    obj[2][0] = "hello";
+    obj = get_nested_literal();
+    assertKind(elements_kind.fast_double, obj[0]);
+    assertKind(elements_kind.fast_smi_only, obj[1]);
+    assertKind(elements_kind.fast, obj[2]);
+
+    // A more complex nested literal case.
+    function get_deep_nested_literal() {
+      var literal = [[1], [[2], "hello"], 3, [4]];
+      return literal;
+    }
+
+    obj = get_deep_nested_literal();
+    assertKind(elements_kind.fast_smi_only, obj[1][0]);
+    obj[0][0] = 3.5;
+    obj[1][0][0] = "goodbye";
+    assertKind(elements_kind.fast_double, obj[0]);
+    assertKind(elements_kind.fast, obj[1][0]);
+
+    obj = get_deep_nested_literal();
+    assertKind(elements_kind.fast_double, obj[0]);
+    assertKind(elements_kind.fast, obj[1][0]);
+  })();
 }
=======================================
--- /branches/bleeding_edge/tools/gyp/v8.gyp    Tue Oct 15 15:35:23 2013 UTC
+++ /branches/bleeding_edge/tools/gyp/v8.gyp    Wed Oct 16 08:10:36 2013 UTC
@@ -209,6 +209,8 @@
         '../../src/accessors.h',
         '../../src/allocation.cc',
         '../../src/allocation.h',
+        '../../src/allocation-site-scopes.cc',
+        '../../src/allocation-site-scopes.h',
         '../../src/api.cc',
         '../../src/api.h',
         '../../src/apiutils.h',

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to