v8:3539 - hold constructor feedback in weak cells

BUG=v8:3539
R=verwaest@chromium.org
LOG=N

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

Cr-Commit-Position: refs/heads/master@{#27581}
This commit is contained in:
mvstanton 2015-04-02 02:39:32 -07:00 committed by Commit bot
parent 2a5eb8299b
commit b134ae74b5
10 changed files with 343 additions and 157 deletions

View File

@ -2367,6 +2367,24 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
} }
static void CallStubInRecordCallTarget(MacroAssembler* masm, CodeStub* stub) {
// r0 : number of arguments to the construct function
// r2 : Feedback vector
// r3 : slot in feedback vector (Smi)
// r1 : the function to call
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL);
// Arguments register must be smi-tagged to call out.
__ SmiTag(r0);
__ Push(r3, r2, r1, r0);
__ CallStub(stub);
__ Pop(r3, r2, r1, r0);
__ SmiUntag(r0);
}
static void GenerateRecordCallTarget(MacroAssembler* masm) { static void GenerateRecordCallTarget(MacroAssembler* masm) {
// Cache the called function in a feedback vector slot. Cache states // Cache the called function in a feedback vector slot. Cache states
// are uninitialized, monomorphic (indicated by a JSFunction), and // are uninitialized, monomorphic (indicated by a JSFunction), and
@ -2388,16 +2406,30 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// A monomorphic cache hit or an already megamorphic state: invoke the // A monomorphic cache hit or an already megamorphic state: invoke the
// function without changing the state. // function without changing the state.
__ cmp(r4, r1); Label check_allocation_site;
Register feedback_map = r5;
Register weak_value = r8;
__ ldr(weak_value, FieldMemOperand(r4, WeakCell::kValueOffset));
__ cmp(r1, weak_value);
__ b(eq, &done); __ b(eq, &done);
__ CompareRoot(r4, Heap::kmegamorphic_symbolRootIndex);
__ b(eq, &done);
__ ldr(feedback_map, FieldMemOperand(r4, 0));
__ CompareRoot(feedback_map, Heap::kWeakCellMapRootIndex);
__ b(ne, FLAG_pretenuring_call_new ? &miss : &check_allocation_site);
// If r1 is not equal to the weak cell value, and the weak cell value is
// cleared, we have a new chance to become monomorphic.
__ JumpIfSmi(weak_value, &initialize);
__ jmp(&megamorphic);
if (!FLAG_pretenuring_call_new) { if (!FLAG_pretenuring_call_new) {
__ bind(&check_allocation_site);
// If we came here, we need to see if we are the array function. // If we came here, we need to see if we are the array function.
// If we didn't have a matching function, and we didn't find the megamorph // If we didn't have a matching function, and we didn't find the megamorph
// sentinel, then we have in the slot either some other function or an // sentinel, then we have in the slot either some other function or an
// AllocationSite. Do a map check on the object in ecx. // AllocationSite. Do a map check on the object in ecx.
__ ldr(r5, FieldMemOperand(r4, 0)); __ CompareRoot(feedback_map, Heap::kAllocationSiteMapRootIndex);
__ CompareRoot(r5, Heap::kAllocationSiteMapRootIndex);
__ b(ne, &miss); __ b(ne, &miss);
// Make sure the function is the Array() function // Make sure the function is the Array() function
@ -2433,33 +2465,15 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// The target function is the Array constructor, // The target function is the Array constructor,
// Create an AllocationSite if we don't already have it, store it in the // Create an AllocationSite if we don't already have it, store it in the
// slot. // slot.
{ CreateAllocationSiteStub create_stub(masm->isolate());
FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); CallStubInRecordCallTarget(masm, &create_stub);
// Arguments register must be smi-tagged to call out.
__ SmiTag(r0);
__ Push(r3, r2, r1, r0);
CreateAllocationSiteStub create_stub(masm->isolate());
__ CallStub(&create_stub);
__ Pop(r3, r2, r1, r0);
__ SmiUntag(r0);
}
__ b(&done); __ b(&done);
__ bind(&not_array_function); __ bind(&not_array_function);
} }
__ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); CreateWeakCellStub create_stub(masm->isolate());
__ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); CallStubInRecordCallTarget(masm, &create_stub);
__ str(r1, MemOperand(r4, 0));
__ Push(r4, r2, r1);
__ RecordWrite(r2, r4, r1, kLRHasNotBeenSaved, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
__ Pop(r4, r2, r1);
__ bind(&done); __ bind(&done);
} }

View File

@ -2730,16 +2730,32 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
} }
static void GenerateRecordCallTarget(MacroAssembler* masm, static void CallStubInRecordCallTarget(MacroAssembler* masm, CodeStub* stub,
Register argc, Register argc, Register function,
Register feedback_vector,
Register index) {
FrameScope scope(masm, StackFrame::INTERNAL);
// Arguments register must be smi-tagged to call out.
__ SmiTag(argc);
__ Push(argc, function, feedback_vector, index);
DCHECK(feedback_vector.Is(x2) && index.Is(x3));
__ CallStub(stub);
__ Pop(index, feedback_vector, function, argc);
__ SmiUntag(argc);
}
static void GenerateRecordCallTarget(MacroAssembler* masm, Register argc,
Register function, Register function,
Register feedback_vector, Register feedback_vector, Register index,
Register index, Register scratch1, Register scratch2,
Register scratch1, Register scratch3) {
Register scratch2) {
ASM_LOCATION("GenerateRecordCallTarget"); ASM_LOCATION("GenerateRecordCallTarget");
DCHECK(!AreAliased(scratch1, scratch2, DCHECK(!AreAliased(scratch1, scratch2, scratch3, argc, function,
argc, function, feedback_vector, index)); feedback_vector, index));
// Cache the called function in a feedback vector slot. Cache states are // Cache the called function in a feedback vector slot. Cache states are
// uninitialized, monomorphic (indicated by a JSFunction), and megamorphic. // uninitialized, monomorphic (indicated by a JSFunction), and megamorphic.
// argc : number of arguments to the construct function // argc : number of arguments to the construct function
@ -2754,22 +2770,37 @@ static void GenerateRecordCallTarget(MacroAssembler* masm,
masm->isolate()->heap()->uninitialized_symbol()); masm->isolate()->heap()->uninitialized_symbol());
// Load the cache state. // Load the cache state.
__ Add(scratch1, feedback_vector, Register feedback = scratch1;
Register feedback_map = scratch2;
Register feedback_value = scratch3;
__ Add(feedback, feedback_vector,
Operand::UntagSmiAndScale(index, kPointerSizeLog2)); Operand::UntagSmiAndScale(index, kPointerSizeLog2));
__ Ldr(scratch1, FieldMemOperand(scratch1, FixedArray::kHeaderSize)); __ Ldr(feedback, FieldMemOperand(feedback, FixedArray::kHeaderSize));
// A monomorphic cache hit or an already megamorphic state: invoke the // A monomorphic cache hit or an already megamorphic state: invoke the
// function without changing the state. // function without changing the state.
__ Cmp(scratch1, function); Label check_allocation_site;
__ Ldr(feedback_value, FieldMemOperand(feedback, WeakCell::kValueOffset));
__ Cmp(function, feedback_value);
__ B(eq, &done); __ B(eq, &done);
__ CompareRoot(feedback, Heap::kmegamorphic_symbolRootIndex);
__ B(eq, &done);
__ Ldr(feedback_map, FieldMemOperand(feedback, 0));
__ CompareRoot(feedback_map, Heap::kWeakCellMapRootIndex);
__ B(ne, FLAG_pretenuring_call_new ? &miss : &check_allocation_site);
// If function is not equal to the weak cell value, and the weak cell value is
// cleared, we have a new chance to become monomorphic.
__ JumpIfSmi(feedback_value, &initialize);
__ B(&megamorphic);
if (!FLAG_pretenuring_call_new) { if (!FLAG_pretenuring_call_new) {
__ bind(&check_allocation_site);
// If we came here, we need to see if we are the array function. // If we came here, we need to see if we are the array function.
// If we didn't have a matching function, and we didn't find the megamorph // If we didn't have a matching function, and we didn't find the megamorph
// sentinel, then we have in the slot either some other function or an // sentinel, then we have in the slot either some other function or an
// AllocationSite. Do a map check on the object in scratch1 register. // AllocationSite. Do a map check on the object in scratch1 register.
__ Ldr(scratch2, FieldMemOperand(scratch1, AllocationSite::kMapOffset)); __ JumpIfNotRoot(feedback_map, Heap::kAllocationSiteMapRootIndex, &miss);
__ JumpIfNotRoot(scratch2, Heap::kAllocationSiteMapRootIndex, &miss);
// Make sure the function is the Array() function // Make sure the function is the Array() function
__ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, scratch1); __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, scratch1);
@ -2805,39 +2836,17 @@ static void GenerateRecordCallTarget(MacroAssembler* masm,
// The target function is the Array constructor, // The target function is the Array constructor,
// Create an AllocationSite if we don't already have it, store it in the // Create an AllocationSite if we don't already have it, store it in the
// slot. // slot.
{ CreateAllocationSiteStub create_stub(masm->isolate());
FrameScope scope(masm, StackFrame::INTERNAL); CallStubInRecordCallTarget(masm, &create_stub, argc, function,
CreateAllocationSiteStub create_stub(masm->isolate()); feedback_vector, index);
// Arguments register must be smi-tagged to call out.
__ SmiTag(argc);
__ Push(argc, function, feedback_vector, index);
// CreateAllocationSiteStub expect the feedback vector in x2 and the slot
// index in x3.
DCHECK(feedback_vector.Is(x2) && index.Is(x3));
__ CallStub(&create_stub);
__ Pop(index, feedback_vector, function, argc);
__ SmiUntag(argc);
}
__ B(&done); __ B(&done);
__ Bind(&not_array_function); __ Bind(&not_array_function);
} }
// An uninitialized cache is patched with the function. CreateWeakCellStub create_stub(masm->isolate());
CallStubInRecordCallTarget(masm, &create_stub, argc, function,
__ Add(scratch1, feedback_vector, feedback_vector, index);
Operand::UntagSmiAndScale(index, kPointerSizeLog2));
__ Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag);
__ Str(function, MemOperand(scratch1, 0));
__ Push(function);
__ RecordWrite(feedback_vector, scratch1, function, kLRHasNotBeenSaved,
kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
__ Pop(function);
__ Bind(&done); __ Bind(&done);
} }
@ -2976,7 +2985,7 @@ void CallConstructStub::Generate(MacroAssembler* masm) {
&slow); &slow);
if (RecordCallTarget()) { if (RecordCallTarget()) {
GenerateRecordCallTarget(masm, x0, function, x2, x3, x4, x5); GenerateRecordCallTarget(masm, x0, function, x2, x3, x4, x5, x11);
__ Add(x5, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2)); __ Add(x5, x2, Operand::UntagSmiAndScale(x3, kPointerSizeLog2));
if (FLAG_pretenuring_call_new) { if (FLAG_pretenuring_call_new) {

View File

@ -1913,6 +1913,30 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
} }
static void CallStubInRecordCallTarget(MacroAssembler* masm, CodeStub* stub) {
// eax : number of arguments to the construct function
// ebx : Feedback vector
// edx : slot in feedback vector (Smi)
// edi : the function to call
FrameScope scope(masm, StackFrame::INTERNAL);
// Arguments register must be smi-tagged to call out.
__ SmiTag(eax);
__ push(eax);
__ push(edi);
__ push(edx);
__ push(ebx);
__ CallStub(stub);
__ pop(ebx);
__ pop(edx);
__ pop(edi);
__ pop(eax);
__ SmiUntag(eax);
}
static void GenerateRecordCallTarget(MacroAssembler* masm) { static void GenerateRecordCallTarget(MacroAssembler* masm) {
// Cache the called function in a feedback vector slot. Cache states // Cache the called function in a feedback vector slot. Cache states
// are uninitialized, monomorphic (indicated by a JSFunction), and // are uninitialized, monomorphic (indicated by a JSFunction), and
@ -1930,18 +1954,26 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// A monomorphic cache hit or an already megamorphic state: invoke the // A monomorphic cache hit or an already megamorphic state: invoke the
// function without changing the state. // function without changing the state.
__ cmp(ecx, edi); Label check_allocation_site;
__ cmp(edi, FieldOperand(ecx, WeakCell::kValueOffset));
__ j(equal, &done, Label::kFar); __ j(equal, &done, Label::kFar);
__ cmp(ecx, Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); __ CompareRoot(ecx, Heap::kmegamorphic_symbolRootIndex);
__ j(equal, &done, Label::kFar); __ j(equal, &done, Label::kFar);
__ CompareRoot(FieldOperand(ecx, 0), Heap::kWeakCellMapRootIndex);
__ j(not_equal, FLAG_pretenuring_call_new ? &miss : &check_allocation_site);
// If edi is not equal to the weak cell value, and the weak cell value is
// cleared, we have a new chance to become monomorphic.
__ JumpIfSmi(FieldOperand(ecx, WeakCell::kValueOffset), &initialize);
__ jmp(&megamorphic);
if (!FLAG_pretenuring_call_new) { if (!FLAG_pretenuring_call_new) {
__ bind(&check_allocation_site);
// If we came here, we need to see if we are the array function. // If we came here, we need to see if we are the array function.
// If we didn't have a matching function, and we didn't find the megamorph // If we didn't have a matching function, and we didn't find the megamorph
// sentinel, then we have in the slot either some other function or an // sentinel, then we have in the slot either some other function or an
// AllocationSite. Do a map check on the object in ecx. // AllocationSite. Do a map check on the object in ecx.
Handle<Map> allocation_site_map = isolate->factory()->allocation_site_map(); __ CompareRoot(FieldOperand(ecx, 0), Heap::kAllocationSiteMapRootIndex);
__ cmp(FieldOperand(ecx, 0), Immediate(allocation_site_map));
__ j(not_equal, &miss); __ j(not_equal, &miss);
// Make sure the function is the Array() function // Make sure the function is the Array() function
@ -1955,7 +1987,7 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// A monomorphic miss (i.e, here the cache is not uninitialized) goes // A monomorphic miss (i.e, here the cache is not uninitialized) goes
// megamorphic. // megamorphic.
__ cmp(ecx, Immediate(TypeFeedbackVector::UninitializedSentinel(isolate))); __ CompareRoot(ecx, Heap::kuninitialized_symbolRootIndex);
__ j(equal, &initialize); __ j(equal, &initialize);
// MegamorphicSentinel is an immortal immovable object (undefined) so no // MegamorphicSentinel is an immortal immovable object (undefined) so no
// write-barrier is needed. // write-barrier is needed.
@ -1977,43 +2009,15 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// The target function is the Array constructor, // The target function is the Array constructor,
// Create an AllocationSite if we don't already have it, store it in the // Create an AllocationSite if we don't already have it, store it in the
// slot. // slot.
{ CreateAllocationSiteStub create_stub(isolate);
FrameScope scope(masm, StackFrame::INTERNAL); CallStubInRecordCallTarget(masm, &create_stub);
// Arguments register must be smi-tagged to call out.
__ SmiTag(eax);
__ push(eax);
__ push(edi);
__ push(edx);
__ push(ebx);
CreateAllocationSiteStub create_stub(isolate);
__ CallStub(&create_stub);
__ pop(ebx);
__ pop(edx);
__ pop(edi);
__ pop(eax);
__ SmiUntag(eax);
}
__ jmp(&done); __ jmp(&done);
__ bind(&not_array_function); __ bind(&not_array_function);
} }
__ mov(FieldOperand(ebx, edx, times_half_pointer_size, CreateWeakCellStub create_stub(isolate);
FixedArray::kHeaderSize), CallStubInRecordCallTarget(masm, &create_stub);
edi);
// We won't need edx or ebx anymore, just save edi
__ push(edi);
__ push(ebx);
__ push(edx);
__ RecordWriteArray(ebx, edi, edx, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
__ pop(edx);
__ pop(ebx);
__ pop(edi);
__ bind(&done); __ bind(&done);
} }

View File

@ -11124,7 +11124,7 @@ void SharedFunctionInfo::ClearTypeFeedbackInfo() {
void SharedFunctionInfo::ClearTypeFeedbackInfoAtGCTime() { void SharedFunctionInfo::ClearTypeFeedbackInfoAtGCTime() {
feedback_vector()->ClearSlots(this); feedback_vector()->ClearSlotsAtGCTime(this);
feedback_vector()->ClearICSlotsAtGCTime(this); feedback_vector()->ClearICSlotsAtGCTime(this);
} }

View File

@ -139,18 +139,21 @@ Handle<TypeFeedbackVector> TypeFeedbackVector::Copy(
// This logic is copied from // This logic is copied from
// StaticMarkingVisitor<StaticVisitor>::VisitCodeTarget. // StaticMarkingVisitor<StaticVisitor>::VisitCodeTarget.
static bool ClearLogic(Heap* heap, int ic_age) { static bool ClearLogic(Heap* heap) {
return FLAG_cleanup_code_caches_at_gc && return FLAG_cleanup_code_caches_at_gc &&
heap->isolate()->serializer_enabled(); heap->isolate()->serializer_enabled();
} }
void TypeFeedbackVector::ClearSlots(SharedFunctionInfo* shared) { void TypeFeedbackVector::ClearSlotsImpl(SharedFunctionInfo* shared,
bool force_clear) {
int slots = Slots(); int slots = Slots();
Isolate* isolate = GetIsolate(); Heap* heap = GetIsolate()->heap();
Object* uninitialized_sentinel =
TypeFeedbackVector::RawUninitializedSentinel(isolate->heap());
if (!force_clear && !ClearLogic(heap)) return;
Object* uninitialized_sentinel =
TypeFeedbackVector::RawUninitializedSentinel(heap);
for (int i = 0; i < slots; i++) { for (int i = 0; i < slots; i++) {
FeedbackVectorSlot slot(i); FeedbackVectorSlot slot(i);
Object* obj = Get(slot); Object* obj = Get(slot);
@ -172,10 +175,7 @@ void TypeFeedbackVector::ClearICSlotsImpl(SharedFunctionInfo* shared,
bool force_clear) { bool force_clear) {
Heap* heap = GetIsolate()->heap(); Heap* heap = GetIsolate()->heap();
// I'm not sure yet if this ic age is the correct one. if (!force_clear && !ClearLogic(heap)) return;
int ic_age = shared->ic_age();
if (!force_clear && !ClearLogic(heap, ic_age)) return;
int slots = ICSlots(); int slots = ICSlots();
Code* host = shared->code(); Code* host = shared->code();

View File

@ -195,7 +195,11 @@ class TypeFeedbackVector : public FixedArray {
Handle<TypeFeedbackVector> vector); Handle<TypeFeedbackVector> vector);
// Clears the vector slots and the vector ic slots. // Clears the vector slots and the vector ic slots.
void ClearSlots(SharedFunctionInfo* shared); void ClearSlots(SharedFunctionInfo* shared) { ClearSlotsImpl(shared, true); }
void ClearSlotsAtGCTime(SharedFunctionInfo* shared) {
ClearSlotsImpl(shared, false);
}
void ClearICSlots(SharedFunctionInfo* shared) { void ClearICSlots(SharedFunctionInfo* shared) {
ClearICSlotsImpl(shared, true); ClearICSlotsImpl(shared, true);
} }
@ -237,12 +241,28 @@ class TypeFeedbackVector : public FixedArray {
typedef BitSetComputer<VectorICKind, kVectorICKindBits, kSmiValueSize, typedef BitSetComputer<VectorICKind, kVectorICKindBits, kSmiValueSize,
uint32_t> VectorICComputer; uint32_t> VectorICComputer;
void ClearSlotsImpl(SharedFunctionInfo* shared, bool force_clear);
void ClearICSlotsImpl(SharedFunctionInfo* shared, bool force_clear); void ClearICSlotsImpl(SharedFunctionInfo* shared, bool force_clear);
DISALLOW_IMPLICIT_CONSTRUCTORS(TypeFeedbackVector); DISALLOW_IMPLICIT_CONSTRUCTORS(TypeFeedbackVector);
}; };
// The following asserts protect an optimization in type feedback vector
// code that looks into the contents of a slot assuming to find a String,
// a Symbol, an AllocationSite, a WeakCell, or a FixedArray.
STATIC_ASSERT(WeakCell::kSize >= 2 * kPointerSize);
STATIC_ASSERT(WeakCell::kValueOffset == AllocationSite::kTransitionInfoOffset);
STATIC_ASSERT(WeakCell::kValueOffset == FixedArray::kLengthOffset);
STATIC_ASSERT(WeakCell::kValueOffset == Name::kHashFieldSlot);
// Verify that an empty hash field looks like a tagged object, but can't
// possibly be confused with a pointer.
STATIC_ASSERT((Name::kEmptyHashField & kHeapObjectTag) == kHeapObjectTag);
STATIC_ASSERT(Name::kEmptyHashField == 0x3);
// Verify that a set hash field will not look like a tagged object.
STATIC_ASSERT(Name::kHashNotComputedMask == kHeapObjectTag);
// A FeedbackNexus is the combination of a TypeFeedbackVector and a slot. // A FeedbackNexus is the combination of a TypeFeedbackVector and a slot.
// Derived classes customize the update and retrieval of feedback. // Derived classes customize the update and retrieval of feedback.
class FeedbackNexus { class FeedbackNexus {

View File

@ -51,7 +51,19 @@ Handle<Object> TypeFeedbackOracle::GetInfo(TypeFeedbackId ast_id) {
Handle<Object> TypeFeedbackOracle::GetInfo(FeedbackVectorSlot slot) { Handle<Object> TypeFeedbackOracle::GetInfo(FeedbackVectorSlot slot) {
DCHECK(slot.ToInt() >= 0 && slot.ToInt() < feedback_vector_->length()); DCHECK(slot.ToInt() >= 0 && slot.ToInt() < feedback_vector_->length());
Handle<Object> undefined =
Handle<Object>::cast(isolate()->factory()->undefined_value());
Object* obj = feedback_vector_->Get(slot); Object* obj = feedback_vector_->Get(slot);
// Slots do not embed direct pointers to functions. Instead a WeakCell is
// always used.
DCHECK(!obj->IsJSFunction());
if (obj->IsWeakCell()) {
WeakCell* cell = WeakCell::cast(obj);
if (cell->cleared()) return undefined;
obj = cell->value();
}
return Handle<Object>(obj, isolate()); return Handle<Object>(obj, isolate());
} }

View File

@ -1778,6 +1778,31 @@ void CompareICStub::GenerateGeneric(MacroAssembler* masm) {
} }
static void CallStubInRecordCallTarget(MacroAssembler* masm, CodeStub* stub) {
// eax : number of arguments to the construct function
// ebx : Feedback vector
// edx : slot in feedback vector (Smi)
// edi : the function to call
FrameScope scope(masm, StackFrame::INTERNAL);
// Arguments register must be smi-tagged to call out.
__ Integer32ToSmi(rax, rax);
__ Push(rax);
__ Push(rdi);
__ Integer32ToSmi(rdx, rdx);
__ Push(rdx);
__ Push(rbx);
__ CallStub(stub);
__ Pop(rbx);
__ Pop(rdx);
__ Pop(rdi);
__ Pop(rax);
__ SmiToInteger32(rax, rax);
}
static void GenerateRecordCallTarget(MacroAssembler* masm) { static void GenerateRecordCallTarget(MacroAssembler* masm) {
// Cache the called function in a feedback vector slot. Cache states // Cache the called function in a feedback vector slot. Cache states
// are uninitialized, monomorphic (indicated by a JSFunction), and // are uninitialized, monomorphic (indicated by a JSFunction), and
@ -1797,19 +1822,28 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// A monomorphic cache hit or an already megamorphic state: invoke the // A monomorphic cache hit or an already megamorphic state: invoke the
// function without changing the state. // function without changing the state.
__ cmpp(rcx, rdi); Label check_allocation_site;
__ j(equal, &done); __ cmpp(rdi, FieldOperand(rcx, WeakCell::kValueOffset));
__ Cmp(rcx, TypeFeedbackVector::MegamorphicSentinel(isolate)); __ j(equal, &done, Label::kFar);
__ j(equal, &done); __ CompareRoot(rcx, Heap::kmegamorphic_symbolRootIndex);
__ j(equal, &done, Label::kFar);
__ CompareRoot(FieldOperand(rcx, 0), Heap::kWeakCellMapRootIndex);
__ j(not_equal, FLAG_pretenuring_call_new ? &miss : &check_allocation_site);
// If edi is not equal to the weak cell value, and the weak cell value is
// cleared, we have a new chance to become monomorphic. Otherwise, we
// need to go megamorphic.
__ CheckSmi(FieldOperand(rcx, WeakCell::kValueOffset));
__ j(equal, &initialize);
__ jmp(&megamorphic);
if (!FLAG_pretenuring_call_new) { if (!FLAG_pretenuring_call_new) {
__ bind(&check_allocation_site);
// If we came here, we need to see if we are the array function. // If we came here, we need to see if we are the array function.
// If we didn't have a matching function, and we didn't find the megamorph // If we didn't have a matching function, and we didn't find the megamorph
// sentinel, then we have in the slot either some other function or an // sentinel, then we have in the slot either some other function or an
// AllocationSite. Do a map check on the object in rcx. // AllocationSite. Do a map check on the object in rcx.
Handle<Map> allocation_site_map = __ CompareRoot(FieldOperand(rcx, 0), Heap::kAllocationSiteMapRootIndex);
masm->isolate()->factory()->allocation_site_map();
__ Cmp(FieldOperand(rcx, 0), allocation_site_map);
__ j(not_equal, &miss); __ j(not_equal, &miss);
// Make sure the function is the Array() function // Make sure the function is the Array() function
@ -1823,7 +1857,7 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
// A monomorphic miss (i.e, here the cache is not uninitialized) goes // A monomorphic miss (i.e, here the cache is not uninitialized) goes
// megamorphic. // megamorphic.
__ Cmp(rcx, TypeFeedbackVector::UninitializedSentinel(isolate)); __ CompareRoot(rcx, Heap::kuninitialized_symbolRootIndex);
__ j(equal, &initialize); __ j(equal, &initialize);
// MegamorphicSentinel is an immortal immovable object (undefined) so no // MegamorphicSentinel is an immortal immovable object (undefined) so no
// write-barrier is needed. // write-barrier is needed.
@ -1842,43 +1876,16 @@ static void GenerateRecordCallTarget(MacroAssembler* masm) {
__ cmpp(rdi, rcx); __ cmpp(rdi, rcx);
__ j(not_equal, &not_array_function); __ j(not_equal, &not_array_function);
{ CreateAllocationSiteStub create_stub(isolate);
FrameScope scope(masm, StackFrame::INTERNAL); CallStubInRecordCallTarget(masm, &create_stub);
// Arguments register must be smi-tagged to call out.
__ Integer32ToSmi(rax, rax);
__ Push(rax);
__ Push(rdi);
__ Integer32ToSmi(rdx, rdx);
__ Push(rdx);
__ Push(rbx);
CreateAllocationSiteStub create_stub(isolate);
__ CallStub(&create_stub);
__ Pop(rbx);
__ Pop(rdx);
__ Pop(rdi);
__ Pop(rax);
__ SmiToInteger32(rax, rax);
}
__ jmp(&done_no_smi_convert); __ jmp(&done_no_smi_convert);
__ bind(&not_array_function); __ bind(&not_array_function);
} }
__ movp(FieldOperand(rbx, rdx, times_pointer_size, FixedArray::kHeaderSize), CreateWeakCellStub create_stub(isolate);
rdi); CallStubInRecordCallTarget(masm, &create_stub);
__ jmp(&done_no_smi_convert);
// We won't need rdx or rbx anymore, just save rdi
__ Push(rdi);
__ Push(rbx);
__ Push(rdx);
__ RecordWriteArray(rbx, rdi, rdx, kDontSaveFPRegs,
EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
__ Pop(rdx);
__ Pop(rbx);
__ Pop(rdi);
__ bind(&done); __ bind(&done);
__ Integer32ToSmi(rdx, rdx); __ Integer32ToSmi(rdx, rdx);

View File

@ -140,13 +140,19 @@ TEST(VectorSlotClearing) {
// Fill with information // Fill with information
vector->Set(FeedbackVectorSlot(0), Smi::FromInt(1)); vector->Set(FeedbackVectorSlot(0), Smi::FromInt(1));
vector->Set(FeedbackVectorSlot(1), *factory->fixed_array_map()); Handle<WeakCell> cell = factory->NewWeakCell(factory->fixed_array_map());
vector->Set(FeedbackVectorSlot(1), *cell);
Handle<AllocationSite> site = factory->NewAllocationSite(); Handle<AllocationSite> site = factory->NewAllocationSite();
vector->Set(FeedbackVectorSlot(2), *site); vector->Set(FeedbackVectorSlot(2), *site);
// GC time clearing leaves slots alone.
vector->ClearSlotsAtGCTime(NULL);
Object* obj = vector->Get(FeedbackVectorSlot(1));
CHECK(obj->IsWeakCell() && !WeakCell::cast(obj)->cleared());
vector->ClearSlots(NULL); vector->ClearSlots(NULL);
// The feedback vector slots are cleared. AllocationSites are granted // The feedback vector slots are cleared. AllocationSites are still granted
// an exemption from clearing, as are smis. // an exemption from clearing, as are smis.
CHECK_EQ(Smi::FromInt(1), vector->Get(FeedbackVectorSlot(0))); CHECK_EQ(Smi::FromInt(1), vector->Get(FeedbackVectorSlot(0)));
CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate), CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),

View File

@ -3387,6 +3387,66 @@ static void CheckVectorICCleared(Handle<JSFunction> f, int ic_slot_index) {
} }
TEST(IncrementalMarkingPreservesMonomorphicConstructor) {
if (i::FLAG_always_opt) return;
CcTest::InitializeVM();
v8::HandleScope scope(CcTest::isolate());
// Prepare function f that contains a monomorphic IC for object
// originating from the same native context.
CompileRun(
"function fun() { this.x = 1; };"
"function f(o) { return new o(); } f(fun); f(fun);");
Handle<JSFunction> f = v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
Handle<TypeFeedbackVector> vector(f->shared()->feedback_vector());
CHECK(vector->Get(FeedbackVectorSlot(0))->IsWeakCell());
SimulateIncrementalMarking(CcTest::heap());
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
CHECK(vector->Get(FeedbackVectorSlot(0))->IsWeakCell());
}
TEST(IncrementalMarkingClearsMonomorphicConstructor) {
if (i::FLAG_always_opt) return;
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::Value> fun1;
{
LocalContext env;
CompileRun("function fun() { this.x = 1; };");
fun1 = env->Global()->Get(v8_str("fun"));
}
// Prepare function f that contains a monomorphic constructor for object
// originating from a different native context.
CcTest::global()->Set(v8_str("fun1"), fun1);
CompileRun(
"function fun() { this.x = 1; };"
"function f(o) { return new o(); } f(fun1); f(fun1);");
Handle<JSFunction> f = v8::Utils::OpenHandle(
*v8::Handle<v8::Function>::Cast(CcTest::global()->Get(v8_str("f"))));
Handle<TypeFeedbackVector> vector(f->shared()->feedback_vector());
CHECK(vector->Get(FeedbackVectorSlot(0))->IsWeakCell());
// Fire context dispose notification.
CcTest::isolate()->ContextDisposedNotification();
SimulateIncrementalMarking(CcTest::heap());
CcTest::heap()->CollectAllGarbage(Heap::kNoGCFlags);
CHECK_EQ(*TypeFeedbackVector::UninitializedSentinel(isolate),
vector->Get(FeedbackVectorSlot(0)));
}
TEST(IncrementalMarkingPreservesMonomorphicIC) { TEST(IncrementalMarkingPreservesMonomorphicIC) {
if (i::FLAG_always_opt) return; if (i::FLAG_always_opt) return;
CcTest::InitializeVM(); CcTest::InitializeVM();
@ -4415,6 +4475,60 @@ static void ClearWeakIC(const v8::WeakCallbackData<v8::Object, void>& data) {
} }
TEST(WeakFunctionInConstructor) {
if (i::FLAG_always_opt) return;
i::FLAG_stress_compaction = false;
CcTest::InitializeVM();
v8::Isolate* isolate = CcTest::isolate();
v8::HandleScope scope(isolate);
CompileRun(
"function createObj(obj) {"
" return new obj();"
"}");
Handle<JSFunction> createObj =
v8::Utils::OpenHandle(*v8::Handle<v8::Function>::Cast(
CcTest::global()->Get(v8_str("createObj"))));
v8::Persistent<v8::Object> garbage;
{
v8::HandleScope scope(isolate);
const char* source =
" (function() {"
" function hat() { this.x = 5; }"
" createObj(hat);"
" createObj(hat);"
" return hat;"
" })();";
garbage.Reset(isolate, CompileRun(source)->ToObject(isolate));
}
weak_ic_cleared = false;
garbage.SetWeak(static_cast<void*>(&garbage), &ClearWeakIC);
Heap* heap = CcTest::i_isolate()->heap();
heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
CHECK(weak_ic_cleared);
// We've determined the constructor in createObj has had it's weak cell
// cleared. Now, verify that one additional call with a new function
// allows monomorphicity.
Handle<TypeFeedbackVector> feedback_vector = Handle<TypeFeedbackVector>(
createObj->shared()->feedback_vector(), CcTest::i_isolate());
for (int i = 0; i < 20; i++) {
Object* slot_value = feedback_vector->Get(FeedbackVectorSlot(0));
CHECK(slot_value->IsWeakCell());
if (WeakCell::cast(slot_value)->cleared()) break;
heap->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask);
}
Object* slot_value = feedback_vector->Get(FeedbackVectorSlot(0));
CHECK(slot_value->IsWeakCell() && WeakCell::cast(slot_value)->cleared());
CompileRun(
"function coat() { this.x = 6; }"
"createObj(coat);");
slot_value = feedback_vector->Get(FeedbackVectorSlot(0));
CHECK(slot_value->IsWeakCell() && !WeakCell::cast(slot_value)->cleared());
}
// Checks that the value returned by execution of the source is weak. // Checks that the value returned by execution of the source is weak.
void CheckWeakness(const char* source) { void CheckWeakness(const char* source) {
i::FLAG_stress_compaction = false; i::FLAG_stress_compaction = false;