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.