| Index: src/arm/stub-cache-arm.cc
|
| ===================================================================
|
| --- src/arm/stub-cache-arm.cc (revision 4439)
|
| +++ src/arm/stub-cache-arm.cc (working copy)
|
| @@ -600,6 +600,259 @@
|
| }
|
|
|
|
|
| +// Reserves space for the extra arguments to FastHandleApiCall in the
|
| +// caller's frame.
|
| +//
|
| +// These arguments are set by CheckPrototypes and GenerateFastApiCall.
|
| +static void ReserveSpaceForFastApiCall(MacroAssembler* masm,
|
| + Register scratch) {
|
| + __ mov(scratch, Operand(Smi::FromInt(0)));
|
| + __ push(scratch);
|
| + __ push(scratch);
|
| + __ push(scratch);
|
| + __ push(scratch);
|
| +}
|
| +
|
| +
|
| +// Undoes the effects of ReserveSpaceForFastApiCall.
|
| +static void FreeSpaceForFastApiCall(MacroAssembler* masm) {
|
| + __ Drop(4);
|
| +}
|
| +
|
| +
|
| +// Generates call to FastHandleApiCall builtin.
|
| +static void GenerateFastApiCall(MacroAssembler* masm,
|
| + const CallOptimization& optimization,
|
| + int argc) {
|
| + // Get the function and setup the context.
|
| + JSFunction* function = optimization.constant_function();
|
| + __ mov(r7, Operand(Handle<JSFunction>(function)));
|
| + __ ldr(cp, FieldMemOperand(r7, JSFunction::kContextOffset));
|
| +
|
| + // Pass the additional arguments FastHandleApiCall expects.
|
| + bool info_loaded = false;
|
| + Object* callback = optimization.api_call_info()->callback();
|
| + if (Heap::InNewSpace(callback)) {
|
| + info_loaded = true;
|
| + __ Move(r0, Handle<CallHandlerInfo>(optimization.api_call_info()));
|
| + __ ldr(r6, FieldMemOperand(r0, CallHandlerInfo::kCallbackOffset));
|
| + } else {
|
| + __ Move(r6, Handle<Object>(callback));
|
| + }
|
| + Object* call_data = optimization.api_call_info()->data();
|
| + if (Heap::InNewSpace(call_data)) {
|
| + if (!info_loaded) {
|
| + __ Move(r0, Handle<CallHandlerInfo>(optimization.api_call_info()));
|
| + }
|
| + __ ldr(r5, FieldMemOperand(r0, CallHandlerInfo::kDataOffset));
|
| + } else {
|
| + __ Move(r5, Handle<Object>(call_data));
|
| + }
|
| +
|
| + __ add(sp, sp, Operand(1 * kPointerSize));
|
| + __ stm(ia, sp, r5.bit() | r6.bit() | r7.bit());
|
| + __ sub(sp, sp, Operand(1 * kPointerSize));
|
| +
|
| + // Set the number of arguments.
|
| + __ mov(r0, Operand(argc + 4));
|
| +
|
| + // Jump to the fast api call builtin (tail call).
|
| + Handle<Code> code = Handle<Code>(
|
| + Builtins::builtin(Builtins::FastHandleApiCall));
|
| + ParameterCount expected(0);
|
| + __ InvokeCode(code, expected, expected,
|
| + RelocInfo::CODE_TARGET, JUMP_FUNCTION);
|
| +}
|
| +
|
| +
|
| +class CallInterceptorCompiler BASE_EMBEDDED {
|
| + public:
|
| + CallInterceptorCompiler(StubCompiler* stub_compiler,
|
| + const ParameterCount& arguments,
|
| + Register name)
|
| + : stub_compiler_(stub_compiler),
|
| + arguments_(arguments),
|
| + name_(name) {}
|
| +
|
| + void Compile(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);
|
| +
|
| + CallOptimization optimization(lookup);
|
| +
|
| + if (optimization.is_constant_call()) {
|
| + CompileCacheable(masm,
|
| + object,
|
| + receiver,
|
| + scratch1,
|
| + scratch2,
|
| + holder,
|
| + lookup,
|
| + name,
|
| + optimization,
|
| + miss);
|
| + } else {
|
| + CompileRegular(masm,
|
| + object,
|
| + receiver,
|
| + scratch1,
|
| + scratch2,
|
| + name,
|
| + holder,
|
| + miss);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + void CompileCacheable(MacroAssembler* masm,
|
| + JSObject* object,
|
| + Register receiver,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + JSObject* holder_obj,
|
| + LookupResult* lookup,
|
| + String* name,
|
| + const CallOptimization& optimization,
|
| + Label* miss_label) {
|
| + ASSERT(optimization.is_constant_call());
|
| + ASSERT(!lookup->holder()->IsGlobalObject());
|
| +
|
| + int depth1 = kInvalidProtoDepth;
|
| + int depth2 = kInvalidProtoDepth;
|
| + bool can_do_fast_api_call = false;
|
| + if (optimization.is_simple_api_call() &&
|
| + !lookup->holder()->IsGlobalObject()) {
|
| + depth1 = optimization.GetPrototypeDepthOfExpectedType(object, holder_obj);
|
| + if (depth1 == kInvalidProtoDepth) {
|
| + depth2 = optimization.GetPrototypeDepthOfExpectedType(holder_obj,
|
| + lookup->holder());
|
| + }
|
| + can_do_fast_api_call = (depth1 != kInvalidProtoDepth) ||
|
| + (depth2 != kInvalidProtoDepth);
|
| + }
|
| +
|
| + __ IncrementCounter(&Counters::call_const_interceptor, 1,
|
| + scratch1, scratch2);
|
| +
|
| + if (can_do_fast_api_call) {
|
| + __ IncrementCounter(&Counters::call_const_interceptor_fast_api, 1,
|
| + scratch1, scratch2);
|
| + ReserveSpaceForFastApiCall(masm, scratch1);
|
| + }
|
| +
|
| + Label miss_cleanup;
|
| + Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
|
| + Register holder =
|
| + stub_compiler_->CheckPrototypes(object, receiver, holder_obj, scratch1,
|
| + scratch2, name, depth1, miss);
|
| +
|
| + Label regular_invoke;
|
| + LoadWithInterceptor(masm, receiver, holder, holder_obj, scratch2,
|
| + ®ular_invoke);
|
| +
|
| + // Generate code for the failed interceptor case.
|
| +
|
| + // Check the lookup is still valid.
|
| + stub_compiler_->CheckPrototypes(holder_obj, receiver,
|
| + lookup->holder(), scratch1,
|
| + scratch2, name, depth2, miss);
|
| +
|
| + if (can_do_fast_api_call) {
|
| + GenerateFastApiCall(masm, optimization, arguments_.immediate());
|
| + } else {
|
| + __ InvokeFunction(optimization.constant_function(), arguments_,
|
| + JUMP_FUNCTION);
|
| + }
|
| +
|
| + if (can_do_fast_api_call) {
|
| + __ bind(&miss_cleanup);
|
| + FreeSpaceForFastApiCall(masm);
|
| + __ b(miss_label);
|
| + }
|
| +
|
| + __ bind(®ular_invoke);
|
| + if (can_do_fast_api_call) {
|
| + FreeSpaceForFastApiCall(masm);
|
| + }
|
| + }
|
| +
|
| + void CompileRegular(MacroAssembler* masm,
|
| + JSObject* object,
|
| + Register receiver,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + String* name,
|
| + JSObject* holder_obj,
|
| + Label* miss_label) {
|
| + Register holder =
|
| + stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
|
| + scratch1, scratch2, name,
|
| + miss_label);
|
| +
|
| + // Call a runtime function to load the interceptor property.
|
| + __ EnterInternalFrame();
|
| + // Save the name_ register across the call.
|
| + __ push(name_);
|
| +
|
| + PushInterceptorArguments(masm,
|
| + receiver,
|
| + holder,
|
| + name_,
|
| + holder_obj);
|
| +
|
| + __ CallExternalReference(
|
| + ExternalReference(
|
| + IC_Utility(IC::kLoadPropertyWithInterceptorForCall)),
|
| + 5);
|
| +
|
| + // Restore the name_ register.
|
| + __ pop(name_);
|
| + __ LeaveInternalFrame();
|
| + }
|
| +
|
| + void LoadWithInterceptor(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register holder,
|
| + JSObject* holder_obj,
|
| + Register scratch,
|
| + Label* interceptor_succeeded) {
|
| + __ EnterInternalFrame();
|
| + __ push(holder); // Save the holder.
|
| + __ push(name_); // Save the name.
|
| +
|
| + CompileCallLoadPropertyWithInterceptor(masm,
|
| + receiver,
|
| + holder,
|
| + name_,
|
| + holder_obj);
|
| +
|
| + __ pop(name_); // Restore the name.
|
| + __ pop(receiver); // Restore the holder.
|
| + __ LeaveInternalFrame();
|
| +
|
| + // If interceptor returns no-result sentinel, call the constant function.
|
| + __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex);
|
| + __ cmp(r0, scratch);
|
| + __ b(ne, interceptor_succeeded);
|
| + }
|
| +
|
| + StubCompiler* stub_compiler_;
|
| + const ParameterCount& arguments_;
|
| + Register name_;
|
| +};
|
| +
|
| +
|
| // Generate code to check that a global property cell is empty. Create
|
| // the property cell at compilation time if no cell exists for the
|
| // property.
|
| @@ -634,12 +887,10 @@
|
| String* name,
|
| int save_at_depth,
|
| Label* miss) {
|
| - // TODO(602): support object saving.
|
| - ASSERT(save_at_depth == kInvalidProtoDepth);
|
| -
|
| // Check that the maps haven't changed.
|
| Register result =
|
| - masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
|
| + masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
|
| + save_at_depth, miss);
|
|
|
| // If we've skipped any global objects, it's not enough to verify
|
| // that their maps haven't changed. We also need to check that the
|
| @@ -946,7 +1197,7 @@
|
| return generator(this, object, holder, function, name, check);
|
| }
|
|
|
| - Label miss;
|
| + Label miss_in_smi_check;
|
|
|
| // Get the receiver from the stack
|
| const int argc = arguments().immediate();
|
| @@ -955,21 +1206,39 @@
|
| // Check that the receiver isn't a smi.
|
| if (check != NUMBER_CHECK) {
|
| __ tst(r1, Operand(kSmiTagMask));
|
| - __ b(eq, &miss);
|
| + __ b(eq, &miss_in_smi_check);
|
| }
|
|
|
| // Make sure that it's okay not to patch the on stack receiver
|
| // unless we're doing a receiver map check.
|
| ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
|
|
|
| + CallOptimization optimization(function);
|
| + int depth = kInvalidProtoDepth;
|
| + Label miss;
|
| +
|
| switch (check) {
|
| case RECEIVER_MAP_CHECK:
|
| + __ IncrementCounter(&Counters::call_const, 1, r0, r3);
|
| +
|
| + if (optimization.is_simple_api_call() && !object->IsGlobalObject()) {
|
| + depth = optimization.GetPrototypeDepthOfExpectedType(
|
| + JSObject::cast(object), holder);
|
| + }
|
| +
|
| + if (depth != kInvalidProtoDepth) {
|
| + __ IncrementCounter(&Counters::call_const_fast_api, 1, r0, r3);
|
| + ReserveSpaceForFastApiCall(masm(), r0);
|
| + }
|
| +
|
| // Check that the maps haven't changed.
|
| - CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
|
| + CheckPrototypes(JSObject::cast(object), r1, holder, r0, r3, name,
|
| + depth, &miss);
|
|
|
| // Patch the receiver on the stack with the global proxy if
|
| // necessary.
|
| if (object->IsGlobalObject()) {
|
| + ASSERT(depth == kInvalidProtoDepth);
|
| __ ldr(r3, FieldMemOperand(r1, GlobalObject::kGlobalReceiverOffset));
|
| __ str(r3, MemOperand(sp, argc * kPointerSize));
|
| }
|
| @@ -1042,10 +1311,19 @@
|
| UNREACHABLE();
|
| }
|
|
|
| - __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
|
| + if (depth != kInvalidProtoDepth) {
|
| + GenerateFastApiCall(masm(), optimization, argc);
|
| + } else {
|
| + __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
|
| + }
|
|
|
| // Handle call cache miss.
|
| __ bind(&miss);
|
| + if (depth != kInvalidProtoDepth) {
|
| + FreeSpaceForFastApiCall(masm());
|
| + }
|
| +
|
| + __ bind(&miss_in_smi_check);
|
| Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
| __ Jump(ic, RelocInfo::CODE_TARGET);
|
|
|
| @@ -1065,96 +1343,33 @@
|
| // -- r2 : name
|
| // -- lr : return address
|
| // -----------------------------------
|
| - ASSERT(holder->HasNamedInterceptor());
|
| - ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
|
| +
|
| Label miss;
|
|
|
| - const Register receiver = r0;
|
| - const Register holder_reg = r1;
|
| - const Register name_reg = r2;
|
| - const Register scratch = r3;
|
| -
|
| // 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));
|
| + // Get the receiver from the stack.
|
| + __ ldr(r1, MemOperand(sp, argc * kPointerSize));
|
|
|
| - // Check that the receiver isn't a smi.
|
| - __ BranchOnSmi(receiver, &miss);
|
| + CallInterceptorCompiler compiler(this, arguments(), r2);
|
| + compiler.Compile(masm(),
|
| + object,
|
| + holder,
|
| + name,
|
| + &lookup,
|
| + r1,
|
| + r3,
|
| + r4,
|
| + &miss);
|
|
|
| - // Check that the maps haven't changed.
|
| - Register reg = CheckPrototypes(object, receiver, holder, holder_reg,
|
| - scratch, name, &miss);
|
| - if (!reg.is(holder_reg)) {
|
| - __ mov(holder_reg, reg);
|
| - }
|
| -
|
| - // If we call a constant function when the interceptor returns
|
| - // the no-result sentinel, generate code that optimizes this case.
|
| - if (lookup.IsProperty() &&
|
| - lookup.IsCacheable() &&
|
| - lookup.type() == CONSTANT_FUNCTION &&
|
| - lookup.GetConstantFunction()->is_compiled() &&
|
| - !holder->IsJSArray()) {
|
| - // Constant functions cannot sit on global object.
|
| - ASSERT(!lookup.holder()->IsGlobalObject());
|
| -
|
| - // Call the interceptor.
|
| - __ EnterInternalFrame();
|
| - __ push(holder_reg);
|
| - __ push(name_reg);
|
| - CompileCallLoadPropertyWithInterceptor(masm(),
|
| - receiver,
|
| - holder_reg,
|
| - name_reg,
|
| - holder);
|
| - __ pop(name_reg);
|
| - __ pop(holder_reg);
|
| - __ LeaveInternalFrame();
|
| - // r0 no longer contains the receiver.
|
| -
|
| - // If interceptor returns no-result sentinal, call the constant function.
|
| - __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex);
|
| - __ cmp(r0, scratch);
|
| - Label invoke;
|
| - __ b(ne, &invoke);
|
| - // Check the prototypes between the interceptor's holder and the
|
| - // constant function's holder.
|
| - CheckPrototypes(holder, holder_reg,
|
| - lookup.holder(), r0,
|
| - scratch,
|
| - name,
|
| - &miss);
|
| -
|
| - __ InvokeFunction(lookup.GetConstantFunction(),
|
| - arguments(),
|
| - JUMP_FUNCTION);
|
| -
|
| - __ bind(&invoke);
|
| -
|
| - } else {
|
| - // Call a runtime function to load the interceptor property.
|
| - __ EnterInternalFrame();
|
| - __ push(name_reg);
|
| -
|
| - PushInterceptorArguments(masm(), receiver, holder_reg, name_reg, holder);
|
| -
|
| - __ CallExternalReference(
|
| - ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall)),
|
| - 5);
|
| -
|
| - __ pop(name_reg);
|
| - __ LeaveInternalFrame();
|
| - }
|
| -
|
| // Move returned value, the function to call, to r1.
|
| __ mov(r1, r0);
|
| // Restore receiver.
|
| - __ ldr(receiver, MemOperand(sp, argc * kPointerSize));
|
| + __ ldr(r0, MemOperand(sp, argc * kPointerSize));
|
|
|
| GenerateCallFunction(masm(), object, arguments(), &miss);
|
|
|
|
|