Index: src/x64/stub-cache-x64.cc |
=================================================================== |
--- src/x64/stub-cache-x64.cc (revision 4439) |
+++ src/x64/stub-cache-x64.cc (working copy) |
@@ -555,76 +555,232 @@ |
}; |
+// 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 ------------- |
+ // -- rsp[0] : return address |
+ // -- rsp[8] : last argument in the internal frame of the caller |
+ // ----------------------------------- |
+ __ pop(scratch); |
+ __ Push(Smi::FromInt(0)); |
+ __ Push(Smi::FromInt(0)); |
+ __ Push(Smi::FromInt(0)); |
+ __ Push(Smi::FromInt(0)); |
+ __ push(scratch); |
+} |
+ |
+ |
+// Undoes the effects of ReserveSpaceForFastApiCall. |
+static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) { |
+ // ----------- S t a t e ------------- |
+ // -- rsp[0] : return address |
+ // -- rsp[8] : last fast api call extra argument |
+ // -- ... |
+ // -- rsp[32] : first fast api call extra argument |
+ // -- rsp[40] : last argument in the internal frame |
+ // ----------------------------------- |
+ __ pop(scratch); |
+ __ Drop(4); |
+ __ push(scratch); |
+} |
+ |
+ |
+// Generates call to FastHandleApiCall builtin. |
+static void GenerateFastApiCall(MacroAssembler* masm, |
+ const CallOptimization& optimization, |
+ int argc) { |
+ // ----------- S t a t e ------------- |
+ // -- rsp[0] : return address |
+ // -- rsp[8] : object passing the type check |
+ // (last fast api call extra argument, |
+ // set by CheckPrototypes) |
+ // -- rsp[16] : api call data |
+ // -- rsp[24] : api callback |
+ // -- rsp[32] : api function |
+ // (first fast api call extra argument) |
+ // -- rsp[40] : last argument |
+ // -- ... |
+ // -- rsp[(argc + 5) * 8] : first argument |
+ // -- rsp[(argc + 6) * 8] : receiver |
+ // ----------------------------------- |
+ |
+ // Get the function and setup the context. |
+ JSFunction* function = optimization.constant_function(); |
+ __ Move(rdi, Handle<JSFunction>(function)); |
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
+ |
+ // Pass the additional arguments FastHandleApiCall expects. |
+ __ movq(Operand(rsp, 4 * kPointerSize), rdi); |
+ bool info_loaded = false; |
+ Object* callback = optimization.api_call_info()->callback(); |
+ if (Heap::InNewSpace(callback)) { |
+ info_loaded = true; |
+ __ Move(rcx, Handle<CallHandlerInfo>(optimization.api_call_info())); |
+ __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kCallbackOffset)); |
+ __ movq(Operand(rsp, 3 * kPointerSize), rbx); |
+ } else { |
+ __ Move(Operand(rsp, 3 * kPointerSize), Handle<Object>(callback)); |
+ } |
+ Object* call_data = optimization.api_call_info()->data(); |
+ if (Heap::InNewSpace(call_data)) { |
+ if (!info_loaded) { |
+ __ Move(rcx, Handle<CallHandlerInfo>(optimization.api_call_info())); |
+ } |
+ __ movq(rbx, FieldOperand(rcx, CallHandlerInfo::kDataOffset)); |
+ __ movq(Operand(rsp, 2 * kPointerSize), rbx); |
+ } else { |
+ __ Move(Operand(rsp, 2 * kPointerSize), Handle<Object>(call_data)); |
+ } |
+ |
+ // Set the number of arguments. |
+ __ movq(rax, 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), 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. |
+ __ JumpIfSmi(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, |
- 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); |
} |
- ASSERT(!lookup->holder()->IsGlobalObject()); |
+ 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); |
- __ EnterInternalFrame(); |
- __ push(holder); // Save the holder. |
- __ push(name_); // Save the name. |
+ Label regular_invoke; |
+ LoadWithInterceptor(masm, receiver, holder, holder_obj, ®ular_invoke); |
- CompileCallLoadPropertyWithInterceptor(masm, |
- receiver, |
- holder, |
- name_, |
- holder_obj); |
+ // Generate code for the failed interceptor case. |
- __ pop(name_); // Restore the name. |
- __ pop(receiver); // Restore the holder. |
- __ LeaveInternalFrame(); |
+ // Check the lookup is still valid. |
+ stub_compiler_->CheckPrototypes(holder_obj, receiver, |
+ lookup->holder(), |
+ scratch1, scratch2, name, |
+ depth2, miss); |
- __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); |
- Label invoke; |
- __ j(not_equal, &invoke); |
+ if (can_do_fast_api_call) { |
+ GenerateFastApiCall(masm, optimization, arguments_.immediate()); |
+ } else { |
+ __ InvokeFunction(optimization.constant_function(), arguments_, |
+ JUMP_FUNCTION); |
+ } |
- stub_compiler->CheckPrototypes(holder_obj, receiver, |
- lookup->holder(), scratch1, |
- scratch2, |
- name, |
- miss_label); |
+ if (can_do_fast_api_call) { |
+ __ bind(&miss_cleanup); |
+ FreeSpaceForFastApiCall(masm, scratch1); |
+ __ jmp(miss_label); |
+ } |
- __ InvokeFunction(function, arguments_, 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_); |
@@ -639,11 +795,35 @@ |
ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall)), |
5); |
+ // 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(); |
+ |
+ __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); |
+ __ j(not_equal, interceptor_succeeded); |
+ } |
+ |
+ StubCompiler* stub_compiler_; |
const ParameterCount& arguments_; |
Register name_; |
}; |
@@ -808,7 +988,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(); |
@@ -816,22 +996,39 @@ |
// Check that the receiver isn't a smi. |
if (check != NUMBER_CHECK) { |
- __ JumpIfSmi(rdx, &miss); |
+ __ JumpIfSmi(rdx, &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); |
+ |
+ 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(), rax); |
+ } |
+ |
// Check that the maps haven't changed. |
CheckPrototypes(JSObject::cast(object), rdx, holder, |
- rbx, rax, name, &miss); |
+ rbx, rax, name, depth, &miss); |
// Patch the receiver on the stack with the global proxy if |
// necessary. |
if (object->IsGlobalObject()) { |
+ ASSERT(depth == kInvalidProtoDepth); |
__ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); |
__ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); |
} |
@@ -901,10 +1098,20 @@ |
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(), rax); |
+ } |
+ |
+ // Handle call cache miss. |
+ __ bind(&miss_in_smi_check); |
Handle<Code> ic = ComputeCallMiss(arguments().immediate()); |
__ Jump(ic, RelocInfo::CODE_TARGET); |
@@ -992,18 +1199,16 @@ |
// Get the receiver from the stack. |
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); |
- CallInterceptorCompiler compiler(arguments(), rcx); |
- CompileLoadInterceptor(&compiler, |
- this, |
- masm(), |
- object, |
- holder, |
- name, |
- &lookup, |
- rdx, |
- rbx, |
- rdi, |
- &miss); |
+ CallInterceptorCompiler compiler(this, arguments(), rcx); |
+ compiler.Compile(masm(), |
+ object, |
+ holder, |
+ name, |
+ &lookup, |
+ rdx, |
+ rbx, |
+ rdi, |
+ &miss); |
// Restore receiver. |
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); |
@@ -1819,12 +2024,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 = |
- __ CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); |
+ __ 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 |