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
This commit is contained in:
mstarzinger@chromium.org 2012-11-08 11:56:44 +00:00
parent bbcfa17134
commit a31889e2de
8 changed files with 135 additions and 57 deletions

View File

@ -3467,7 +3467,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
// r1: constructor function
// r2: initial map
// r7: undefined
ASSERT(function->has_initial_map());
__ 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);
// Allocated the JSObject, now initialize the fields. Map is set to initial
@ -3525,7 +3531,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
}
// Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties();
i++) {

View File

@ -3421,6 +3421,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#endif
// Load the initial map and verify that it is in fact a map.
// edi: constructor
__ mov(ebx, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi.
__ JumpIfSmi(ebx, &generic_stub_call);
@ -3429,19 +3430,23 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#ifdef DEBUG
// Cannot construct functions this way.
// edi: constructor
// ebx: initial map
__ CmpInstanceType(ebx, JS_FUNCTION_TYPE);
__ Assert(not_equal, "Function constructed by construct stub.");
__ Check(not_equal, "Function constructed by construct stub.");
#endif
// Now allocate the JSObject on the heap by moving the new space allocation
// top forward.
// edi: constructor
// 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));
__ 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);
// Allocated the JSObject, now initialize the fields and add the heap tag.
@ -3501,7 +3506,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
}
// Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties();
i++) {

View File

@ -3453,7 +3453,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
// t7: undefined
__ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset));
__ Check(ne, "Function constructed by construct stub.",
a3, Operand(JS_FUNCTION_TYPE));
a3, Operand(JS_FUNCTION_TYPE));
#endif
// Now allocate the JSObject in new space.
@ -3461,7 +3461,13 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
// a1: constructor function
// a2: initial map
// t7: undefined
ASSERT(function->has_initial_map());
__ 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);
// Allocated the JSObject, now initialize the fields. Map is set to initial
@ -3524,7 +3530,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
}
// Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties();
i++) {

View File

@ -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() {
return prototype_or_initial_map()->IsMap();
}

View File

@ -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) {
ASSERT(value->IsJSReceiver());
Heap* heap = GetHeap();
@ -7868,14 +7897,29 @@ MaybeObject* JSFunction::SetInstancePrototype(Object* value) {
// Now some logic for the maps of the objects that are created by using this
// function as a constructor.
if (has_initial_map()) {
// If the function has allocated the initial map
// replace it with a copy containing the new prototype.
// If the function has allocated the initial map replace it with a
// 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;
MaybeObject* maybe_new_map = initial_map()->Copy();
if (!maybe_new_map->To(&new_map)) return maybe_new_map;
MaybeObject* maybe_object = initial_map()->Copy();
if (!maybe_object->To(&new_map)) return maybe_object;
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 {
// 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

View File

@ -6132,8 +6132,6 @@ class JSFunction: public JSObject {
// The initial map for an object created by this constructor.
inline Map* initial_map();
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();
// Get and set the prototype property on a JSFunction. If the

View File

@ -3240,6 +3240,7 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#endif
// Load the initial map and verify that it is in fact a map.
// rdi: constructor
__ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset));
// Will both indicate a NULL and a Smi.
STATIC_ASSERT(kSmiTag == 0);
@ -3249,18 +3250,22 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
#ifdef DEBUG
// Cannot construct functions this way.
// rdi: constructor
// rbx: initial map
__ CmpInstanceType(rbx, JS_FUNCTION_TYPE);
__ Assert(not_equal, "Function constructed by construct stub.");
__ Check(not_equal, "Function constructed by construct stub.");
#endif
// Now allocate the JSObject in new space.
// rdi: constructor
// rbx: initial map
ASSERT(function->has_initial_map());
int instance_size = function->initial_map()->instance_size();
#ifdef DEBUG
__ movzxbq(rcx, FieldOperand(rbx, Map::kInstanceSizeOffset));
__ 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);
// Allocated the JSObject, now initialize the fields and add the heap tag.
@ -3306,7 +3311,6 @@ Handle<Code> ConstructStubCompiler::CompileConstructStub(
}
// Fill the unused in-object property fields with undefined.
ASSERT(function->has_initial_map());
for (int i = shared->this_property_assignments_count();
i < function->initial_map()->inobject_properties();
i++) {

View File

@ -0,0 +1,54 @@
// 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();
}