From 875dbe6584da1cfb2955cc8d52fdea030df69345 Mon Sep 17 00:00:00 2001 From: "kasperl@chromium.org" Date: Fri, 10 Jul 2009 09:40:47 +0000 Subject: [PATCH] Re-enable ICs for loads and calls that skips a global object during lookup through the prototype chain. Review URL: http://codereview.chromium.org/155344 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2425 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/stub-cache-arm.cc | 289 ++++++++++++++++++++++------------------ src/ia32/stub-cache-ia32.cc | 315 +++++++++++++++++++++++++------------------- src/objects-inl.h | 2 +- src/objects.cc | 62 +++++---- src/objects.h | 3 + src/stub-cache.cc | 6 +- src/stub-cache.h | 88 ++++++++----- src/x64/stub-cache-x64.cc | 3 +- 8 files changed, 449 insertions(+), 319 deletions(-) diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 5c7c939..b10e560 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -171,110 +171,6 @@ void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, } -void StubCompiler::GenerateLoadField(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - int index, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ tst(receiver, Operand(kSmiTagMask)); - __ b(eq, miss_label); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - GenerateFastPropertyLoad(masm, r0, reg, holder, index); - __ Ret(); -} - - -void StubCompiler::GenerateLoadConstant(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - Object* value, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ tst(receiver, Operand(kSmiTagMask)); - __ b(eq, miss_label); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Return the constant value. - __ mov(r0, Operand(Handle(value))); - __ Ret(); -} - - -void StubCompiler::GenerateLoadCallback(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register name, - Register scratch1, - Register scratch2, - AccessorInfo* callback, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ tst(receiver, Operand(kSmiTagMask)); - __ b(eq, miss_label); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Push the arguments on the JS stack of the caller. - __ push(receiver); // receiver - __ mov(ip, Operand(Handle(callback))); // callback data - __ push(ip); - __ push(name); // name - __ push(reg); // holder - - // Do tail-call to the runtime system. - ExternalReference load_callback_property = - ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); - __ TailCallRuntime(load_callback_property, 4); -} - - -void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Smi* lookup_hint, - Register receiver, - Register name, - Register scratch1, - Register scratch2, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ tst(receiver, Operand(kSmiTagMask)); - __ b(eq, miss_label); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Push the arguments on the JS stack of the caller. - __ push(receiver); // receiver - __ push(reg); // holder - __ push(name); // name - __ mov(scratch1, Operand(lookup_hint)); - __ push(scratch1); - - // Do tail-call to the runtime system. - ExternalReference load_ic_property = - ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); - __ TailCallRuntime(load_ic_property, 4); -} - - void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm, Register receiver, Register scratch, @@ -462,6 +358,147 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { #define __ ACCESS_MASM(masm()) +Register StubCompiler::CheckPrototypes(JSObject* object, + Register object_reg, + JSObject* holder, + Register holder_reg, + Register scratch, + String* name, + Label* miss) { + // Check that the maps haven't changed. + Register result = + masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); + + // If we've skipped any global objects, it's not enough to verify + // that their maps haven't changed. + while (object != holder) { + if (object->IsGlobalObject()) { + GlobalObject* global = GlobalObject::cast(object); + Object* probe = global->EnsurePropertyCell(name); + if (probe->IsFailure()) { + set_failure(Failure::cast(probe)); + return result; + } + JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); + ASSERT(cell->value()->IsTheHole()); + __ mov(scratch, Operand(Handle(cell))); + __ ldr(scratch, + FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset)); + __ cmp(scratch, Operand(Factory::the_hole_value())); + __ b(ne, miss); + } + object = JSObject::cast(object->GetPrototype()); + } + + // Return the register containin the holder. + return result; +} + + +void StubCompiler::GenerateLoadField(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + int index, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ tst(receiver, Operand(kSmiTagMask)); + __ b(eq, miss); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); + GenerateFastPropertyLoad(masm(), r0, reg, holder, index); + __ Ret(); +} + + +void StubCompiler::GenerateLoadConstant(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + Object* value, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ tst(receiver, Operand(kSmiTagMask)); + __ b(eq, miss); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); + + // Return the constant value. + __ mov(r0, Operand(Handle(value))); + __ Ret(); +} + + +void StubCompiler::GenerateLoadCallback(JSObject* object, + JSObject* holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + AccessorInfo* callback, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ tst(receiver, Operand(kSmiTagMask)); + __ b(eq, miss); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); + + // Push the arguments on the JS stack of the caller. + __ push(receiver); // receiver + __ mov(ip, Operand(Handle(callback))); // callback data + __ push(ip); + __ push(name_reg); // name + __ push(reg); // holder + + // Do tail-call to the runtime system. + ExternalReference load_callback_property = + ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); + __ TailCallRuntime(load_callback_property, 4); +} + + +void StubCompiler::GenerateLoadInterceptor(JSObject* object, + JSObject* holder, + Smi* lookup_hint, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ tst(receiver, Operand(kSmiTagMask)); + __ b(eq, miss); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss); + + // Push the arguments on the JS stack of the caller. + __ push(receiver); // receiver + __ push(reg); // holder + __ push(name_reg); // name + __ mov(scratch1, Operand(lookup_hint)); + __ push(scratch1); + + // Do tail-call to the runtime system. + ExternalReference load_ic_property = + ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); + __ TailCallRuntime(load_ic_property, 4); +} + + Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { // ----------- S t a t e ------------- // -- r1: function @@ -513,7 +550,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, // Do the right check and compute the holder register. Register reg = - masm()->CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss); + CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss); GenerateFastPropertyLoad(masm(), r1, reg, holder, index); // Check that the function really is a function. @@ -546,6 +583,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, Object* CallStubCompiler::CompileCallConstant(Object* object, JSObject* holder, JSFunction* function, + String* name, CheckType check) { // ----------- S t a t e ------------- // -- lr: return address @@ -569,7 +607,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, switch (check) { case RECEIVER_MAP_CHECK: // Check that the maps haven't changed. - __ CheckMaps(JSObject::cast(object), r1, holder, r3, r2, &miss); + CheckPrototypes(JSObject::cast(object), r1, holder, r3, r2, name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -587,8 +625,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, GenerateLoadGlobalFunctionPrototype(masm(), Context::STRING_FUNCTION_INDEX, r2); - __ CheckMaps(JSObject::cast(object->GetPrototype()), - r2, holder, r3, r1, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3, + r1, name, &miss); break; case NUMBER_CHECK: { @@ -603,8 +641,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, GenerateLoadGlobalFunctionPrototype(masm(), Context::NUMBER_FUNCTION_INDEX, r2); - __ CheckMaps(JSObject::cast(object->GetPrototype()), - r2, holder, r3, r1, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3, + r1, name, &miss); break; } @@ -620,13 +658,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, GenerateLoadGlobalFunctionPrototype(masm(), Context::BOOLEAN_FUNCTION_INDEX, r2); - __ CheckMaps(JSObject::cast(object->GetPrototype()), - r2, holder, r3, r1, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3, + r1, name, &miss); break; } case JSARRAY_HAS_FAST_ELEMENTS_CHECK: - __ CheckMaps(JSObject::cast(object), r1, holder, r3, r2, &miss); + CheckPrototypes(JSObject::cast(object), r1, holder, r3, r2, name, &miss); // Make sure object->elements()->map() != Heap::hash_table_map() // Get the elements array of the object. __ ldr(r3, FieldMemOperand(r1, JSObject::kElementsOffset)); @@ -712,7 +750,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, } // Check that the maps haven't changed. - masm()->CheckMaps(object, r0, holder, r3, r2, &miss); + CheckPrototypes(object, r0, holder, r3, r2, name, &miss); // Get the value from the cell. __ mov(r3, Operand(Handle(cell))); @@ -941,7 +979,7 @@ Object* LoadStubCompiler::CompileLoadField(JSObject* object, __ ldr(r0, MemOperand(sp, 0)); - GenerateLoadField(masm(), object, holder, r0, r3, r1, index, &miss); + GenerateLoadField(object, holder, r0, r3, r1, index, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -962,7 +1000,7 @@ Object* LoadStubCompiler::CompileLoadCallback(JSObject* object, Label miss; __ ldr(r0, MemOperand(sp, 0)); - GenerateLoadCallback(masm(), object, holder, r0, r2, r3, r1, callback, &miss); + GenerateLoadCallback(object, holder, r0, r2, r3, r1, callback, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -984,7 +1022,7 @@ Object* LoadStubCompiler::CompileLoadConstant(JSObject* object, __ ldr(r0, MemOperand(sp, 0)); - GenerateLoadConstant(masm(), object, holder, r0, r3, r1, value, &miss); + GenerateLoadConstant(object, holder, r0, r3, r1, value, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1005,14 +1043,14 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* object, __ ldr(r0, MemOperand(sp, 0)); - GenerateLoadInterceptor(masm(), - object, + GenerateLoadInterceptor(object, holder, holder->InterceptorPropertyLookupHint(name), r0, r2, r3, r1, + name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1048,7 +1086,7 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object, } // Check that the map of the global has not changed. - masm()->CheckMaps(object, r1, holder, r3, r0, &miss); + CheckPrototypes(object, r1, holder, r3, r0, name, &miss); // Get the value from the cell. __ mov(r3, Operand(Handle(cell))); @@ -1091,7 +1129,7 @@ Object* KeyedLoadStubCompiler::CompileLoadField(String* name, __ cmp(r2, Operand(Handle(name))); __ b(ne, &miss); - GenerateLoadField(masm(), receiver, holder, r0, r3, r1, index, &miss); + GenerateLoadField(receiver, holder, r0, r3, r1, index, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1116,8 +1154,7 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name, __ cmp(r2, Operand(Handle(name))); __ b(ne, &miss); - GenerateLoadCallback(masm(), receiver, holder, r0, r2, r3, - r1, callback, &miss); + GenerateLoadCallback(receiver, holder, r0, r2, r3, r1, callback, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1143,7 +1180,7 @@ Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name, __ cmp(r2, Operand(Handle(name))); __ b(ne, &miss); - GenerateLoadConstant(masm(), receiver, holder, r0, r3, r1, value, &miss); + GenerateLoadConstant(receiver, holder, r0, r3, r1, value, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1169,14 +1206,14 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, __ cmp(r2, Operand(Handle(name))); __ b(ne, &miss); - GenerateLoadInterceptor(masm(), - receiver, + GenerateLoadInterceptor(receiver, holder, Smi::FromInt(JSObject::kLookupInHolder), r0, r2, r3, r1, + name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 37ee9c4..0a887d5 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -273,114 +273,6 @@ void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, } -void StubCompiler::GenerateLoadField(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - int index, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ test(receiver, Immediate(kSmiTagMask)); - __ j(zero, miss_label, not_taken); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Get the value from the properties. - GenerateFastPropertyLoad(masm, eax, reg, holder, index); - __ ret(0); -} - - -void StubCompiler::GenerateLoadCallback(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register name, - Register scratch1, - Register scratch2, - AccessorInfo* callback, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ test(receiver, Immediate(kSmiTagMask)); - __ j(zero, miss_label, not_taken); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Push the arguments on the JS stack of the caller. - __ pop(scratch2); // remove return address - __ push(receiver); // receiver - __ push(Immediate(Handle(callback))); // callback data - __ push(name); // name - __ push(reg); // holder - __ push(scratch2); // restore return address - - // Do tail-call to the runtime system. - ExternalReference load_callback_property = - ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); - __ TailCallRuntime(load_callback_property, 4); -} - - -void StubCompiler::GenerateLoadConstant(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - Object* value, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ test(receiver, Immediate(kSmiTagMask)); - __ j(zero, miss_label, not_taken); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Return the constant value. - __ mov(eax, Handle(value)); - __ ret(0); -} - - -void StubCompiler::GenerateLoadInterceptor(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Smi* lookup_hint, - Register receiver, - Register name, - Register scratch1, - Register scratch2, - Label* miss_label) { - // Check that the receiver isn't a smi. - __ test(receiver, Immediate(kSmiTagMask)); - __ j(zero, miss_label, not_taken); - - // Check that the maps haven't changed. - Register reg = - masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label); - - // Push the arguments on the JS stack of the caller. - __ pop(scratch2); // remove return address - __ push(receiver); // receiver - __ push(reg); // holder - __ push(name); // name - // TODO(367): Maybe don't push lookup_hint for LOOKUP_IN_HOLDER and/or - // LOOKUP_IN_PROTOTYPE, but use a special version of lookup method? - __ push(Immediate(lookup_hint)); - __ push(scratch2); // restore return address - - // Do tail-call to the runtime system. - ExternalReference load_ic_property = - ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); - __ TailCallRuntime(load_ic_property, 4); -} void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { @@ -474,10 +366,159 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm, #undef __ - #define __ ACCESS_MASM(masm()) +Register StubCompiler::CheckPrototypes(JSObject* object, + Register object_reg, + JSObject* holder, + Register holder_reg, + Register scratch, + String* name, + Label* miss) { + // Check that the maps haven't changed. + Register result = + masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); + + // If we've skipped any global objects, it's not enough to verify + // that their maps haven't changed. + while (object != holder) { + if (object->IsGlobalObject()) { + GlobalObject* global = GlobalObject::cast(object); + Object* probe = global->EnsurePropertyCell(name); + if (probe->IsFailure()) { + set_failure(Failure::cast(probe)); + return result; + } + JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); + ASSERT(cell->value()->IsTheHole()); + __ mov(scratch, Immediate(Handle(cell))); + __ cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), + Immediate(Factory::the_hole_value())); + __ j(not_equal, miss, not_taken); + } + object = JSObject::cast(object->GetPrototype()); + } + + // Return the register containin the holder. + return result; +} + + +void StubCompiler::GenerateLoadField(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + int index, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ test(receiver, Immediate(kSmiTagMask)); + __ j(zero, miss, not_taken); + + // Check the prototype chain. + Register reg = + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, name, miss); + + // Get the value from the properties. + GenerateFastPropertyLoad(masm(), eax, reg, holder, index); + __ ret(0); +} + + +void StubCompiler::GenerateLoadCallback(JSObject* object, + JSObject* holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + AccessorInfo* callback, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ test(receiver, Immediate(kSmiTagMask)); + __ j(zero, miss, not_taken); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, name, miss); + + // Push the arguments on the JS stack of the caller. + __ pop(scratch2); // remove return address + __ push(receiver); // receiver + __ push(Immediate(Handle(callback))); // callback data + __ push(name_reg); // name + __ push(reg); // holder + __ push(scratch2); // restore return address + + // Do tail-call to the runtime system. + ExternalReference load_callback_property = + ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); + __ TailCallRuntime(load_callback_property, 4); +} + + +void StubCompiler::GenerateLoadConstant(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + Object* value, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ test(receiver, Immediate(kSmiTagMask)); + __ j(zero, miss, not_taken); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, name, miss); + + // Return the constant value. + __ mov(eax, Handle(value)); + __ ret(0); +} + + +void StubCompiler::GenerateLoadInterceptor(JSObject* object, + JSObject* holder, + Smi* lookup_hint, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ test(receiver, Immediate(kSmiTagMask)); + __ j(zero, miss, not_taken); + + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, name, miss); + + // Push the arguments on the JS stack of the caller. + __ pop(scratch2); // remove return address + __ push(receiver); // receiver + __ push(reg); // holder + __ push(name_reg); // name + // TODO(367): Maybe don't push lookup_hint for LOOKUP_IN_HOLDER and/or + // LOOKUP_IN_PROTOTYPE, but use a special version of lookup method? + __ push(Immediate(lookup_hint)); + __ push(scratch2); // restore return address + + // Do tail-call to the runtime system. + ExternalReference load_ic_property = + ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); + __ TailCallRuntime(load_ic_property, 4); +} + + // TODO(1241006): Avoid having lazy compile stubs specialized by the // number of arguments. It is not needed anymore. Object* StubCompiler::CompileLazyCompile(Code::Flags flags) { @@ -520,7 +561,8 @@ Object* CallStubCompiler::CompileCallField(Object* object, // Do the right check and compute the holder register. Register reg = - masm()->CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss); + CheckPrototypes(JSObject::cast(object), edx, holder, + ebx, ecx, name, &miss); GenerateFastPropertyLoad(masm(), edi, reg, holder, index); @@ -553,6 +595,7 @@ Object* CallStubCompiler::CompileCallField(Object* object, Object* CallStubCompiler::CompileCallConstant(Object* object, JSObject* holder, JSFunction* function, + String* name, CheckType check) { // ----------- S t a t e ------------- // ----------------------------------- @@ -575,7 +618,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, switch (check) { case RECEIVER_MAP_CHECK: // Check that the maps haven't changed. - __ CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss); + CheckPrototypes(JSObject::cast(object), edx, holder, + ebx, ecx, name, &miss); // Patch the receiver on the stack with the global proxy if // necessary. @@ -595,8 +639,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, GenerateLoadGlobalFunctionPrototype(masm(), Context::STRING_FUNCTION_INDEX, ecx); - __ CheckMaps(JSObject::cast(object->GetPrototype()), - ecx, holder, ebx, edx, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), ecx, holder, + ebx, edx, name, &miss); break; case NUMBER_CHECK: { @@ -611,8 +655,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, GenerateLoadGlobalFunctionPrototype(masm(), Context::NUMBER_FUNCTION_INDEX, ecx); - __ CheckMaps(JSObject::cast(object->GetPrototype()), - ecx, holder, ebx, edx, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), ecx, holder, + ebx, edx, name, &miss); break; } @@ -628,13 +672,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object, GenerateLoadGlobalFunctionPrototype(masm(), Context::BOOLEAN_FUNCTION_INDEX, ecx); - __ CheckMaps(JSObject::cast(object->GetPrototype()), - ecx, holder, ebx, edx, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), ecx, holder, + ebx, edx, name, &miss); break; } case JSARRAY_HAS_FAST_ELEMENTS_CHECK: - __ CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss); + CheckPrototypes(JSObject::cast(object), edx, holder, + ebx, ecx, name, &miss); // Make sure object->elements()->map() != Heap::dictionary_array_map() // Get the elements array of the object. __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset)); @@ -692,7 +737,8 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object, // Check that maps have not changed and compute the holder register. Register reg = - masm()->CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss); + CheckPrototypes(JSObject::cast(object), edx, holder, + ebx, ecx, name, &miss); // Enter an internal frame. __ EnterInternalFrame(); @@ -771,7 +817,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object, } // Check that the maps haven't changed. - masm()->CheckMaps(object, edx, holder, ebx, ecx, &miss); + CheckPrototypes(object, edx, holder, ebx, ecx, name, &miss); // Get the value from the cell. __ mov(edi, Immediate(Handle(cell))); @@ -1033,6 +1079,7 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, } + Object* LoadStubCompiler::CompileLoadField(JSObject* object, JSObject* holder, int index, @@ -1045,7 +1092,7 @@ Object* LoadStubCompiler::CompileLoadField(JSObject* object, Label miss; __ mov(eax, (Operand(esp, kPointerSize))); - GenerateLoadField(masm(), object, holder, eax, ebx, edx, index, &miss); + GenerateLoadField(object, holder, eax, ebx, edx, index, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1066,8 +1113,8 @@ Object* LoadStubCompiler::CompileLoadCallback(JSObject* object, Label miss; __ mov(eax, (Operand(esp, kPointerSize))); - GenerateLoadCallback(masm(), object, holder, eax, ecx, ebx, - edx, callback, &miss); + GenerateLoadCallback(object, holder, eax, ecx, ebx, edx, + callback, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1088,7 +1135,7 @@ Object* LoadStubCompiler::CompileLoadConstant(JSObject* object, Label miss; __ mov(eax, (Operand(esp, kPointerSize))); - GenerateLoadConstant(masm(), object, holder, eax, ebx, edx, value, &miss); + GenerateLoadConstant(object, holder, eax, ebx, edx, value, name, &miss); __ bind(&miss); GenerateLoadMiss(masm(), Code::LOAD_IC); @@ -1110,14 +1157,14 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, __ mov(eax, (Operand(esp, kPointerSize))); // TODO(368): Compile in the whole chain: all the interceptors in // prototypes and ultimate answer. - GenerateLoadInterceptor(masm(), - receiver, + GenerateLoadInterceptor(receiver, holder, holder->InterceptorPropertyLookupHint(name), eax, ecx, edx, ebx, + name, &miss); __ bind(&miss); @@ -1154,7 +1201,7 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object, } // Check that the maps haven't changed. - masm()->CheckMaps(object, eax, holder, ebx, edx, &miss); + CheckPrototypes(object, eax, holder, ebx, edx, name, &miss); // Get the value from the cell. __ mov(eax, Immediate(Handle(cell))); @@ -1200,7 +1247,8 @@ Object* KeyedLoadStubCompiler::CompileLoadField(String* name, __ cmp(Operand(eax), Immediate(Handle(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadField(masm(), receiver, holder, ecx, ebx, edx, index, &miss); + GenerateLoadField(receiver, holder, ecx, ebx, edx, index, name, &miss); + __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_field, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1229,8 +1277,8 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name, __ cmp(Operand(eax), Immediate(Handle(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadCallback(masm(), receiver, holder, ecx, eax, ebx, edx, - callback, &miss); + GenerateLoadCallback(receiver, holder, ecx, eax, ebx, edx, + callback, name, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_callback, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1259,7 +1307,8 @@ Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name, __ cmp(Operand(eax), Immediate(Handle(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadConstant(masm(), receiver, holder, ecx, ebx, edx, value, &miss); + GenerateLoadConstant(receiver, holder, ecx, ebx, edx, + value, name, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_constant_function, 1); GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); @@ -1287,14 +1336,14 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, __ cmp(Operand(eax), Immediate(Handle(name))); __ j(not_equal, &miss, not_taken); - GenerateLoadInterceptor(masm(), - receiver, + GenerateLoadInterceptor(receiver, holder, Smi::FromInt(JSObject::kLookupInHolder), ecx, eax, edx, ebx, + name, &miss); __ bind(&miss); __ DecrementCounter(&Counters::keyed_load_interceptor, 1); diff --git a/src/objects-inl.h b/src/objects-inl.h index e0e9634..8e65638 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2653,7 +2653,7 @@ void Dictionary::SetEntry(int entry, Object* key, Object* value, PropertyDetails details) { - ASSERT(!key->IsString() || details.index() > 0); + ASSERT(!key->IsString() || details.IsDeleted() || details.index() > 0); int index = HashTable::EntryToIndex(entry); WriteBarrierMode mode = FixedArray::GetWriteBarrierMode(); FixedArray::set(index, key, mode); diff --git a/src/objects.cc b/src/objects.cc index 93e7495..0619564 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -436,8 +436,7 @@ Object* JSObject::SetNormalizedProperty(String* name, store_value = Heap::AllocateJSGlobalPropertyCell(value); if (store_value->IsFailure()) return store_value; } - Object* dict = - property_dictionary()->Add(name, store_value, details); + Object* dict = property_dictionary()->Add(name, store_value, details); if (dict->IsFailure()) return dict; set_properties(StringDictionary::cast(dict)); return value; @@ -1712,30 +1711,24 @@ void JSObject::LocalLookupRealNamedProperty(String* name, } else { int entry = property_dictionary()->FindEntry(name); if (entry != StringDictionary::kNotFound) { - // Make sure to disallow caching for uninitialized constants - // found in the dictionary-mode objects. Object* value = property_dictionary()->ValueAt(entry); if (IsGlobalObject()) { PropertyDetails d = property_dictionary()->DetailsAt(entry); if (d.IsDeleted()) { - // We've skipped a global object during lookup, so we cannot - // use inline caching because the map of the global object - // doesn't change if the property should be re-added. - result->DisallowCaching(); result->NotFound(); return; } value = JSGlobalPropertyCell::cast(value)->value(); ASSERT(result->IsLoaded()); } - if (value->IsTheHole()) { - result->DisallowCaching(); - } + // Make sure to disallow caching for uninitialized constants + // found in the dictionary-mode objects. + if (value->IsTheHole()) result->DisallowCaching(); result->DictionaryResult(this, entry); return; } // Slow case object skipped during lookup. Do not use inline caching. - result->DisallowCaching(); + if (!IsGlobalObject()) result->DisallowCaching(); } result->NotFound(); } @@ -6841,6 +6834,26 @@ Object* GlobalObject::GetPropertyCell(LookupResult* result) { } +Object* GlobalObject::EnsurePropertyCell(String* name) { + ASSERT(!HasFastProperties()); + int entry = property_dictionary()->FindEntry(name); + if (entry == StringDictionary::kNotFound) { + Object* cell = Heap::AllocateJSGlobalPropertyCell(Heap::the_hole_value()); + if (cell->IsFailure()) return cell; + PropertyDetails details(NONE, NORMAL); + details = details.AsDeleted(); + Object* dictionary = property_dictionary()->Add(name, cell, details); + if (dictionary->IsFailure()) return dictionary; + set_properties(StringDictionary::cast(dictionary)); + return cell; + } else { + Object* value = property_dictionary()->ValueAt(entry); + ASSERT(value->IsJSGlobalPropertyCell()); + return value; + } +} + + Object* SymbolTable::LookupString(String* string, Object** s) { SymbolKey key(string); return LookupKey(&key, s); @@ -7200,7 +7213,7 @@ Object* Dictionary::AddEntry(Key key, uint32_t entry = Dictionary::FindInsertionEntry(hash); // Insert element at empty or deleted entry - if (details.index() == 0 && Shape::kIsEnumerable) { + if (!details.IsDeleted() && details.index() == 0 && Shape::kIsEnumerable) { // Assign an enumeration index to the property and update // SetNextEnumerationIndex. int index = NextEnumerationIndex(); @@ -7273,7 +7286,9 @@ int Dictionary::NumberOfElementsFilterAttributes( for (int i = 0; i < capacity; i++) { Object* k = HashTable::KeyAt(i); if (HashTable::IsKey(k)) { - PropertyAttributes attr = DetailsAt(i).attributes(); + PropertyDetails details = DetailsAt(i); + if (details.IsDeleted()) continue; + PropertyAttributes attr = details.attributes(); if ((attr & filter) == 0) result++; } } @@ -7297,7 +7312,9 @@ void Dictionary::CopyKeysTo(FixedArray* storage, for (int i = 0; i < capacity; i++) { Object* k = HashTable::KeyAt(i); if (HashTable::IsKey(k)) { - PropertyAttributes attr = DetailsAt(i).attributes(); + PropertyDetails details = DetailsAt(i); + if (details.IsDeleted()) continue; + PropertyAttributes attr = details.attributes(); if ((attr & filter) == 0) storage->set(index++, k); } } @@ -7315,13 +7332,12 @@ void StringDictionary::CopyEnumKeysTo(FixedArray* storage, Object* k = KeyAt(i); if (IsKey(k)) { PropertyDetails details = DetailsAt(i); - if (!details.IsDontEnum()) { - storage->set(index, k); - sort_array->set(index, - Smi::FromInt(details.index()), - SKIP_WRITE_BARRIER); - index++; - } + if (details.IsDeleted() || details.IsDontEnum()) continue; + storage->set(index, k); + sort_array->set(index, + Smi::FromInt(details.index()), + SKIP_WRITE_BARRIER); + index++; } } storage->SortPairs(sort_array, sort_array->length()); @@ -7338,6 +7354,8 @@ void Dictionary::CopyKeysTo(FixedArray* storage) { for (int i = 0; i < capacity; i++) { Object* k = HashTable::KeyAt(i); if (HashTable::IsKey(k)) { + PropertyDetails details = DetailsAt(i); + if (details.IsDeleted()) continue; storage->set(index++, k); } } diff --git a/src/objects.h b/src/objects.h index d53a855..cea924e 100644 --- a/src/objects.h +++ b/src/objects.h @@ -3240,6 +3240,9 @@ class GlobalObject: public JSObject { // Retrieve the property cell used to store a property. Object* GetPropertyCell(LookupResult* result); + // Ensure that the global object has a cell for the given property name. + Object* EnsurePropertyCell(String* name); + // Casting. static inline GlobalObject* cast(Object* obj); diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 01424f9..7eb8cd3 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -450,7 +450,7 @@ Object* StubCache::ComputeCallConstant(int argc, if (!function->is_compiled()) return Failure::InternalError(); // Compile the stub - only create stubs for fully compiled functions. CallStubCompiler compiler(argc, in_loop); - code = compiler.CompileCallConstant(object, holder, function, check); + code = compiler.CompileCallConstant(object, holder, function, name, check); if (code->IsFailure()) return code; ASSERT_EQ(flags, Code::cast(code)->flags()); LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name)); @@ -957,6 +957,10 @@ Object* StubCompiler::CompileCallDebugPrepareStepIn(Code::Flags flags) { Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) { + // Check for allocation failures during stub compilation. + if (failure_->IsFailure()) return failure_; + + // Create code object in the heap. CodeDesc desc; masm_.GetCode(&desc); Object* result = Heap::CreateCode(desc, NULL, flags, masm_.CodeObject()); diff --git a/src/stub-cache.h b/src/stub-cache.h index 9363390..8bee370 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -324,7 +324,7 @@ class StubCompiler BASE_EMBEDDED { JSARRAY_HAS_FAST_ELEMENTS_CHECK }; - StubCompiler() : scope_(), masm_(NULL, 256) { } + StubCompiler() : scope_(), masm_(NULL, 256), failure_(NULL) { } Object* CompileCallInitialize(Code::Flags flags); Object* CompileCallPreMonomorphic(Code::Flags flags); @@ -344,40 +344,7 @@ class StubCompiler BASE_EMBEDDED { static void GenerateFastPropertyLoad(MacroAssembler* masm, Register dst, Register src, JSObject* holder, int index); - static void GenerateLoadField(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - int index, - Label* miss_label); - static void GenerateLoadCallback(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register name, - Register scratch1, - Register scratch2, - AccessorInfo* callback, - Label* miss_label); - static void GenerateLoadConstant(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - Object* value, - Label* miss_label); - static void GenerateLoadInterceptor(MacroAssembler* masm, - JSObject* object, - JSObject* holder, - Smi* lookup_hint, - Register receiver, - Register name, - Register scratch1, - Register scratch2, - Label* miss_label); + static void GenerateLoadArrayLength(MacroAssembler* masm, Register receiver, Register scratch, @@ -412,10 +379,60 @@ class StubCompiler BASE_EMBEDDED { Object* GetCodeWithFlags(Code::Flags flags, String* name); MacroAssembler* masm() { return &masm_; } + void set_failure(Failure* failure) { failure_ = failure; } + + // Check the integrity of the prototype chain to make sure that the + // current IC is still valid. + Register CheckPrototypes(JSObject* object, + Register object_reg, + JSObject* holder, + Register holder_reg, + Register scratch, + String* name, + Label* miss); + + void GenerateLoadField(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + int index, + String* name, + Label* miss); + + void GenerateLoadCallback(JSObject* object, + JSObject* holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + AccessorInfo* callback, + String* name, + Label* miss); + + void GenerateLoadConstant(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + Object* value, + String* name, + Label* miss); + + void GenerateLoadInterceptor(JSObject* object, + JSObject* holder, + Smi* lookup_hint, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + String* name, + Label* miss); private: HandleScope scope_; MacroAssembler masm_; + Failure* failure_; }; @@ -518,6 +535,7 @@ class CallStubCompiler: public StubCompiler { Object* CompileCallConstant(Object* object, JSObject* holder, JSFunction* function, + String* name, CheckType check); Object* CompileCallInterceptor(Object* object, JSObject* holder, diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 5f89fc2..c577615 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -42,7 +42,8 @@ namespace internal { Object* CallStubCompiler::CompileCallConstant(Object* a, JSObject* b, JSFunction* c, - StubCompiler::CheckType d) { + String* d, + StubCompiler::CheckType e) { UNIMPLEMENTED(); return NULL; } -- 2.7.4