| Index: src/ia32/stub-cache-ia32.cc
|
| ===================================================================
|
| --- src/ia32/stub-cache-ia32.cc (revision 3935)
|
| +++ src/ia32/stub-cache-ia32.cc (working copy)
|
| @@ -152,22 +152,6 @@
|
| }
|
|
|
|
|
| -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));
|
| - __ mov(receiver, Immediate(Handle<Object>(interceptor)));
|
| - __ push(receiver);
|
| - __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset));
|
| -}
|
| -
|
| -
|
| void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
|
| int index,
|
| Register prototype) {
|
| @@ -226,30 +210,32 @@
|
|
|
| void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
|
| Register receiver,
|
| - Register scratch,
|
| + Register scratch1,
|
| + Register scratch2,
|
| Label* miss) {
|
| - Label load_length, check_wrapper;
|
| + Label check_wrapper;
|
|
|
| // Check if the object is a string leaving the instance type in the
|
| // scratch register.
|
| - GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper);
|
| + GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper);
|
|
|
| // Load length from the string and convert to a smi.
|
| - __ bind(&load_length);
|
| __ mov(eax, FieldOperand(receiver, String::kLengthOffset));
|
| __ SmiTag(eax);
|
| __ ret(0);
|
|
|
| // Check if the object is a JSValue wrapper.
|
| __ bind(&check_wrapper);
|
| - __ cmp(scratch, JS_VALUE_TYPE);
|
| + __ cmp(scratch1, JS_VALUE_TYPE);
|
| __ j(not_equal, miss, not_taken);
|
|
|
| // Check if the wrapped value is a string and load the length
|
| // directly if it is.
|
| - __ mov(receiver, FieldOperand(receiver, JSValue::kValueOffset));
|
| - GenerateStringCheck(masm, receiver, scratch, miss, miss);
|
| - __ jmp(&load_length);
|
| + __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
|
| + GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
|
| + __ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
|
| + __ SmiTag(eax);
|
| + __ ret(0);
|
| }
|
|
|
|
|
| @@ -285,20 +271,31 @@
|
| }
|
|
|
|
|
| +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));
|
| + __ mov(receiver, Immediate(Handle<Object>(interceptor)));
|
| + __ push(receiver);
|
| + __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset));
|
| +}
|
| +
|
| +
|
| 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(eax, Immediate(5));
|
| - __ mov(ebx, Immediate(ref));
|
| -
|
| - CEntryStub stub(1);
|
| - __ CallStub(&stub);
|
| + __ CallExternalReference(
|
| + ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)),
|
| + 5);
|
| }
|
|
|
|
|
| @@ -326,7 +323,7 @@
|
| stub_compiler->CheckPrototypes(object, receiver, holder,
|
| scratch1, scratch2, name, miss);
|
|
|
| - if (lookup->IsValid() && lookup->IsCacheable()) {
|
| + if (lookup->IsProperty() && lookup->IsCacheable()) {
|
| compiler->CompileCacheable(masm,
|
| stub_compiler,
|
| receiver,
|
| @@ -362,7 +359,7 @@
|
| LookupResult* lookup,
|
| String* name,
|
| Label* miss_label) {
|
| - AccessorInfo* callback = 0;
|
| + AccessorInfo* callback = NULL;
|
| 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
|
| @@ -479,88 +476,337 @@
|
| };
|
|
|
|
|
| +// Holds information about possible function call optimizations.
|
| +class CallOptimization BASE_EMBEDDED {
|
| + public:
|
| + explicit CallOptimization(LookupResult* lookup)
|
| + : constant_function_(NULL),
|
| + is_simple_api_call_(false),
|
| + expected_receiver_type_(NULL),
|
| + api_call_info_(NULL) {
|
| + if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
|
| +
|
| + // We only optimize constant function calls.
|
| + if (lookup->type() != CONSTANT_FUNCTION) return;
|
| +
|
| + Initialize(lookup->GetConstantFunction());
|
| + }
|
| +
|
| + explicit CallOptimization(JSFunction* function) {
|
| + Initialize(function);
|
| + }
|
| +
|
| + bool is_constant_call() const {
|
| + return constant_function_ != NULL;
|
| + }
|
| +
|
| + JSFunction* constant_function() const {
|
| + ASSERT(constant_function_ != NULL);
|
| + return constant_function_;
|
| + }
|
| +
|
| + bool is_simple_api_call() const {
|
| + return is_simple_api_call_;
|
| + }
|
| +
|
| + FunctionTemplateInfo* expected_receiver_type() const {
|
| + ASSERT(is_simple_api_call_);
|
| + return expected_receiver_type_;
|
| + }
|
| +
|
| + CallHandlerInfo* api_call_info() const {
|
| + ASSERT(is_simple_api_call_);
|
| + return api_call_info_;
|
| + }
|
| +
|
| + // Returns the depth of the object having the expected type in the
|
| + // prototype chain between the two arguments.
|
| + int GetPrototypeDepthOfExpectedType(JSObject* object,
|
| + JSObject* holder) const {
|
| + ASSERT(is_simple_api_call_);
|
| + if (expected_receiver_type_ == NULL) return 0;
|
| + int depth = 0;
|
| + while (object != holder) {
|
| + if (object->IsInstanceOf(expected_receiver_type_)) return depth;
|
| + object = JSObject::cast(object->GetPrototype());
|
| + ++depth;
|
| + }
|
| + if (holder->IsInstanceOf(expected_receiver_type_)) return depth;
|
| + return kInvalidProtoDepth;
|
| + }
|
| +
|
| + private:
|
| + void Initialize(JSFunction* function) {
|
| + if (!function->is_compiled()) return;
|
| +
|
| + constant_function_ = function;
|
| + is_simple_api_call_ = false;
|
| +
|
| + AnalyzePossibleApiFunction(function);
|
| + }
|
| +
|
| + // Determines whether the given function can be called using the
|
| + // fast api call builtin.
|
| + void AnalyzePossibleApiFunction(JSFunction* function) {
|
| + SharedFunctionInfo* sfi = function->shared();
|
| + if (sfi->function_data()->IsUndefined()) return;
|
| + FunctionTemplateInfo* info =
|
| + FunctionTemplateInfo::cast(sfi->function_data());
|
| +
|
| + // Require a C++ callback.
|
| + if (info->call_code()->IsUndefined()) return;
|
| + api_call_info_ = CallHandlerInfo::cast(info->call_code());
|
| +
|
| + // Accept signatures that either have no restrictions at all or
|
| + // only have restrictions on the receiver.
|
| + if (!info->signature()->IsUndefined()) {
|
| + SignatureInfo* signature = SignatureInfo::cast(info->signature());
|
| + if (!signature->args()->IsUndefined()) return;
|
| + if (!signature->receiver()->IsUndefined()) {
|
| + expected_receiver_type_ =
|
| + FunctionTemplateInfo::cast(signature->receiver());
|
| + }
|
| + }
|
| +
|
| + is_simple_api_call_ = true;
|
| + }
|
| +
|
| + JSFunction* constant_function_;
|
| + bool is_simple_api_call_;
|
| + FunctionTemplateInfo* expected_receiver_type_;
|
| + CallHandlerInfo* api_call_info_;
|
| +};
|
| +
|
| +
|
| +// 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) {
|
| + // ----------- S t a t e -------------
|
| + // -- esp[0] : return address
|
| + // -- esp[4] : last argument in the internal frame of the caller
|
| + // -----------------------------------
|
| + __ pop(scratch);
|
| + __ push(Immediate(Smi::FromInt(0)));
|
| + __ push(Immediate(Smi::FromInt(0)));
|
| + __ push(Immediate(Smi::FromInt(0)));
|
| + __ push(Immediate(Smi::FromInt(0)));
|
| + __ push(scratch);
|
| +}
|
| +
|
| +
|
| +// Undoes the effects of ReserveSpaceForFastApiCall.
|
| +static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
|
| + // ----------- S t a t e -------------
|
| + // -- esp[0] : return address
|
| + // -- esp[4] : last fast api call extra argument
|
| + // -- ...
|
| + // -- esp[16] : first fast api call extra argument
|
| + // -- esp[20] : last argument in the internal frame
|
| + // -----------------------------------
|
| + __ pop(scratch);
|
| + __ add(Operand(esp), Immediate(kPointerSize * 4));
|
| + __ push(scratch);
|
| +}
|
| +
|
| +
|
| +// Generates call to FastHandleApiCall builtin.
|
| +static void GenerateFastApiCall(MacroAssembler* masm,
|
| + const CallOptimization& optimization,
|
| + int argc) {
|
| + // ----------- S t a t e -------------
|
| + // -- esp[0] : return address
|
| + // -- esp[4] : object passing the type check
|
| + // (last fast api call extra argument,
|
| + // set by CheckPrototypes)
|
| + // -- esp[8] : api call data
|
| + // -- esp[12] : api callback
|
| + // -- esp[16] : api function
|
| + // (first fast api call extra argument)
|
| + // -- esp[20] : last argument
|
| + // -- ...
|
| + // -- esp[(argc + 5) * 4] : first argument
|
| + // -- esp[(argc + 6) * 4] : receiver
|
| + // -----------------------------------
|
| +
|
| + // Get the function and setup the context.
|
| + JSFunction* function = optimization.constant_function();
|
| + __ mov(edi, Immediate(Handle<JSFunction>(function)));
|
| + __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
| +
|
| + // Pass the additional arguments FastHandleApiCall expects.
|
| + __ mov(Operand(esp, 4 * kPointerSize), edi);
|
| + bool info_loaded = false;
|
| + Object* callback = optimization.api_call_info()->callback();
|
| + if (Heap::InNewSpace(callback)) {
|
| + info_loaded = true;
|
| + __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
|
| + __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kCallbackOffset));
|
| + __ mov(Operand(esp, 3 * kPointerSize), ebx);
|
| + } else {
|
| + __ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(callback)));
|
| + }
|
| + Object* call_data = optimization.api_call_info()->data();
|
| + if (Heap::InNewSpace(call_data)) {
|
| + if (!info_loaded) {
|
| + __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
|
| + }
|
| + __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset));
|
| + __ mov(Operand(esp, 2 * kPointerSize), ebx);
|
| + } else {
|
| + __ mov(Operand(esp, 2 * kPointerSize),
|
| + Immediate(Handle<Object>(call_data)));
|
| + }
|
| +
|
| + // Set the number of arguments.
|
| + __ mov(eax, Immediate(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(const ParameterCount& arguments, Register name)
|
| - : arguments_(arguments), argc_(arguments.immediate()), name_(name) {}
|
| + 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.
|
| + __ test(receiver, Immediate(kSmiTagMask));
|
| + __ j(zero, miss, not_taken);
|
| +
|
| + CallOptimization optimization(lookup);
|
| +
|
| + if (optimization.is_constant_call() &&
|
| + !Top::CanHaveSpecialFunctions(holder)) {
|
| + 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,
|
| - StubCompiler* stub_compiler,
|
| + JSObject* object,
|
| Register receiver,
|
| - Register holder,
|
| Register scratch1,
|
| Register scratch2,
|
| JSObject* holder_obj,
|
| LookupResult* lookup,
|
| String* name,
|
| + const CallOptimization& optimization,
|
| 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;
|
| + 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);
|
| }
|
|
|
| - if (!optimize) {
|
| - CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label);
|
| - return;
|
| + __ IncrementCounter(&Counters::call_const_interceptor, 1);
|
| +
|
| + if (can_do_fast_api_call) {
|
| + __ IncrementCounter(&Counters::call_const_interceptor_fast_api, 1);
|
| + ReserveSpaceForFastApiCall(masm, scratch1);
|
| }
|
|
|
| - __ EnterInternalFrame();
|
| - __ push(holder); // Save the holder.
|
| - __ push(name_); // Save the name.
|
| + 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);
|
|
|
| - CompileCallLoadPropertyWithInterceptor(masm,
|
| - receiver,
|
| - holder,
|
| - name_,
|
| - holder_obj);
|
| + Label regular_invoke;
|
| + LoadWithInterceptor(masm, receiver, holder, holder_obj, ®ular_invoke);
|
|
|
| - __ pop(name_); // Restore the name.
|
| - __ pop(receiver); // Restore the holder.
|
| - __ LeaveInternalFrame();
|
| + // Generate code for the failed interceptor case.
|
|
|
| - __ cmp(eax, Factory::no_interceptor_result_sentinel());
|
| - Label invoke;
|
| - __ j(not_equal, &invoke);
|
| + // Check the lookup is still valid.
|
| + stub_compiler_->CheckPrototypes(holder_obj, receiver,
|
| + lookup->holder(),
|
| + scratch1, scratch2, name,
|
| + depth2, miss);
|
|
|
| - 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);
|
| + if (can_do_fast_api_call) {
|
| + GenerateFastApiCall(masm, optimization, arguments_.immediate());
|
| + } else {
|
| + __ InvokeFunction(optimization.constant_function(), arguments_,
|
| + JUMP_FUNCTION);
|
| }
|
|
|
| - ASSERT(function->is_compiled());
|
| - // Get the function and setup the context.
|
| - __ mov(edi, Immediate(Handle<JSFunction>(function)));
|
| - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
| + if (can_do_fast_api_call) {
|
| + __ bind(&miss_cleanup);
|
| + FreeSpaceForFastApiCall(masm, scratch1);
|
| + __ jmp(miss_label);
|
| + }
|
|
|
| - // 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);
|
| -
|
| - __ bind(&invoke);
|
| + __ bind(®ular_invoke);
|
| + if (can_do_fast_api_call) {
|
| + FreeSpaceForFastApiCall(masm, scratch1);
|
| + }
|
| }
|
|
|
| void CompileRegular(MacroAssembler* masm,
|
| + JSObject* object,
|
| Register receiver,
|
| - Register holder,
|
| - Register scratch,
|
| + 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);
|
| +
|
| __ EnterInternalFrame();
|
| // Save the name_ register across the call.
|
| __ push(name_);
|
| @@ -571,22 +817,41 @@
|
| name_,
|
| holder_obj);
|
|
|
| - ExternalReference ref = ExternalReference(
|
| - IC_Utility(IC::kLoadPropertyWithInterceptorForCall));
|
| - __ mov(eax, Immediate(5));
|
| - __ mov(ebx, Immediate(ref));
|
| + __ CallExternalReference(
|
| + ExternalReference(
|
| + IC_Utility(IC::kLoadPropertyWithInterceptorForCall)),
|
| + 5);
|
|
|
| - CEntryStub stub(1);
|
| - __ CallStub(&stub);
|
| -
|
| // Restore the name_ register.
|
| __ pop(name_);
|
| __ LeaveInternalFrame();
|
| }
|
|
|
| - private:
|
| + void LoadWithInterceptor(MacroAssembler* masm,
|
| + Register receiver,
|
| + Register holder,
|
| + JSObject* holder_obj,
|
| + 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();
|
| +
|
| + __ cmp(eax, Factory::no_interceptor_result_sentinel());
|
| + __ j(not_equal, interceptor_succeeded);
|
| + }
|
| +
|
| + StubCompiler* stub_compiler_;
|
| const ParameterCount& arguments_;
|
| - int argc_;
|
| Register name_;
|
| };
|
|
|
| @@ -605,8 +870,9 @@
|
| }
|
|
|
|
|
| +// Both name_reg and receiver_reg are preserved on jumps to miss_label,
|
| +// but may be destroyed if store is successful.
|
| void StubCompiler::GenerateStoreField(MacroAssembler* masm,
|
| - Builtins::Name storage_extend,
|
| JSObject* object,
|
| int index,
|
| Map* transition,
|
| @@ -636,9 +902,13 @@
|
| if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
|
| // The properties must be extended before we can store the value.
|
| // We jump to a runtime call that extends the properties array.
|
| - __ mov(ecx, Immediate(Handle<Map>(transition)));
|
| - Handle<Code> ic(Builtins::builtin(storage_extend));
|
| - __ jmp(ic, RelocInfo::CODE_TARGET);
|
| + __ pop(scratch); // Return address.
|
| + __ push(receiver_reg);
|
| + __ push(Immediate(Handle<Map>(transition)));
|
| + __ push(eax);
|
| + __ push(scratch);
|
| + __ TailCallRuntime(
|
| + ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
|
| return;
|
| }
|
|
|
| @@ -691,10 +961,12 @@
|
| Register holder_reg,
|
| Register scratch,
|
| String* name,
|
| + int push_at_depth,
|
| Label* miss) {
|
| // 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,
|
| + push_at_depth, miss);
|
|
|
| // If we've skipped any global objects, it's not enough to verify
|
| // that their maps haven't changed.
|
| @@ -716,7 +988,7 @@
|
| object = JSObject::cast(object->GetPrototype());
|
| }
|
|
|
| - // Return the register containin the holder.
|
| + // Return the register containing the holder.
|
| return result;
|
| }
|
|
|
| @@ -887,7 +1159,7 @@
|
| }
|
|
|
|
|
| -Object* CallStubCompiler::CompileCallField(Object* object,
|
| +Object* CallStubCompiler::CompileCallField(JSObject* object,
|
| JSObject* holder,
|
| int index,
|
| String* name) {
|
| @@ -909,9 +1181,7 @@
|
| __ j(zero, &miss, not_taken);
|
|
|
| // Do the right check and compute the holder register.
|
| - Register reg =
|
| - CheckPrototypes(JSObject::cast(object), edx, holder,
|
| - ebx, eax, name, &miss);
|
| + Register reg = CheckPrototypes(object, edx, holder, ebx, eax, name, &miss);
|
|
|
| GenerateFastPropertyLoad(masm(), edi, reg, holder, index);
|
|
|
| @@ -969,15 +1239,31 @@
|
| // unless we're doing a receiver map check.
|
| ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
|
|
|
| + CallOptimization optimization(function);
|
| + int depth = kInvalidProtoDepth;
|
| +
|
| switch (check) {
|
| case RECEIVER_MAP_CHECK:
|
| + __ IncrementCounter(&Counters::call_const, 1);
|
| +
|
| + 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);
|
| + ReserveSpaceForFastApiCall(masm(), eax);
|
| + }
|
| +
|
| // Check that the maps haven't changed.
|
| CheckPrototypes(JSObject::cast(object), edx, holder,
|
| - ebx, eax, name, &miss);
|
| + ebx, eax, name, depth, &miss);
|
|
|
| // Patch the receiver on the stack with the global proxy if
|
| // necessary.
|
| if (object->IsGlobalObject()) {
|
| + ASSERT(depth == kInvalidProtoDepth);
|
| __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
|
| __ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
|
| }
|
| @@ -1062,19 +1348,17 @@
|
| UNREACHABLE();
|
| }
|
|
|
| - // Get the function and setup the context.
|
| - __ mov(edi, Immediate(Handle<JSFunction>(function)));
|
| - __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
|
| + if (depth != kInvalidProtoDepth) {
|
| + GenerateFastApiCall(masm(), optimization, argc);
|
| + } else {
|
| + __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
|
| + }
|
|
|
| - // 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);
|
| -
|
| // Handle call cache miss.
|
| __ bind(&miss);
|
| + if (depth != kInvalidProtoDepth) {
|
| + FreeSpaceForFastApiCall(masm(), eax);
|
| + }
|
| Handle<Code> ic = ComputeCallMiss(arguments().immediate());
|
| __ jmp(ic, RelocInfo::CODE_TARGET);
|
|
|
| @@ -1087,7 +1371,7 @@
|
| }
|
|
|
|
|
| -Object* CallStubCompiler::CompileCallInterceptor(Object* object,
|
| +Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
|
| JSObject* holder,
|
| String* name) {
|
| // ----------- S t a t e -------------
|
| @@ -1108,18 +1392,16 @@
|
| // Get the receiver from the stack.
|
| __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
|
|
| - CallInterceptorCompiler compiler(arguments(), ecx);
|
| - CompileLoadInterceptor(&compiler,
|
| - this,
|
| - masm(),
|
| - JSObject::cast(object),
|
| - holder,
|
| - name,
|
| - &lookup,
|
| - edx,
|
| - ebx,
|
| - edi,
|
| - &miss);
|
| + CallInterceptorCompiler compiler(this, arguments(), ecx);
|
| + compiler.Compile(masm(),
|
| + object,
|
| + holder,
|
| + name,
|
| + &lookup,
|
| + edx,
|
| + ebx,
|
| + edi,
|
| + &miss);
|
|
|
| // Restore receiver.
|
| __ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
|
| @@ -1249,7 +1531,6 @@
|
|
|
| // Generate store field code. Trashes the name register.
|
| GenerateStoreField(masm(),
|
| - Builtins::StoreIC_ExtendStorage,
|
| object,
|
| index,
|
| transition,
|
| @@ -1423,15 +1704,14 @@
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| // Get the object from the stack.
|
| - __ mov(ebx, Operand(esp, 2 * kPointerSize));
|
| + __ mov(edx, Operand(esp, 2 * kPointerSize));
|
|
|
| // Generate store field code. Trashes the name register.
|
| GenerateStoreField(masm(),
|
| - Builtins::KeyedStoreIC_ExtendStorage,
|
| object,
|
| index,
|
| transition,
|
| - ebx, ecx, edx,
|
| + edx, ecx, ebx,
|
| &miss);
|
|
|
| // Handle store cache miss.
|
| @@ -1451,13 +1731,12 @@
|
| int index,
|
| String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : receiver
|
| // -- ecx : name
|
| // -- esp[0] : return address
|
| - // -- esp[4] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| GenerateLoadField(object, holder, eax, ebx, edx, index, name, &miss);
|
| __ bind(&miss);
|
| GenerateLoadMiss(masm(), Code::LOAD_IC);
|
| @@ -1472,13 +1751,12 @@
|
| JSObject* holder,
|
| AccessorInfo* callback) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : receiver
|
| // -- ecx : name
|
| // -- esp[0] : return address
|
| - // -- esp[4] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| Failure* failure = Failure::InternalError();
|
| bool success = GenerateLoadCallback(object, holder, eax, ecx, ebx, edx,
|
| callback, name, &miss, &failure);
|
| @@ -1497,13 +1775,12 @@
|
| Object* value,
|
| String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : receiver
|
| // -- ecx : name
|
| // -- esp[0] : return address
|
| - // -- esp[4] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| GenerateLoadConstant(object, holder, eax, ebx, edx, value, name, &miss);
|
| __ bind(&miss);
|
| GenerateLoadMiss(masm(), Code::LOAD_IC);
|
| @@ -1517,16 +1794,15 @@
|
| JSObject* holder,
|
| String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : receiver
|
| // -- ecx : name
|
| // -- esp[0] : return address
|
| - // -- esp[4] : receiver
|
| // -----------------------------------
|
| 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,
|
| @@ -1553,15 +1829,12 @@
|
| String* name,
|
| bool is_dont_delete) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : receiver
|
| // -- ecx : name
|
| // -- esp[0] : return address
|
| - // -- esp[4] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - // Get the receiver from the stack.
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| -
|
| // If the object is the holder then we know that it's a global
|
| // object which can only happen for contextual loads. In this case,
|
| // the receiver cannot be a smi.
|
| @@ -1574,19 +1847,20 @@
|
| CheckPrototypes(object, eax, holder, ebx, edx, name, &miss);
|
|
|
| // Get the value from the cell.
|
| - __ mov(eax, Immediate(Handle<JSGlobalPropertyCell>(cell)));
|
| - __ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset));
|
| + __ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell)));
|
| + __ mov(ebx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
|
|
|
| // Check for deleted property if property can actually be deleted.
|
| if (!is_dont_delete) {
|
| - __ cmp(eax, Factory::the_hole_value());
|
| + __ cmp(ebx, Factory::the_hole_value());
|
| __ j(equal, &miss, not_taken);
|
| } else if (FLAG_debug_code) {
|
| - __ cmp(eax, Factory::the_hole_value());
|
| + __ cmp(ebx, Factory::the_hole_value());
|
| __ Check(not_equal, "DontDelete cells can't contain the hole");
|
| }
|
|
|
| __ IncrementCounter(&Counters::named_load_global_inline, 1);
|
| + __ mov(eax, ebx);
|
| __ ret(0);
|
|
|
| __ bind(&miss);
|
| @@ -1603,21 +1877,19 @@
|
| JSObject* holder,
|
| int index) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_field, 1);
|
|
|
| // Check that the name has not changed.
|
| __ cmp(Operand(eax), Immediate(Handle<String>(name)));
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| - GenerateLoadField(receiver, holder, ecx, ebx, edx, index, name, &miss);
|
| + GenerateLoadField(receiver, holder, edx, ebx, ecx, index, name, &miss);
|
|
|
| __ bind(&miss);
|
| __ DecrementCounter(&Counters::keyed_load_field, 1);
|
| @@ -1633,14 +1905,12 @@
|
| JSObject* holder,
|
| AccessorInfo* callback) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_callback, 1);
|
|
|
| // Check that the name has not changed.
|
| @@ -1648,7 +1918,7 @@
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| Failure* failure = Failure::InternalError();
|
| - bool success = GenerateLoadCallback(receiver, holder, ecx, eax, ebx, edx,
|
| + bool success = GenerateLoadCallback(receiver, holder, edx, eax, ebx, ecx,
|
| callback, name, &miss, &failure);
|
| if (!success) return failure;
|
|
|
| @@ -1666,21 +1936,19 @@
|
| JSObject* holder,
|
| Object* value) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_constant_function, 1);
|
|
|
| // Check that the name has not changed.
|
| __ cmp(Operand(eax), Immediate(Handle<String>(name)));
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| - GenerateLoadConstant(receiver, holder, ecx, ebx, edx,
|
| + GenerateLoadConstant(receiver, holder, edx, ebx, ecx,
|
| value, name, &miss);
|
| __ bind(&miss);
|
| __ DecrementCounter(&Counters::keyed_load_constant_function, 1);
|
| @@ -1695,14 +1963,12 @@
|
| JSObject* holder,
|
| String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_interceptor, 1);
|
|
|
| // Check that the name has not changed.
|
| @@ -1714,9 +1980,9 @@
|
| GenerateLoadInterceptor(receiver,
|
| holder,
|
| &lookup,
|
| + edx,
|
| + eax,
|
| ecx,
|
| - eax,
|
| - edx,
|
| ebx,
|
| name,
|
| &miss);
|
| @@ -1733,21 +1999,19 @@
|
|
|
| Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_array_length, 1);
|
|
|
| // Check that the name has not changed.
|
| __ cmp(Operand(eax), Immediate(Handle<String>(name)));
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| - GenerateLoadArrayLength(masm(), ecx, edx, &miss);
|
| + GenerateLoadArrayLength(masm(), edx, ecx, &miss);
|
| __ bind(&miss);
|
| __ DecrementCounter(&Counters::keyed_load_array_length, 1);
|
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
|
| @@ -1759,21 +2023,19 @@
|
|
|
| Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_string_length, 1);
|
|
|
| // Check that the name has not changed.
|
| __ cmp(Operand(eax), Immediate(Handle<String>(name)));
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| - GenerateLoadStringLength(masm(), ecx, edx, &miss);
|
| + GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss);
|
| __ bind(&miss);
|
| __ DecrementCounter(&Counters::keyed_load_string_length, 1);
|
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
|
| @@ -1785,21 +2047,19 @@
|
|
|
| Object* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
|
| // ----------- S t a t e -------------
|
| + // -- eax : key
|
| + // -- edx : receiver
|
| // -- esp[0] : return address
|
| - // -- esp[4] : name
|
| - // -- esp[8] : receiver
|
| // -----------------------------------
|
| Label miss;
|
|
|
| - __ mov(eax, Operand(esp, kPointerSize));
|
| - __ mov(ecx, Operand(esp, 2 * kPointerSize));
|
| __ IncrementCounter(&Counters::keyed_load_function_prototype, 1);
|
|
|
| // Check that the name has not changed.
|
| __ cmp(Operand(eax), Immediate(Handle<String>(name)));
|
| __ j(not_equal, &miss, not_taken);
|
|
|
| - GenerateLoadFunctionPrototype(masm(), ecx, edx, ebx, &miss);
|
| + GenerateLoadFunctionPrototype(masm(), edx, ecx, ebx, &miss);
|
| __ bind(&miss);
|
| __ DecrementCounter(&Counters::keyed_load_function_prototype, 1);
|
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
|
|
|