| Index: src/arm/stub-cache-arm.cc
|
| diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
|
| index 687fb1e73dc31b3e855f3c643984b55b6a950807..5c3a66bbafd4f7edab99bb0e120cb215faf73766 100644
|
| --- a/src/arm/stub-cache-arm.cc
|
| +++ b/src/arm/stub-cache-arm.cc
|
| @@ -362,6 +362,369 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
|
| }
|
|
|
|
|
| +static void GenerateCallFunction(MacroAssembler* masm,
|
| + Object* object,
|
| + const ParameterCount& arguments,
|
| + Label* miss) {
|
| + // ----------- S t a t e -------------
|
| + // -- r0: receiver
|
| + // -- r1: function to call
|
| + // -----------------------------------
|
| +
|
| + // Check that the function really is a function.
|
| + __ BranchOnSmi(r1, miss);
|
| + __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
|
| + __ b(ne, miss);
|
| +
|
| + // Patch the receiver on the stack with the global proxy if
|
| + // necessary.
|
| + if (object->IsGlobalObject()) {
|
| + __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
|
| + __ str(r3, MemOperand(sp, arguments.immediate() * kPointerSize));
|
| + }
|
| +
|
| + // Invoke the function.
|
| + __ InvokeFunction(r1, arguments, JUMP_FUNCTION);
|
| +}
|
| +
|
| +
|
| +static void GenerateCallConstFunction(MacroAssembler* masm,
|
| + JSFunction* function,
|
| + const ParameterCount& arguments) {
|
| + ASSERT(function->is_compiled());
|
| +
|
| + // Get the function and setup the context.
|
| + __ mov(r1, Operand(Handle<JSFunction>(function)));
|
| + __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
|
| +
|
| + // Jump to the cached code (tail call).
|
| + Handle<Code> code(function->code());
|
| + ParameterCount expected(function->shared()->formal_parameter_count());
|
| + __ InvokeCode(code, expected, arguments,
|
| + RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
| +}
|
| +
|
| +
|
| +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.
|
| + __ BranchOnSmi(receiver, miss);
|
| +
|
| + // 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 PushInterceptorArguments(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register holder,
|
| + Register name,
|
| + JSObject* holder_obj) {
|
| + __ push(receiver);
|
| + __ push(holder);
|
| + __ push(name);
|
| + InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
|
| + ASSERT(!Heap::InNewSpace(interceptor));
|
| +
|
| + Register scratch = receiver;
|
| + __ mov(scratch, Operand(Handle<Object>(interceptor)));
|
| + __ push(scratch);
|
| + __ ldr(scratch, FieldMemOperand(scratch, InterceptorInfo::kDataOffset));
|
| + __ push(scratch);
|
| +}
|
| +
|
| +
|
| +static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register holder,
|
| + Register name,
|
| + JSObject* holder_obj) {
|
| + PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
|
| +
|
| + ExternalReference ref =
|
| + ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly));
|
| + __ mov(r0, Operand(5));
|
| + __ mov(r1, Operand(ref));
|
| +
|
| + CEntryStub stub(1);
|
| + __ CallStub(&stub);
|
| +}
|
| +
|
| +
|
| +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;
|
| + // Compare with no_interceptor_result_sentinel.
|
| + __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex);
|
| + __ cmp(r0, scratch1);
|
| + __ b(eq, &interceptor_failed);
|
| + __ LeaveInternalFrame();
|
| + __ Ret();
|
| +
|
| + __ 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,
|
| + r0,
|
| + holder,
|
| + lookup->holder(),
|
| + lookup->GetFieldIndex());
|
| + __ Ret();
|
| + } 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);
|
| +
|
| + __ push(holder);
|
| + __ Move(holder, Handle<AccessorInfo>(callback));
|
| + __ push(holder);
|
| + __ ldr(scratch1, FieldMemOperand(holder, AccessorInfo::kDataOffset));
|
| + __ push(scratch1);
|
| + __ push(name_);
|
| +
|
| + ExternalReference ref =
|
| + ExternalReference(IC_Utility(IC::kLoadCallbackProperty));
|
| + __ TailCallRuntime(ref, 5, 1);
|
| +
|
| + __ bind(&cleanup);
|
| + __ pop(scratch1);
|
| + __ pop(scratch2);
|
| + __ push(scratch1);
|
| + }
|
| + }
|
| +
|
| +
|
| + void CompileRegular(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register holder,
|
| + Register scratch,
|
| + JSObject* holder_obj,
|
| + Label* miss_label) {
|
| + PushInterceptorArguments(masm, receiver, holder, name_, holder_obj);
|
| +
|
| + ExternalReference ref = ExternalReference(
|
| + IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
|
| + __ TailCallRuntime(ref, 5, 1);
|
| + }
|
| +
|
| + private:
|
| + Register name_;
|
| +};
|
| +
|
| +
|
| +class CallInterceptorCompiler BASE_EMBEDDED {
|
| + public:
|
| + CallInterceptorCompiler(const ParameterCount& arguments, Register name)
|
| + : arguments_(arguments), argc_(arguments.immediate()), 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) {
|
| + 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;
|
| + }
|
| +
|
| + // Constant functions cannot sit on global object.
|
| + ASSERT(!lookup->holder()->IsGlobalObject());
|
| +
|
| + __ EnterInternalFrame();
|
| + __ push(holder); // Save the holder.
|
| + __ push(name_); // Save the name.
|
| +
|
| + CompileCallLoadPropertyWithInterceptor(masm,
|
| + receiver,
|
| + holder,
|
| + name_,
|
| + holder_obj);
|
| +
|
| + ASSERT(!r0.is(name_));
|
| + ASSERT(!r0.is(scratch1));
|
| + __ pop(name_); // Restore the name.
|
| + __ pop(scratch1); // Restore the holder.
|
| + __ LeaveInternalFrame();
|
| +
|
| + // Compare with no_interceptor_result_sentinel.
|
| + __ LoadRoot(scratch2, Heap::kNoInterceptorResultSentinelRootIndex);
|
| + __ cmp(r0, scratch2);
|
| + Label invoke;
|
| + __ b(ne, &invoke);
|
| +
|
| + stub_compiler->CheckPrototypes(holder_obj, scratch1,
|
| + lookup->holder(), scratch1,
|
| + scratch2,
|
| + name,
|
| + miss_label);
|
| + GenerateCallConstFunction(masm, function, arguments_);
|
| +
|
| + __ bind(&invoke);
|
| + }
|
| +
|
| + void CompileRegular(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register holder,
|
| + Register scratch,
|
| + JSObject* holder_obj,
|
| + Label* miss_label) {
|
| + __ EnterInternalFrame();
|
| + // Save the name_ register across the call.
|
| + __ push(name_);
|
| +
|
| + PushInterceptorArguments(masm,
|
| + receiver,
|
| + holder,
|
| + name_,
|
| + holder_obj);
|
| +
|
| + ExternalReference ref = ExternalReference(
|
| + IC_Utility(IC::kLoadPropertyWithInterceptorForCall));
|
| + __ mov(r0, Operand(5));
|
| + __ mov(r1, Operand(ref));
|
| +
|
| + CEntryStub stub(1);
|
| + __ CallStub(&stub);
|
| +
|
| + // Restore the name_ register.
|
| + __ pop(name_);
|
| + __ LeaveInternalFrame();
|
| + }
|
| +
|
| + private:
|
| + const ParameterCount& arguments_;
|
| + int argc_;
|
| + Register name_;
|
| +};
|
| +
|
| +
|
| #undef __
|
| #define __ ACCESS_MASM(masm())
|
|
|
| @@ -491,30 +854,18 @@ void StubCompiler::GenerateLoadInterceptor(JSObject* object,
|
| 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
|
| -
|
| - InterceptorInfo* interceptor = holder->GetNamedInterceptor();
|
| - ASSERT(!Heap::InNewSpace(interceptor));
|
| - __ mov(scratch1, Operand(Handle<Object>(interceptor)));
|
| - __ push(scratch1);
|
| - __ ldr(scratch2, FieldMemOperand(scratch1, InterceptorInfo::kDataOffset));
|
| - __ push(scratch2);
|
| -
|
| - // Do tail-call to the runtime system.
|
| - ExternalReference load_ic_property =
|
| - ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
|
| - __ TailCallRuntime(load_ic_property, 5, 1);
|
| + LoadInterceptorCompiler compiler(name_reg);
|
| + CompileLoadInterceptor(&compiler,
|
| + this,
|
| + masm(),
|
| + object,
|
| + holder,
|
| + name,
|
| + lookup,
|
| + receiver,
|
| + scratch1,
|
| + scratch2,
|
| + miss);
|
| }
|
|
|
|
|
| @@ -572,22 +923,7 @@ Object* CallStubCompiler::CompileCallField(Object* object,
|
| CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss);
|
| GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
|
|
|
| - // Check that the function really is a function.
|
| - __ tst(r1, Operand(kSmiTagMask));
|
| - __ b(eq, &miss);
|
| - // Get the map.
|
| - __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
|
| - __ b(ne, &miss);
|
| -
|
| - // Patch the receiver on the stack with the global proxy if
|
| - // necessary.
|
| - if (object->IsGlobalObject()) {
|
| - __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
|
| - __ str(r3, MemOperand(sp, argc * kPointerSize));
|
| - }
|
| -
|
| - // Invoke the function.
|
| - __ InvokeFunction(r1, arguments(), JUMP_FUNCTION);
|
| + GenerateCallFunction(masm(), object, arguments(), &miss);
|
|
|
| // Handle call cache miss.
|
| __ bind(&miss);
|
| @@ -715,16 +1051,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
|
| UNREACHABLE();
|
| }
|
|
|
| - // Get the function and setup the context.
|
| - __ mov(r1, Operand(Handle<JSFunction>(function)));
|
| - __ ldr(cp, FieldMemOperand(r1, 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);
|
| + GenerateCallConstFunction(masm(), function, arguments());
|
|
|
| // Handle call cache miss.
|
| __ bind(&miss);
|
| @@ -748,7 +1075,34 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - // TODO(1224669): Implement.
|
| + // Get the number of arguments.
|
| + const int argc = arguments().immediate();
|
| +
|
| + LookupResult lookup;
|
| + LookupPostInterceptor(holder, name, &lookup);
|
| +
|
| + // Get the receiver from the stack into r0.
|
| + __ ldr(r0, MemOperand(sp, argc * kPointerSize));
|
| + // Load the name from the stack into r1.
|
| + __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize));
|
| +
|
| + CallInterceptorCompiler compiler(arguments(), r1);
|
| + CompileLoadInterceptor(&compiler,
|
| + this,
|
| + masm(),
|
| + JSObject::cast(object),
|
| + holder,
|
| + name,
|
| + &lookup,
|
| + r0,
|
| + r2,
|
| + r3,
|
| + &miss);
|
| +
|
| + // Restore receiver.
|
| + __ ldr(r0, MemOperand(sp, argc * kPointerSize));
|
| +
|
| + GenerateCallFunction(masm(), object, arguments(), &miss);
|
|
|
| // Handle call cache miss.
|
| __ bind(&miss);
|
| @@ -1099,7 +1453,7 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* object,
|
| __ ldr(r0, MemOperand(sp, 0));
|
|
|
| LookupResult lookup;
|
| - holder->LocalLookupRealNamedProperty(name, &lookup);
|
| + LookupPostInterceptor(holder, name, &lookup);
|
| GenerateLoadInterceptor(object,
|
| holder,
|
| &lookup,
|
| @@ -1265,7 +1619,7 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
|
| __ b(ne, &miss);
|
|
|
| LookupResult lookup;
|
| - holder->LocalLookupRealNamedProperty(name, &lookup);
|
| + LookupPostInterceptor(holder, name, &lookup);
|
| GenerateLoadInterceptor(receiver,
|
| holder,
|
| &lookup,
|
|
|