Fix slack tracking when instance prototype changes.

This fixes a corner case when the instance prototype of a function is
changed while inobject slack tracking is still in progress. This caused
the intial map to be unrelated for functions with the same shared info
and hence the shared construct stub is no longer generic enough to work
for all those functions.

R=danno@chromium.org
BUG=chromium:157019
TEST=mjsunit/regress/regress-crbug-157019

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@12896 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bbcfa171
...@@ -3467,7 +3467,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3467,7 +3467,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
// r1: constructor function // r1: constructor function
// r2: initial map // r2: initial map
// r7: undefined // r7: undefined
ASSERT(function->has_initial_map());
__ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset)); __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceSizeOffset));
#ifdef DEBUG
int instance_size = function->initial_map()->instance_size();
__ cmp(r3, Operand(instance_size >> kPointerSizeLog2));
__ Check(eq, "Instance size of initial map changed.");
#endif
__ AllocateInNewSpace(r3, r4, r5, r6, &generic_stub_call, SIZE_IN_WORDS); __ AllocateInNewSpace(r3, r4, r5, r6, &generic_stub_call, SIZE_IN_WORDS);
// Allocated the JSObject, now initialize the fields. Map is set to initial // Allocated the JSObject, now initialize the fields. Map is set to initial
...@@ -3525,7 +3531,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3525,7 +3531,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
} }
// Fill the unused in-object property fields with undefined. // Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count(); for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties(); i < function->initial_map()->inobject_properties();
i++) { i++) {
......
...@@ -3421,6 +3421,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3421,6 +3421,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#endif #endif
// Load the initial map and verify that it is in fact a map. // Load the initial map and verify that it is in fact a map.
// edi: constructor
__ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); __ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi. // Will both indicate a NULL and a Smi.
__ JumpIfSmi(ebx, &generic_stub_call); __ JumpIfSmi(ebx, &generic_stub_call);
...@@ -3429,19 +3430,23 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3429,19 +3430,23 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#ifdef DEBUG #ifdef DEBUG
// Cannot construct functions this way. // Cannot construct functions this way.
// edi: constructor
// ebx: initial map // ebx: initial map
__ CmpInstanceType(ebx, JS_FUNCTION_TYPE); __ CmpInstanceType(ebx, JS_FUNCTION_TYPE);
__ Assert(not_equal, "Function constructed by construct stub."); __ Check(not_equal, "Function constructed by construct stub.");
#endif #endif
// Now allocate the JSObject on the heap by moving the new space allocation // Now allocate the JSObject on the heap by moving the new space allocation
// top forward. // top forward.
// edi: constructor
// ebx: initial map // ebx: initial map
ASSERT(function->has_initial_map());
int instance_size = function->initial_map()->instance_size();
#ifdef DEBUG
__ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset)); __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset));
__ shl(ecx, kPointerSizeLog2); __ shl(ecx, kPointerSizeLog2);
__ AllocateInNewSpace(ecx, edx, ecx, no_reg, __ cmp(ecx, Immediate(instance_size));
__ Check(equal, "Instance size of initial map changed.");
#endif
__ AllocateInNewSpace(instance_size, edx, ecx, no_reg,
&generic_stub_call, NO_ALLOCATION_FLAGS); &generic_stub_call, NO_ALLOCATION_FLAGS);
// Allocated the JSObject, now initialize the fields and add the heap tag. // Allocated the JSObject, now initialize the fields and add the heap tag.
...@@ -3501,7 +3506,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3501,7 +3506,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
} }
// Fill the unused in-object property fields with undefined. // Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count(); for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties(); i < function->initial_map()->inobject_properties();
i++) { i++) {
......
...@@ -3453,7 +3453,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3453,7 +3453,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
// t7: undefined // t7: undefined
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset)); __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
__ Check(ne, "Function constructed by construct stub.", __ Check(ne, "Function constructed by construct stub.",
a3, Operand(JS_FUNCTION_TYPE)); a3, Operand(JS_FUNCTION_TYPE));
#endif #endif
// Now allocate the JSObject in new space. // Now allocate the JSObject in new space.
...@@ -3461,7 +3461,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3461,7 +3461,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
// a1: constructor function // a1: constructor function
// a2: initial map // a2: initial map
// t7: undefined // t7: undefined
ASSERT(function->has_initial_map());
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset)); __ lbu(a3, FieldMemOperand(a2, Map::kInstanceSizeOffset));
#ifdef DEBUG
int instance_size = function->initial_map()->instance_size();
__ Check(eq, "Instance size of initial map changed.",
a3, Operand(instance_size >> kPointerSizeLog2));
#endif
__ AllocateInNewSpace(a3, t4, t5, t6, &generic_stub_call, SIZE_IN_WORDS); __ AllocateInNewSpace(a3, t4, t5, t6, &generic_stub_call, SIZE_IN_WORDS);
// Allocated the JSObject, now initialize the fields. Map is set to initial // Allocated the JSObject, now initialize the fields. Map is set to initial
...@@ -3524,7 +3530,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3524,7 +3530,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
} }
// Fill the unused in-object property fields with undefined. // Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count(); for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties(); i < function->initial_map()->inobject_properties();
i++) { i++) {
......
...@@ -4436,42 +4436,6 @@ void JSFunction::set_initial_map(Map* value) { ...@@ -4436,42 +4436,6 @@ void JSFunction::set_initial_map(Map* value) {
} }
MaybeObject* JSFunction::set_initial_map_and_cache_transitions(
Map* initial_map) {
Context* native_context = context()->native_context();
Object* array_function =
native_context->get(Context::ARRAY_FUNCTION_INDEX);
if (array_function->IsJSFunction() &&
this == JSFunction::cast(array_function)) {
// Replace all of the cached initial array maps in the native context with
// the appropriate transitioned elements kind maps.
Heap* heap = GetHeap();
MaybeObject* maybe_maps =
heap->AllocateFixedArrayWithHoles(kElementsKindCount);
FixedArray* maps;
if (!maybe_maps->To(&maps)) return maybe_maps;
Map* current_map = initial_map;
ElementsKind kind = current_map->elements_kind();
ASSERT(kind == GetInitialFastElementsKind());
maps->set(kind, current_map);
for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
i < kFastElementsKindCount; ++i) {
Map* new_map;
ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
MaybeObject* maybe_new_map =
current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
maps->set(next_kind, new_map);
current_map = new_map;
}
native_context->set_js_array_maps(maps);
}
set_initial_map(initial_map);
return this;
}
bool JSFunction::has_initial_map() { bool JSFunction::has_initial_map() {
return prototype_or_initial_map()->IsMap(); return prototype_or_initial_map()->IsMap();
} }
......
...@@ -7854,6 +7854,35 @@ MaybeObject* JSObject::OptimizeAsPrototype() { ...@@ -7854,6 +7854,35 @@ MaybeObject* JSObject::OptimizeAsPrototype() {
} }
MUST_USE_RESULT static MaybeObject* CacheInitialJSArrayMaps(
Context* native_context, Map* initial_map) {
// Replace all of the cached initial array maps in the native context with
// the appropriate transitioned elements kind maps.
Heap* heap = native_context->GetHeap();
MaybeObject* maybe_maps =
heap->AllocateFixedArrayWithHoles(kElementsKindCount);
FixedArray* maps;
if (!maybe_maps->To(&maps)) return maybe_maps;
Map* current_map = initial_map;
ElementsKind kind = current_map->elements_kind();
ASSERT(kind == GetInitialFastElementsKind());
maps->set(kind, current_map);
for (int i = GetSequenceIndexFromFastElementsKind(kind) + 1;
i < kFastElementsKindCount; ++i) {
Map* new_map;
ElementsKind next_kind = GetFastElementsKindFromSequenceIndex(i);
MaybeObject* maybe_new_map =
current_map->CopyAsElementsKind(next_kind, INSERT_TRANSITION);
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
maps->set(next_kind, new_map);
current_map = new_map;
}
native_context->set_js_array_maps(maps);
return initial_map;
}
MaybeObject* JSFunction::SetInstancePrototype(Object* value) { MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
ASSERT(value->IsJSReceiver()); ASSERT(value->IsJSReceiver());
Heap* heap = GetHeap(); Heap* heap = GetHeap();
...@@ -7868,14 +7897,29 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) { ...@@ -7868,14 +7897,29 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
// Now some logic for the maps of the objects that are created by using this // Now some logic for the maps of the objects that are created by using this
// function as a constructor. // function as a constructor.
if (has_initial_map()) { if (has_initial_map()) {
// If the function has allocated the initial map // If the function has allocated the initial map replace it with a
// replace it with a copy containing the new prototype. // copy containing the new prototype. Also complete any in-object
// slack tracking that is in progress at this point because it is
// still tracking the old copy.
if (shared()->IsInobjectSlackTrackingInProgress()) {
shared()->CompleteInobjectSlackTracking();
}
Map* new_map; Map* new_map;
MaybeObject* maybe_new_map = initial_map()->Copy(); MaybeObject* maybe_object = initial_map()->Copy();
if (!maybe_new_map->To(&new_map)) return maybe_new_map; if (!maybe_object->To(&new_map)) return maybe_object;
new_map->set_prototype(value); new_map->set_prototype(value);
MaybeObject* maybe_object = set_initial_map_and_cache_transitions(new_map);
if (maybe_object->IsFailure()) return maybe_object; // If the function is used as the global Array function, cache the
// initial map (and transitioned versions) in the native context.
Context* native_context = context()->native_context();
Object* array_function = native_context->get(Context::ARRAY_FUNCTION_INDEX);
if (array_function->IsJSFunction() &&
this == JSFunction::cast(array_function)) {
MaybeObject* ok = CacheInitialJSArrayMaps(native_context, new_map);
if (ok->IsFailure()) return ok;
}
set_initial_map(new_map);
} else { } else {
// Put the value in the initial map field until an initial map is // Put the value in the initial map field until an initial map is
// needed. At that point, a new initial map is created and the // needed. At that point, a new initial map is created and the
......
...@@ -6132,8 +6132,6 @@ class JSFunction: public JSObject { ...@@ -6132,8 +6132,6 @@ class JSFunction: public JSObject {
// The initial map for an object created by this constructor. // The initial map for an object created by this constructor.
inline Map* initial_map(); inline Map* initial_map();
inline void set_initial_map(Map* value); inline void set_initial_map(Map* value);
MUST_USE_RESULT inline MaybeObject* set_initial_map_and_cache_transitions(
Map* value);
inline bool has_initial_map(); inline bool has_initial_map();
// Get and set the prototype property on a JSFunction. If the // Get and set the prototype property on a JSFunction. If the
......
...@@ -3240,6 +3240,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3240,6 +3240,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#endif #endif
// Load the initial map and verify that it is in fact a map. // Load the initial map and verify that it is in fact a map.
// rdi: constructor
__ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi. // Will both indicate a NULL and a Smi.
STATIC_ASSERT(kSmiTag == 0); STATIC_ASSERT(kSmiTag == 0);
...@@ -3249,18 +3250,22 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3249,18 +3250,22 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#ifdef DEBUG #ifdef DEBUG
// Cannot construct functions this way. // Cannot construct functions this way.
// rdi: constructor
// rbx: initial map // rbx: initial map
__ CmpInstanceType(rbx, JS_FUNCTION_TYPE); __ CmpInstanceType(rbx, JS_FUNCTION_TYPE);
__ Assert(not_equal, "Function constructed by construct stub."); __ Check(not_equal, "Function constructed by construct stub.");
#endif #endif
// Now allocate the JSObject in new space. // Now allocate the JSObject in new space.
// rdi: constructor
// rbx: initial map // rbx: initial map
ASSERT(function->has_initial_map());
int instance_size = function->initial_map()->instance_size();
#ifdef DEBUG
__ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset)); __ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset));
__ shl(rcx, Immediate(kPointerSizeLog2)); __ shl(rcx, Immediate(kPointerSizeLog2));
__ AllocateInNewSpace(rcx, rdx, rcx, no_reg, __ cmpq(rcx, Immediate(instance_size));
__ Check(equal, "Instance size of initial map changed.");
#endif
__ AllocateInNewSpace(instance_size, rdx, rcx, no_reg,
&generic_stub_call, NO_ALLOCATION_FLAGS); &generic_stub_call, NO_ALLOCATION_FLAGS);
// Allocated the JSObject, now initialize the fields and add the heap tag. // Allocated the JSObject, now initialize the fields and add the heap tag.
...@@ -3306,7 +3311,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub( ...@@ -3306,7 +3311,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
} }
// Fill the unused in-object property fields with undefined. // Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count(); for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties(); i < function->initial_map()->inobject_properties();
i++) { i++) {
......
// Copyright 2012 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.
// Flags: --allow-natives-syntax --nocrankshaft
function makeConstructor() {
return function() {
this.a = 1;
this.b = 2;
};
}
var c1 = makeConstructor();
var o1 = new c1();
c1.prototype = {};
for (var i = 0; i < 10; i++) {
var o = new c1();
for (var j = 0; j < 8; j++) {
o["x" + j] = 0;
}
}
var c2 = makeConstructor();
var o2 = new c2();
for (var i = 0; i < 50000; i++) {
new c2();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment