Index: src/ia32/stub-cache-ia32.cc |
=================================================================== |
--- src/ia32/stub-cache-ia32.cc (revision 2562) |
+++ src/ia32/stub-cache-ia32.cc (working copy) |
@@ -157,15 +157,10 @@ |
Register receiver, |
Register holder, |
Pushable name, |
- JSObject* holder_obj, |
- Smi* lookup_hint) { |
+ JSObject* holder_obj) { |
__ push(receiver); |
__ push(holder); |
__ push(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)); |
- |
InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); |
__ mov(receiver, Immediate(Handle<Object>(interceptor))); |
__ push(receiver); |
@@ -294,6 +289,322 @@ |
} |
+template <class Pushable> |
+static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, |
+ Register receiver, |
+ Register holder, |
+ Pushable name, |
+ JSObject* holder_obj) { |
+ PushInterceptorArguments(masm, receiver, holder, name, holder_obj); |
+ |
+ ExternalReference ref = |
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)); |
+ __ mov(eax, Immediate(5)); |
+ __ mov(ebx, Immediate(ref)); |
+ |
+ CEntryStub stub; |
+ __ CallStub(&stub); |
+} |
+ |
+ |
+template <class Compiler> |
+static void CompileLoadInterceptor(Compiler* compiler, |
+ StubCompiler* stub_compiler, |
+ MacroAssembler* masm, |
+ JSObject* object, |
+ JSObject* holder, |
+ String* name, |
+ LookupResult* lookup, |
+ Register receiver, |
+ Register scratch1, |
+ Register scratch2, |
+ Label* miss) { |
+ ASSERT(holder->HasNamedInterceptor()); |
+ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined()); |
+ |
+ // 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 = |
+ stub_compiler->CheckPrototypes(object, receiver, holder, |
+ scratch1, scratch2, name, miss); |
+ |
+ if (lookup->IsValid() && lookup->IsCacheable()) { |
+ compiler->CompileCacheable(masm, |
+ stub_compiler, |
+ receiver, |
+ reg, |
+ scratch1, |
+ scratch2, |
+ holder, |
+ lookup, |
+ name, |
+ miss); |
+ } else { |
+ compiler->CompileRegular(masm, |
+ receiver, |
+ reg, |
+ scratch2, |
+ holder, |
+ miss); |
+ } |
+} |
+ |
+ |
+static void LookupPostInterceptor(JSObject* holder, |
+ String* name, |
+ LookupResult* lookup) { |
+ holder->LocalLookupRealNamedProperty(name, lookup); |
+ if (lookup->IsNotFound()) { |
+ Object* proto = holder->GetPrototype(); |
+ if (proto != Heap::null_value()) { |
+ proto->Lookup(name, lookup); |
+ } |
+ } |
+} |
+ |
+ |
+class LoadInterceptorCompiler BASE_EMBEDDED { |
+ public: |
+ explicit LoadInterceptorCompiler(Register name) : name_(name) {} |
+ |
+ void CompileCacheable(MacroAssembler* masm, |
+ StubCompiler* stub_compiler, |
+ Register receiver, |
+ Register holder, |
+ Register scratch1, |
+ Register scratch2, |
+ JSObject* holder_obj, |
+ LookupResult* lookup, |
+ String* name, |
+ Label* miss_label) { |
+ AccessorInfo* callback = 0; |
+ bool optimize = false; |
+ // So far the most popular follow ups for interceptor loads are FIELD |
+ // and CALLBACKS, so inline only them, other cases may be added |
+ // later. |
+ if (lookup->type() == FIELD) { |
+ optimize = true; |
+ } else if (lookup->type() == CALLBACKS) { |
+ Object* callback_object = lookup->GetCallbackObject(); |
+ if (callback_object->IsAccessorInfo()) { |
+ callback = AccessorInfo::cast(callback_object); |
+ optimize = callback->getter() != NULL; |
+ } |
+ } |
+ |
+ if (!optimize) { |
+ CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label); |
+ return; |
+ } |
+ |
+ // Note: starting a frame here makes GC aware of pointers pushed below. |
+ __ EnterInternalFrame(); |
+ |
+ if (lookup->type() == CALLBACKS) { |
+ __ push(receiver); |
+ } |
+ __ push(holder); |
+ __ push(name_); |
+ |
+ CompileCallLoadPropertyWithInterceptor(masm, |
+ receiver, |
+ holder, |
+ name_, |
+ holder_obj); |
+ |
+ Label interceptor_failed; |
+ __ cmp(eax, Factory::no_interceptor_result_sentinel()); |
+ __ j(equal, &interceptor_failed); |
+ __ LeaveInternalFrame(); |
+ __ ret(0); |
+ |
+ __ bind(&interceptor_failed); |
+ __ pop(name_); |
+ __ pop(holder); |
+ if (lookup->type() == CALLBACKS) { |
+ __ pop(receiver); |
+ } |
+ |
+ __ LeaveInternalFrame(); |
+ |
+ if (lookup->type() == FIELD) { |
+ holder = stub_compiler->CheckPrototypes(holder_obj, holder, |
+ lookup->holder(), scratch1, |
+ scratch2, |
+ name, |
+ miss_label); |
+ stub_compiler->GenerateFastPropertyLoad(masm, eax, |
+ holder, lookup->holder(), |
+ lookup->GetFieldIndex()); |
+ __ ret(0); |
+ } else { |
+ ASSERT(lookup->type() == CALLBACKS); |
+ ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); |
+ ASSERT(callback != NULL); |
+ ASSERT(callback->getter() != NULL); |
+ |
+ Label cleanup; |
+ __ pop(scratch2); |
+ __ push(receiver); |
+ __ push(scratch2); |
+ |
+ holder = stub_compiler->CheckPrototypes(holder_obj, holder, |
+ lookup->holder(), scratch1, |
+ scratch2, |
+ name, |
+ &cleanup); |
+ |
+ __ pop(scratch2); // save old return address |
+ __ push(holder); |
+ __ mov(holder, Immediate(Handle<AccessorInfo>(callback))); |
+ __ push(holder); |
+ __ push(FieldOperand(holder, AccessorInfo::kDataOffset)); |
+ __ push(name_); |
+ __ push(scratch2); // restore old return address |
+ |
+ ExternalReference ref = |
+ ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); |
+ __ TailCallRuntime(ref, 5); |
+ |
+ __ bind(&cleanup); |
+ __ pop(scratch1); |
+ __ pop(scratch2); |
+ __ push(scratch1); |
+ } |
+ } |
+ |
+ |
+ void CompileRegular(MacroAssembler* masm, |
+ Register receiver, |
+ Register holder, |
+ Register scratch, |
+ JSObject* holder_obj, |
+ Label* miss_label) { |
+ __ pop(scratch); // save old return address |
+ PushInterceptorArguments(masm, receiver, holder, name_, holder_obj); |
+ __ push(scratch); // restore old return address |
+ |
+ ExternalReference ref = ExternalReference( |
+ IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); |
+ __ TailCallRuntime(ref, 5); |
+ } |
+ |
+ private: |
+ Register name_; |
+}; |
+ |
+ |
+class CallInterceptorCompiler BASE_EMBEDDED { |
+ public: |
+ explicit CallInterceptorCompiler(const ParameterCount& arguments) |
+ : arguments_(arguments), argc_(arguments.immediate()) {} |
+ |
+ void CompileCacheable(MacroAssembler* masm, |
+ StubCompiler* stub_compiler, |
+ Register receiver, |
+ Register holder, |
+ Register scratch1, |
+ Register scratch2, |
+ JSObject* holder_obj, |
+ LookupResult* lookup, |
+ String* name, |
+ Label* miss_label) { |
+ JSFunction* function = 0; |
+ bool optimize = false; |
+ // So far the most popular case for failed interceptor is |
+ // CONSTANT_FUNCTION sitting below. |
+ if (lookup->type() == CONSTANT_FUNCTION) { |
+ function = lookup->GetConstantFunction(); |
+ // JSArray holder is a special case for call constant function |
+ // (see the corresponding code). |
+ if (function->is_compiled() && !holder_obj->IsJSArray()) { |
+ optimize = true; |
+ } |
+ } |
+ |
+ if (!optimize) { |
+ CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label); |
+ return; |
+ } |
+ |
+ __ EnterInternalFrame(); |
+ __ push(holder); // save the holder |
+ |
+ CompileCallLoadPropertyWithInterceptor( |
+ masm, |
+ receiver, |
+ holder, |
+ // Under EnterInternalFrame this refers to name. |
+ Operand(ebp, (argc_ + 3) * kPointerSize), |
+ holder_obj); |
+ |
+ __ pop(receiver); // restore holder |
+ __ LeaveInternalFrame(); |
+ |
+ __ cmp(eax, Factory::no_interceptor_result_sentinel()); |
+ Label invoke; |
+ __ j(not_equal, &invoke); |
+ |
+ stub_compiler->CheckPrototypes(holder_obj, receiver, |
+ lookup->holder(), scratch1, |
+ scratch2, |
+ name, |
+ miss_label); |
+ if (lookup->holder()->IsGlobalObject()) { |
+ __ mov(edx, Operand(esp, (argc_ + 1) * kPointerSize)); |
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset)); |
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx); |
+ } |
+ |
+ ASSERT(function->is_compiled()); |
+ // Get the function and setup the context. |
+ __ mov(edi, Immediate(Handle<JSFunction>(function))); |
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); |
+ |
+ // Jump to the cached code (tail call). |
+ ASSERT(function->is_compiled()); |
+ Handle<Code> code(function->code()); |
+ ParameterCount expected(function->shared()->formal_parameter_count()); |
+ __ InvokeCode(code, expected, arguments_, |
+ RelocInfo::CODE_TARGET, JUMP_FUNCTION); |
+ |
+ __ bind(&invoke); |
+ } |
+ |
+ void CompileRegular(MacroAssembler* masm, |
+ Register receiver, |
+ Register holder, |
+ Register scratch, |
+ JSObject* holder_obj, |
+ Label* miss_label) { |
+ __ EnterInternalFrame(); |
+ |
+ PushInterceptorArguments(masm, |
+ receiver, |
+ holder, |
+ Operand(ebp, (argc_ + 3) * kPointerSize), |
+ holder_obj); |
+ |
+ ExternalReference ref = ExternalReference( |
+ IC_Utility(IC::kLoadPropertyWithInterceptorForCall)); |
+ __ mov(eax, Immediate(5)); |
+ __ mov(ebx, Immediate(ref)); |
+ |
+ CEntryStub stub; |
+ __ CallStub(&stub); |
+ |
+ __ LeaveInternalFrame(); |
+ } |
+ |
+ private: |
+ const ParameterCount& arguments_; |
+ int argc_; |
+}; |
+ |
+ |
void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { |
ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); |
Code* code = NULL; |
@@ -507,36 +818,25 @@ |
void StubCompiler::GenerateLoadInterceptor(JSObject* object, |
JSObject* holder, |
- Smi* lookup_hint, |
+ LookupResult* lookup, |
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 |
- PushInterceptorArguments(masm(), |
- receiver, |
- reg, |
- name_reg, |
- holder, |
- 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, 6); |
+ LoadInterceptorCompiler compiler(name_reg); |
+ CompileLoadInterceptor(&compiler, |
+ this, |
+ masm(), |
+ object, |
+ holder, |
+ name, |
+ lookup, |
+ receiver, |
+ scratch1, |
+ scratch2, |
+ miss); |
} |
@@ -749,49 +1049,32 @@ |
// Get the number of arguments. |
const int argc = arguments().immediate(); |
+ LookupResult lookup; |
+ LookupPostInterceptor(holder, name, &lookup); |
+ |
// Get the receiver from the stack. |
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); |
- // Check that the receiver isn't a smi. |
- __ test(edx, Immediate(kSmiTagMask)); |
- __ j(zero, &miss, not_taken); |
+ CallInterceptorCompiler compiler(arguments()); |
+ CompileLoadInterceptor(&compiler, |
+ this, |
+ masm(), |
+ JSObject::cast(object), |
+ holder, |
+ name, |
+ &lookup, |
+ edx, |
+ ebx, |
+ ecx, |
+ &miss); |
- // Check that maps have not changed and compute the holder register. |
- Register reg = |
- CheckPrototypes(JSObject::cast(object), edx, holder, |
- ebx, ecx, name, &miss); |
+ // Restore receiver. |
+ __ mov(edx, Operand(esp, (argc + 1) * kPointerSize)); |
- // Enter an internal frame. |
- __ EnterInternalFrame(); |
- |
- // Push arguments on the expression stack. |
- PushInterceptorArguments(masm(), |
- edx, |
- reg, |
- Operand(ebp, (argc + 3) * kPointerSize), |
- holder, |
- holder->InterceptorPropertyLookupHint(name)); |
- |
- // Perform call. |
- ExternalReference load_interceptor = |
- ExternalReference(IC_Utility(IC::kLoadInterceptorProperty)); |
- __ mov(eax, Immediate(6)); |
- __ mov(ebx, Immediate(load_interceptor)); |
- |
- CEntryStub stub; |
- __ CallStub(&stub); |
- |
- // Move result to edi and restore receiver. |
- __ mov(edi, eax); |
- __ mov(edx, Operand(ebp, (argc + 2) * kPointerSize)); // receiver |
- |
- // Exit frame. |
- __ LeaveInternalFrame(); |
- |
// Check that the function really is a function. |
- __ test(edi, Immediate(kSmiTagMask)); |
+ __ test(eax, Immediate(kSmiTagMask)); |
__ j(zero, &miss, not_taken); |
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, ebx); |
+ __ CmpObjectType(eax, JS_FUNCTION_TYPE, ebx); |
__ j(not_equal, &miss, not_taken); |
// Patch the receiver on the stack with the global proxy if |
@@ -802,6 +1085,7 @@ |
} |
// Invoke the function. |
+ __ mov(edi, eax); |
__ InvokeFunction(edi, arguments(), JUMP_FUNCTION); |
// Handle load cache miss. |
@@ -1173,12 +1457,15 @@ |
// ----------------------------------- |
Label miss; |
+ LookupResult lookup; |
+ LookupPostInterceptor(holder, name, &lookup); |
+ |
__ mov(eax, Operand(esp, kPointerSize)); |
// TODO(368): Compile in the whole chain: all the interceptors in |
// prototypes and ultimate answer. |
GenerateLoadInterceptor(receiver, |
holder, |
- holder->InterceptorPropertyLookupHint(name), |
+ &lookup, |
eax, |
ecx, |
edx, |
@@ -1353,9 +1640,11 @@ |
__ cmp(Operand(eax), Immediate(Handle<String>(name))); |
__ j(not_equal, &miss, not_taken); |
+ LookupResult lookup; |
+ LookupPostInterceptor(holder, name, &lookup); |
GenerateLoadInterceptor(receiver, |
holder, |
- Smi::FromInt(JSObject::kLookupInHolder), |
+ &lookup, |
ecx, |
eax, |
edx, |