From c28bde775c0fa1028d0dcc0625e898f7cbc2af37 Mon Sep 17 00:00:00 2001 From: "whesse@chromium.org" Date: Tue, 4 Jan 2011 09:02:53 +0000 Subject: [PATCH] Reorder the functions in stub-cache-x64.cc, so they are in the same order as in stub-cache-ia32.cc. Review URL: http://codereview.chromium.org/6085006 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@6140 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ia32/stub-cache-ia32.cc | 1 + src/x64/stub-cache-x64.cc | 3396 +++++++++++++++++++++---------------------- 2 files changed, 1697 insertions(+), 1700 deletions(-) diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 99888b0..bcb02ed 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -1686,6 +1686,7 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( Label miss; Label index_out_of_range; + GenerateNameCheck(name, &miss); // Check that the maps starting from the prototype haven't changed. diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index dc81fa1..57cba14 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -25,23 +25,17 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - #include "v8.h" #if defined(V8_TARGET_ARCH_X64) #include "ic-inl.h" -#include "code-stubs.h" #include "codegen-inl.h" #include "stub-cache.h" -#include "macro-assembler.h" namespace v8 { namespace internal { -//----------------------------------------------------------------------------- -// StubCompiler static helper functions - #define __ ACCESS_MASM(masm) @@ -182,92 +176,6 @@ static void GenerateDictionaryNegativeLookup(MacroAssembler* masm, } -void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { - ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); - Code* code = NULL; - if (kind == Code::LOAD_IC) { - code = Builtins::builtin(Builtins::LoadIC_Miss); - } else { - code = Builtins::builtin(Builtins::KeyedLoadIC_Miss); - } - - Handle ic(code); - __ Jump(ic, RelocInfo::CODE_TARGET); -} - - -void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, - int index, - Register prototype) { - // Load the global or builtins object from the current context. - __ movq(prototype, - Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); - // Load the global context from the global or builtins object. - __ movq(prototype, - FieldOperand(prototype, GlobalObject::kGlobalContextOffset)); - // Load the function from the global context. - __ movq(prototype, Operand(prototype, Context::SlotOffset(index))); - // Load the initial map. The global functions all have initial maps. - __ movq(prototype, - FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); - // Load the prototype from the initial map. - __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); -} - - -void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( - MacroAssembler* masm, int index, Register prototype, Label* miss) { - // Check we're still in the same context. - __ Move(prototype, Top::global()); - __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)), - prototype); - __ j(not_equal, miss); - // Get the global function with the given index. - JSFunction* function = JSFunction::cast(Top::global_context()->get(index)); - // Load its initial map. The global functions all have initial maps. - __ Move(prototype, Handle(function->initial_map())); - // Load the prototype from the initial map. - __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); -} - - -// Load a fast property out of a holder object (src). In-object properties -// are loaded directly otherwise the property is loaded from the properties -// fixed array. -void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, - Register dst, Register src, - JSObject* holder, int index) { - // Adjust for the number of properties stored in the holder. - index -= holder->map()->inobject_properties(); - if (index < 0) { - // Get the property straight out of the holder. - int offset = holder->map()->instance_size() + (index * kPointerSize); - __ movq(dst, FieldOperand(src, offset)); - } else { - // Calculate the offset into the properties array. - int offset = index * kPointerSize + FixedArray::kHeaderSize; - __ movq(dst, FieldOperand(src, JSObject::kPropertiesOffset)); - __ movq(dst, FieldOperand(dst, offset)); - } -} - - -static void PushInterceptorArguments(MacroAssembler* masm, - Register receiver, - Register holder, - Register name, - JSObject* holder_obj) { - __ push(name); - InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); - ASSERT(!Heap::InNewSpace(interceptor)); - __ Move(kScratchRegister, Handle(interceptor)); - __ push(kScratchRegister); - __ push(receiver); - __ push(holder); - __ push(FieldOperand(kScratchRegister, InterceptorInfo::kDataOffset)); -} - - void StubCache::GenerateProbe(MacroAssembler* masm, Code::Flags flags, Register receiver, @@ -324,83 +232,38 @@ void StubCache::GenerateProbe(MacroAssembler* masm, } -// 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, - JSObject* object, - int index, - Map* transition, - Register receiver_reg, - Register name_reg, - Register scratch, - Label* miss_label) { - // Check that the object isn't a smi. - __ JumpIfSmi(receiver_reg, miss_label); - - // Check that the map of the object hasn't changed. - __ Cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset), - Handle(object->map())); - __ j(not_equal, miss_label); - - // Perform global security token check if needed. - if (object->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label); - } - - // Stub never generated for non-global objects that require access - // checks. - ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); - - // Perform map transition for the receiver if necessary. - 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. - __ pop(scratch); // Return address. - __ push(receiver_reg); - __ Push(Handle(transition)); - __ push(rax); - __ push(scratch); - __ TailCallExternalReference( - ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1); - return; - } - - if (transition != NULL) { - // Update the map of the object; no write barrier updating is - // needed because the map is never in new space. - __ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset), - Handle(transition)); - } - - // Adjust for the number of properties stored in the object. Even in the - // face of a transition we can use the old map here because the size of the - // object and the number of in-object properties is not going to change. - index -= object->map()->inobject_properties(); - - if (index < 0) { - // Set the property straight into the object. - int offset = object->map()->instance_size() + (index * kPointerSize); - __ movq(FieldOperand(receiver_reg, offset), rax); - - // Update the write barrier for the array address. - // Pass the value being stored in the now unused name_reg. - __ movq(name_reg, rax); - __ RecordWrite(receiver_reg, offset, name_reg, scratch); - } else { - // Write to the properties array. - int offset = index * kPointerSize + FixedArray::kHeaderSize; - // Get the properties array (optimistically). - __ movq(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); - __ movq(FieldOperand(scratch, offset), rax); +void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, + int index, + Register prototype) { + // Load the global or builtins object from the current context. + __ movq(prototype, + Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); + // Load the global context from the global or builtins object. + __ movq(prototype, + FieldOperand(prototype, GlobalObject::kGlobalContextOffset)); + // Load the function from the global context. + __ movq(prototype, Operand(prototype, Context::SlotOffset(index))); + // Load the initial map. The global functions all have initial maps. + __ movq(prototype, + FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); + // Load the prototype from the initial map. + __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); +} - // Update the write barrier for the array address. - // Pass the value being stored in the now unused name_reg. - __ movq(name_reg, rax); - __ RecordWrite(scratch, offset, name_reg, receiver_reg); - } - // Return the value (register rax). - __ ret(0); +void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype( + MacroAssembler* masm, int index, Register prototype, Label* miss) { + // Check we're still in the same context. + __ Move(prototype, Top::global()); + __ cmpq(Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)), + prototype); + __ j(not_equal, miss); + // Get the global function with the given index. + JSFunction* function = JSFunction::cast(Top::global_context()->get(index)); + // Load its initial map. The global functions all have initial maps. + __ Move(prototype, Handle(function->initial_map())); + // Load the prototype from the initial map. + __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); } @@ -469,6 +332,54 @@ void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm, } +void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, + Register receiver, + Register result, + Register scratch, + Label* miss_label) { + __ TryGetFunctionPrototype(receiver, result, miss_label); + if (!result.is(rax)) __ movq(rax, result); + __ ret(0); +} + + +// Load a fast property out of a holder object (src). In-object properties +// are loaded directly otherwise the property is loaded from the properties +// fixed array. +void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm, + Register dst, Register src, + JSObject* holder, int index) { + // Adjust for the number of properties stored in the holder. + index -= holder->map()->inobject_properties(); + if (index < 0) { + // Get the property straight out of the holder. + int offset = holder->map()->instance_size() + (index * kPointerSize); + __ movq(dst, FieldOperand(src, offset)); + } else { + // Calculate the offset into the properties array. + int offset = index * kPointerSize + FixedArray::kHeaderSize; + __ movq(dst, FieldOperand(src, JSObject::kPropertiesOffset)); + __ movq(dst, FieldOperand(dst, offset)); + } +} + + +static void PushInterceptorArguments(MacroAssembler* masm, + Register receiver, + Register holder, + Register name, + JSObject* holder_obj) { + __ push(name); + InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor(); + ASSERT(!Heap::InNewSpace(interceptor)); + __ Move(kScratchRegister, Handle(interceptor)); + __ push(kScratchRegister); + __ push(receiver); + __ push(holder); + __ push(FieldOperand(kScratchRegister, InterceptorInfo::kDataOffset)); +} + + static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, Register receiver, Register holder, @@ -486,20 +397,10 @@ static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm, } - -void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm, - Register receiver, - Register result, - Register scratch, - Label* miss_label) { - __ TryGetFunctionPrototype(receiver, result, miss_label); - if (!result.is(rax)) __ movq(rax, result); - __ ret(0); -} - // Number of pointers to be reserved on stack for fast API call. static const int kFastApiCallArguments = 3; + // Reserves space for the extra arguments to API function in the // caller's frame. // @@ -553,7 +454,6 @@ static bool GenerateFastApiCall(MacroAssembler* masm, // -- rsp[(argc + 3) * 8] : first argument // -- rsp[(argc + 4) * 8] : receiver // ----------------------------------- - // Get the function and setup the context. JSFunction* function = optimization.constant_function(); __ Move(rdi, Handle(function)); @@ -833,8 +733,102 @@ class CallInterceptorCompiler BASE_EMBEDDED { }; -// Generate code to check that a global property cell is empty. Create -// the property cell at compilation time if no cell exists for the +void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) { + ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC); + Code* code = NULL; + if (kind == Code::LOAD_IC) { + code = Builtins::builtin(Builtins::LoadIC_Miss); + } else { + code = Builtins::builtin(Builtins::KeyedLoadIC_Miss); + } + + Handle ic(code); + __ Jump(ic, RelocInfo::CODE_TARGET); +} + + +// 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, + JSObject* object, + int index, + Map* transition, + Register receiver_reg, + Register name_reg, + Register scratch, + Label* miss_label) { + // Check that the object isn't a smi. + __ JumpIfSmi(receiver_reg, miss_label); + + // Check that the map of the object hasn't changed. + __ Cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset), + Handle(object->map())); + __ j(not_equal, miss_label); + + // Perform global security token check if needed. + if (object->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(receiver_reg, scratch, miss_label); + } + + // Stub never generated for non-global objects that require access + // checks. + ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); + + // Perform map transition for the receiver if necessary. + 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. + __ pop(scratch); // Return address. + __ push(receiver_reg); + __ Push(Handle(transition)); + __ push(rax); + __ push(scratch); + __ TailCallExternalReference( + ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1); + return; + } + + if (transition != NULL) { + // Update the map of the object; no write barrier updating is + // needed because the map is never in new space. + __ Move(FieldOperand(receiver_reg, HeapObject::kMapOffset), + Handle(transition)); + } + + // Adjust for the number of properties stored in the object. Even in the + // face of a transition we can use the old map here because the size of the + // object and the number of in-object properties is not going to change. + index -= object->map()->inobject_properties(); + + if (index < 0) { + // Set the property straight into the object. + int offset = object->map()->instance_size() + (index * kPointerSize); + __ movq(FieldOperand(receiver_reg, offset), rax); + + // Update the write barrier for the array address. + // Pass the value being stored in the now unused name_reg. + __ movq(name_reg, rax); + __ RecordWrite(receiver_reg, offset, name_reg, scratch); + } else { + // Write to the properties array. + int offset = index * kPointerSize + FixedArray::kHeaderSize; + // Get the properties array (optimistically). + __ movq(scratch, FieldOperand(receiver_reg, JSObject::kPropertiesOffset)); + __ movq(FieldOperand(scratch, offset), rax); + + // Update the write barrier for the array address. + // Pass the value being stored in the now unused name_reg. + __ movq(name_reg, rax); + __ RecordWrite(scratch, offset, name_reg, receiver_reg); + } + + // Return the value (register rax). + __ ret(0); +} + + +// 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. MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( MacroAssembler* masm, @@ -857,692 +851,537 @@ MUST_USE_RESULT static MaybeObject* GenerateCheckPropertyCell( #undef __ - #define __ ACCESS_MASM((masm())) -void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { - if (kind_ == Code::KEYED_CALL_IC) { - __ Cmp(rcx, Handle(name)); - __ j(not_equal, miss); - } -} +Register StubCompiler::CheckPrototypes(JSObject* object, + Register object_reg, + JSObject* holder, + Register holder_reg, + Register scratch1, + Register scratch2, + String* name, + int save_at_depth, + Label* miss) { + // Make sure there's no overlap between holder and object registers. + ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); + ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) + && !scratch2.is(scratch1)); + // Keep track of the current object in register reg. On the first + // iteration, reg is an alias for object_reg, on later iterations, + // it is an alias for holder_reg. + Register reg = object_reg; + int depth = 0; -void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, - JSObject* holder, - String* name, - Label* miss) { - ASSERT(holder->IsGlobalObject()); + if (save_at_depth == depth) { + __ movq(Operand(rsp, kPointerSize), object_reg); + } - // Get the number of arguments. - const int argc = arguments().immediate(); + // Check the maps in the prototype chain. + // Traverse the prototype chain from the object and do map checks. + JSObject* current = object; + while (current != holder) { + depth++; - // Get the receiver from the stack. - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + // Only global objects and objects that do not require access + // checks are allowed in stubs. + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); - // If the object is the holder then we know that it's a global - // object which can only happen for contextual calls. In this case, - // the receiver cannot be a smi. - if (object != holder) { - __ JumpIfSmi(rdx, miss); - } + JSObject* prototype = JSObject::cast(current->GetPrototype()); + if (!current->HasFastProperties() && + !current->IsJSGlobalObject() && + !current->IsJSGlobalProxy()) { + if (!name->IsSymbol()) { + MaybeObject* lookup_result = Heap::LookupSymbol(name); + if (lookup_result->IsFailure()) { + set_failure(Failure::cast(lookup_result)); + return reg; + } else { + name = String::cast(lookup_result->ToObjectUnchecked()); + } + } + ASSERT(current->property_dictionary()->FindEntry(name) == + StringDictionary::kNotFound); - // Check that the maps haven't changed. - CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, miss); -} + GenerateDictionaryNegativeLookup(masm(), + miss, + reg, + name, + scratch1, + scratch2); + __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + reg = holder_reg; // from now the object is in holder_reg + __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + } else if (Heap::InNewSpace(prototype)) { + // Get the map of the current object. + __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + __ Cmp(scratch1, Handle(current->map())); + // Branch on the result of the map check. + __ j(not_equal, miss); + // Check access rights to the global object. This has to happen + // after the map check so that we know that the object is + // actually a global object. + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch1, miss); + // Restore scratch register to be the map of the object. + // We load the prototype from the map in the scratch register. + __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); + } + // The prototype is in new space; we cannot store a reference + // to it in the code. Load it from the map. + reg = holder_reg; // from now the object is in holder_reg + __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); -void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, - JSFunction* function, - Label* miss) { - // Get the value from the cell. - __ Move(rdi, Handle(cell)); - __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset)); + } else { + // Check the map of the current object. + __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), + Handle(current->map())); + // Branch on the result of the map check. + __ j(not_equal, miss); + // Check access rights to the global object. This has to happen + // after the map check so that we know that the object is + // actually a global object. + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch1, miss); + } + // The prototype is in old space; load it directly. + reg = holder_reg; // from now the object is in holder_reg + __ Move(reg, Handle(prototype)); + } - // Check that the cell contains the same function. - if (Heap::InNewSpace(function)) { - // We can't embed a pointer to a function in new space so we have - // to verify that the shared function info is unchanged. This has - // the nice side effect that multiple closures based on the same - // function can all use this call IC. Before we load through the - // function, we have to verify that it still is a function. - __ JumpIfSmi(rdi, miss); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax); - __ j(not_equal, miss); + if (save_at_depth == depth) { + __ movq(Operand(rsp, kPointerSize), reg); + } - // Check the shared function info. Make sure it hasn't changed. - __ Move(rax, Handle(function->shared())); - __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax); - __ j(not_equal, miss); - } else { - __ Cmp(rdi, Handle(function)); - __ j(not_equal, miss); + // Go to the next object in the prototype chain. + current = prototype; } -} + // Check the holder map. + __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle(holder->map())); + __ j(not_equal, miss); -MaybeObject* CallStubCompiler::GenerateMissBranch() { - MaybeObject* maybe_obj = - StubCache::ComputeCallMiss(arguments().immediate(), kind_); - Object* obj; - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - __ Jump(Handle(Code::cast(obj)), RelocInfo::CODE_TARGET); - return obj; -} - + // Log the check depth. + LOG(IntEvent("check-maps-depth", depth + 1)); -MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, - JSObject* holder, - JSFunction* function, - String* name, - CheckType check) { - // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver - // ----------------------------------- - - SharedFunctionInfo* function_info = function->shared(); - if (function_info->HasBuiltinFunctionId()) { - BuiltinFunctionId id = function_info->builtin_function_id(); - MaybeObject* maybe_result = CompileCustomCall( - id, object, holder, NULL, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; - } - - Label miss_in_smi_check; - - GenerateNameCheck(name, &miss_in_smi_check); - - // Get the receiver from the stack. - const int argc = arguments().immediate(); - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - - // Check that the receiver isn't a smi. - if (check != NUMBER_CHECK) { - __ JumpIfSmi(rdx, &miss_in_smi_check); + // Perform security check for access to the global object and return + // the holder register. + ASSERT(current == holder); + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); + if (current->IsJSGlobalProxy()) { + __ CheckAccessGlobalProxy(reg, scratch1, miss); } - // 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); - // Allocate space for v8::Arguments implicit values. Must be initialized - // before to call any runtime function. - __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); - } - - // Check that the maps haven't changed. - CheckPrototypes(JSObject::cast(object), rdx, holder, - rbx, rax, rdi, 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); - } - break; - - case STRING_CHECK: - if (!function->IsBuiltin()) { - // Calling non-builtins with a value as receiver requires boxing. - __ jmp(&miss); - } else { - // Check that the object is a two-byte string or a symbol. - __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax); - __ j(above_equal, &miss); - // Check that the maps starting from the prototype haven't changed. - GenerateDirectLoadGlobalFunctionPrototype( - masm(), Context::STRING_FUNCTION_INDEX, rax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); - } - break; - - case NUMBER_CHECK: { - if (!function->IsBuiltin()) { - // Calling non-builtins with a value as receiver requires boxing. - __ jmp(&miss); - } else { - Label fast; - // Check that the object is a smi or a heap number. - __ JumpIfSmi(rdx, &fast); - __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax); - __ j(not_equal, &miss); - __ bind(&fast); - // Check that the maps starting from the prototype haven't changed. - GenerateDirectLoadGlobalFunctionPrototype( - masm(), Context::NUMBER_FUNCTION_INDEX, rax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); - } - break; - } - - case BOOLEAN_CHECK: { - if (!function->IsBuiltin()) { - // Calling non-builtins with a value as receiver requires boxing. - __ jmp(&miss); - } else { - Label fast; - // Check that the object is a boolean. - __ CompareRoot(rdx, Heap::kTrueValueRootIndex); - __ j(equal, &fast); - __ CompareRoot(rdx, Heap::kFalseValueRootIndex); - __ j(not_equal, &miss); - __ bind(&fast); - // Check that the maps starting from the prototype haven't changed. - GenerateDirectLoadGlobalFunctionPrototype( - masm(), Context::BOOLEAN_FUNCTION_INDEX, rax, &miss); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &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 + // property cell for the property is still empty. + current = object; + while (current != holder) { + if (current->IsGlobalObject()) { + MaybeObject* cell = GenerateCheckPropertyCell(masm(), + GlobalObject::cast(current), + name, + scratch1, + miss); + if (cell->IsFailure()) { + set_failure(Failure::cast(cell)); + return reg; } - break; - } - - default: - UNREACHABLE(); - } - - if (depth != kInvalidProtoDepth) { - Failure* failure; - // Move the return address on top of the stack. - __ movq(rax, Operand(rsp, 3 * kPointerSize)); - __ movq(Operand(rsp, 0 * kPointerSize), rax); - - // rsp[2 * kPointerSize] is uninitialized, rsp[3 * kPointerSize] contains - // duplicate of return address and will be overwritten. - bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); - if (!success) { - return failure; } - } else { - __ InvokeFunction(function, arguments(), JUMP_FUNCTION); - } - - // Handle call cache miss. - __ bind(&miss); - if (depth != kInvalidProtoDepth) { - __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); - } - - // Handle call cache miss. - __ bind(&miss_in_smi_check); - Object* obj; - { MaybeObject* maybe_obj = GenerateMissBranch(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; + current = JSObject::cast(current->GetPrototype()); } - // Return the generated code. - return GetCode(function); + // Return the register containing the holder. + return reg; } -MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, - JSObject* holder, - int index, - String* name) { - // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver - // ----------------------------------- - Label miss; - - GenerateNameCheck(name, &miss); - - // Get the receiver from the stack. - const int argc = arguments().immediate(); - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - +void StubCompiler::GenerateLoadField(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + int index, + String* name, + Label* miss) { // Check that the receiver isn't a smi. - __ JumpIfSmi(rdx, &miss); - - // Do the right check and compute the holder register. - Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, rdi, - name, &miss); - - GenerateFastPropertyLoad(masm(), rdi, reg, holder, index); - - // Check that the function really is a function. - __ JumpIfSmi(rdi, &miss); - __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rbx); - __ j(not_equal, &miss); - - // Patch the receiver on the stack with the global proxy if - // necessary. - if (object->IsGlobalObject()) { - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); - } - - // Invoke the function. - __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION); + __ JumpIfSmi(receiver, miss); - // Handle call cache miss. - __ bind(&miss); - Object* obj; - { MaybeObject* maybe_obj = GenerateMissBranch(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } + // Check the prototype chain. + Register reg = + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, scratch3, name, miss); - // Return the generated code. - return GetCode(FIELD, name); + // Get the value from the properties. + GenerateFastPropertyLoad(masm(), rax, reg, holder, index); + __ ret(0); } -MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { - // ----------- S t a t e ------------- - // -- rcx : name - // -- rsp[0] : return address - // -- rsp[(argc - n) * 8] : arg[n] (zero-based) - // -- ... - // -- rsp[(argc + 1) * 8] : receiver - // ----------------------------------- +bool StubCompiler::GenerateLoadCallback(JSObject* object, + JSObject* holder, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + AccessorInfo* callback, + String* name, + Label* miss, + Failure** failure) { + // Check that the receiver isn't a smi. + __ JumpIfSmi(receiver, miss); - // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, scratch1, + scratch2, scratch3, name, miss); - Label miss; + Handle callback_handle(callback); - GenerateNameCheck(name, &miss); + // Insert additional parameters into the stack frame above return address. + ASSERT(!scratch2.is(reg)); + __ pop(scratch2); // Get return address to place it below. - // Get the receiver from the stack. - const int argc = arguments().immediate(); - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + __ push(receiver); // receiver + __ push(reg); // holder + if (Heap::InNewSpace(callback_handle->data())) { + __ Move(scratch1, callback_handle); + __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data + } else { + __ Push(Handle(callback_handle->data())); + } + __ push(name_reg); // name + // Save a pointer to where we pushed the arguments pointer. + // This will be passed as the const AccessorInfo& to the C++ callback. - // Check that the receiver isn't a smi. - __ JumpIfSmi(rdx, &miss); +#ifdef _WIN64 + // Win64 uses first register--rcx--for returned value. + Register accessor_info_arg = r8; + Register name_arg = rdx; +#else + Register accessor_info_arg = rsi; + Register name_arg = rdi; +#endif - CheckPrototypes(JSObject::cast(object), - rdx, - holder, - rbx, - rax, - rdi, - name, - &miss); + ASSERT(!name_arg.is(scratch2)); + __ movq(name_arg, rsp); + __ push(scratch2); // Restore return address. - if (argc == 0) { - // Noop, return the length. - __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset)); - __ ret((argc + 1) * kPointerSize); - } else { - Label call_builtin; + // Do call through the api. + Address getter_address = v8::ToCData
(callback->getter()); + ApiFunction fun(getter_address); - // Get the elements array of the object. - __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); + // 3 elements array for v8::Agruments::values_ and handler for name. + const int kStackSpace = 4; - // Check that the elements are in fast mode and writable. - __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), - Factory::fixed_array_map()); - __ j(not_equal, &call_builtin); + // Allocate v8::AccessorInfo in non-GCed stack space. + const int kArgStackSpace = 1; - if (argc == 1) { // Otherwise fall through to call builtin. - Label exit, with_write_barrier, attempt_to_grow_elements; + __ PrepareCallApiFunction(kArgStackSpace); + __ lea(rax, Operand(name_arg, 3 * kPointerSize)); - // Get the array's length into rax and calculate new length. - __ SmiToInteger32(rax, FieldOperand(rdx, JSArray::kLengthOffset)); - STATIC_ASSERT(FixedArray::kMaxLength < Smi::kMaxValue); - __ addl(rax, Immediate(argc)); + // v8::AccessorInfo::args_. + __ movq(StackSpaceOperand(0), rax); - // Get the element's length into rcx. - __ SmiToInteger32(rcx, FieldOperand(rbx, FixedArray::kLengthOffset)); + // The context register (rsi) has been saved in PrepareCallApiFunction and + // could be used to pass arguments. + __ lea(accessor_info_arg, StackSpaceOperand(0)); - // Check if we could survive without allocation. - __ cmpl(rax, rcx); - __ j(greater, &attempt_to_grow_elements); + // Emitting a stub call may try to allocate (if the code is not + // already generated). Do not allow the assembler to perform a + // garbage collection but instead return the allocation failure + // object. + MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); + if (result->IsFailure()) { + *failure = Failure::cast(result); + return false; + } + return true; +} - // Save new length. - __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rax); - // Push the element. - __ movq(rcx, Operand(rsp, argc * kPointerSize)); - __ lea(rdx, FieldOperand(rbx, - rax, times_pointer_size, - FixedArray::kHeaderSize - argc * kPointerSize)); - __ movq(Operand(rdx, 0), rcx); +void StubCompiler::GenerateLoadConstant(JSObject* object, + JSObject* holder, + Register receiver, + Register scratch1, + Register scratch2, + Register scratch3, + Object* value, + String* name, + Label* miss) { + // Check that the receiver isn't a smi. + __ JumpIfSmi(receiver, miss); - // Check if value is a smi. - __ Integer32ToSmi(rax, rax); // Return new length as smi. + // Check that the maps haven't changed. + Register reg = + CheckPrototypes(object, receiver, holder, + scratch1, scratch2, scratch3, name, miss); - __ JumpIfNotSmi(rcx, &with_write_barrier); + // Return the constant value. + __ Move(rax, Handle(value)); + __ ret(0); +} - __ bind(&exit); - __ ret((argc + 1) * kPointerSize); - __ bind(&with_write_barrier); +void StubCompiler::GenerateLoadInterceptor(JSObject* object, + JSObject* interceptor_holder, + LookupResult* lookup, + Register receiver, + Register name_reg, + Register scratch1, + Register scratch2, + Register scratch3, + String* name, + Label* miss) { + ASSERT(interceptor_holder->HasNamedInterceptor()); + ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); - __ InNewSpace(rbx, rcx, equal, &exit); + // Check that the receiver isn't a smi. + __ JumpIfSmi(receiver, miss); - __ RecordWriteHelper(rbx, rdx, rcx); + // So far the most popular follow ups for interceptor loads are FIELD + // and CALLBACKS, so inline only them, other cases may be added + // later. + bool compile_followup_inline = false; + if (lookup->IsProperty() && lookup->IsCacheable()) { + if (lookup->type() == FIELD) { + compile_followup_inline = true; + } else if (lookup->type() == CALLBACKS && + lookup->GetCallbackObject()->IsAccessorInfo() && + AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) { + compile_followup_inline = true; + } + } - __ ret((argc + 1) * kPointerSize); + if (compile_followup_inline) { + // Compile the interceptor call, followed by inline code to load the + // property from further up the prototype chain if the call fails. + // Check that the maps haven't changed. + Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder, + scratch1, scratch2, scratch3, + name, miss); + ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1)); - __ bind(&attempt_to_grow_elements); - if (!FLAG_inline_new) { - __ jmp(&call_builtin); - } + // Save necessary data before invoking an interceptor. + // Requires a frame to make GC aware of pushed pointers. + __ EnterInternalFrame(); - ExternalReference new_space_allocation_top = - ExternalReference::new_space_allocation_top_address(); - ExternalReference new_space_allocation_limit = - ExternalReference::new_space_allocation_limit_address(); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + // CALLBACKS case needs a receiver to be passed into C++ callback. + __ push(receiver); + } + __ push(holder_reg); + __ push(name_reg); - const int kAllocationDelta = 4; - // Load top. - __ movq(rcx, new_space_allocation_top); - __ movq(rcx, Operand(rcx, 0)); + // Invoke an interceptor. Note: map checks from receiver to + // interceptor's holder has been compiled before (see a caller + // of this method.) + CompileCallLoadPropertyWithInterceptor(masm(), + receiver, + holder_reg, + name_reg, + interceptor_holder); - // Check if it's the end of elements. - __ lea(rdx, FieldOperand(rbx, - rax, times_pointer_size, - FixedArray::kHeaderSize - argc * kPointerSize)); - __ cmpq(rdx, rcx); - __ j(not_equal, &call_builtin); - __ addq(rcx, Immediate(kAllocationDelta * kPointerSize)); - __ movq(kScratchRegister, new_space_allocation_limit); - __ cmpq(rcx, Operand(kScratchRegister, 0)); - __ j(above, &call_builtin); + // Check if interceptor provided a value for property. If it's + // the case, return immediately. + Label interceptor_failed; + __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); + __ j(equal, &interceptor_failed); + __ LeaveInternalFrame(); + __ ret(0); - // We fit and could grow elements. - __ movq(kScratchRegister, new_space_allocation_top); - __ movq(Operand(kScratchRegister, 0), rcx); - __ movq(rcx, Operand(rsp, argc * kPointerSize)); + __ bind(&interceptor_failed); + __ pop(name_reg); + __ pop(holder_reg); + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { + __ pop(receiver); + } - // Push the argument... - __ movq(Operand(rdx, 0), rcx); - // ... and fill the rest with holes. - __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); - for (int i = 1; i < kAllocationDelta; i++) { - __ movq(Operand(rdx, i * kPointerSize), kScratchRegister); - } + __ LeaveInternalFrame(); - // Restore receiver to rdx as finish sequence assumes it's here. - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + // Check that the maps from interceptor's holder to lookup's holder + // haven't changed. And load lookup's holder into |holder| register. + if (interceptor_holder != lookup->holder()) { + holder_reg = CheckPrototypes(interceptor_holder, + holder_reg, + lookup->holder(), + scratch1, + scratch2, + scratch3, + name, + miss); + } - // Increment element's and array's sizes. - __ SmiAddConstant(FieldOperand(rbx, FixedArray::kLengthOffset), - Smi::FromInt(kAllocationDelta)); + if (lookup->type() == FIELD) { + // We found FIELD property in prototype chain of interceptor's holder. + // Retrieve a field from field's holder. + GenerateFastPropertyLoad(masm(), rax, holder_reg, + lookup->holder(), lookup->GetFieldIndex()); + __ ret(0); + } else { + // We found CALLBACKS property in prototype chain of interceptor's + // holder. + ASSERT(lookup->type() == CALLBACKS); + ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); + AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); + ASSERT(callback != NULL); + ASSERT(callback->getter() != NULL); - // Make new length a smi before returning it. - __ Integer32ToSmi(rax, rax); - __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax); + // Tail call to runtime. + // Important invariant in CALLBACKS case: the code above must be + // structured to never clobber |receiver| register. + __ pop(scratch2); // return address + __ push(receiver); + __ push(holder_reg); + __ Move(holder_reg, Handle(callback)); + __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); + __ push(holder_reg); + __ push(name_reg); + __ push(scratch2); // restore return address - // Elements are in new space, so write barrier is not required. - __ ret((argc + 1) * kPointerSize); + ExternalReference ref = + ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); + __ TailCallExternalReference(ref, 5, 1); } + } else { // !compile_followup_inline + // Call the runtime system to load the interceptor. + // Check that the maps haven't changed. + Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder, + scratch1, scratch2, scratch3, + name, miss); + __ pop(scratch2); // save old return address + PushInterceptorArguments(masm(), receiver, holder_reg, + name_reg, interceptor_holder); + __ push(scratch2); // restore old return address - __ bind(&call_builtin); - __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), - argc + 1, - 1); - } - - __ bind(&miss); - Object* obj; - { MaybeObject* maybe_obj = GenerateMissBranch(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; + ExternalReference ref = ExternalReference( + IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); + __ TailCallExternalReference(ref, 5, 1); } - - // Return the generated code. - return GetCode(function); } -MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { - // ----------- S t a t e ------------- - // -- rcx : name - // -- rsp[0] : return address - // -- rsp[(argc - n) * 8] : arg[n] (zero-based) - // -- ... - // -- rsp[(argc + 1) * 8] : receiver - // ----------------------------------- +void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { + if (kind_ == Code::KEYED_CALL_IC) { + __ Cmp(rcx, Handle(name)); + __ j(not_equal, miss); + } +} - // If object is not an array, bail out to regular call. - if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); - Label miss, return_undefined, call_builtin; +void CallStubCompiler::GenerateGlobalReceiverCheck(JSObject* object, + JSObject* holder, + String* name, + Label* miss) { + ASSERT(holder->IsGlobalObject()); - GenerateNameCheck(name, &miss); + // Get the number of arguments. + const int argc = arguments().immediate(); // Get the receiver from the stack. - const int argc = arguments().immediate(); __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - // Check that the receiver isn't a smi. - __ JumpIfSmi(rdx, &miss); - - CheckPrototypes(JSObject::cast(object), rdx, - holder, rbx, - rax, rdi, name, &miss); - - // Get the elements array of the object. - __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); - - // Check that the elements are in fast mode and writable. - __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset), - Heap::kFixedArrayMapRootIndex); - __ j(not_equal, &call_builtin); - - // Get the array's length into rcx and calculate new length. - __ SmiToInteger32(rcx, FieldOperand(rdx, JSArray::kLengthOffset)); - __ subl(rcx, Immediate(1)); - __ j(negative, &return_undefined); - - // Get the last element. - __ LoadRoot(r9, Heap::kTheHoleValueRootIndex); - __ movq(rax, FieldOperand(rbx, - rcx, times_pointer_size, - FixedArray::kHeaderSize)); - // Check if element is already the hole. - __ cmpq(rax, r9); - // If so, call slow-case to also check prototypes for value. - __ j(equal, &call_builtin); - - // Set the array's length. - __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rcx); - - // Fill with the hole and return original value. - __ movq(FieldOperand(rbx, - rcx, times_pointer_size, - FixedArray::kHeaderSize), - r9); - __ ret((argc + 1) * kPointerSize); - - __ bind(&return_undefined); - __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); - __ ret((argc + 1) * kPointerSize); - - __ bind(&call_builtin); - __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop), - argc + 1, - 1); - - __ bind(&miss); - Object* obj; - { MaybeObject* maybe_obj = GenerateMissBranch(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; + // If the object is the holder then we know that it's a global + // object which can only happen for contextual calls. In this case, + // the receiver cannot be a smi. + if (object != holder) { + __ JumpIfSmi(rdx, miss); } - // Return the generated code. - return GetCode(function); + // Check that the maps haven't changed. + CheckPrototypes(object, rdx, holder, rbx, rax, rdi, name, miss); } -MaybeObject* CallStubCompiler::CompileStringCharAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { - // ----------- S t a t e ------------- - // -- rcx : function name - // -- rsp[0] : return address - // -- rsp[(argc - n) * 8] : arg[n] (zero-based) - // -- ... - // -- rsp[(argc + 1) * 8] : receiver - // ----------------------------------- - - // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return Heap::undefined_value(); - - const int argc = arguments().immediate(); - - Label miss; - Label index_out_of_range; - - GenerateNameCheck(name, &miss); +void CallStubCompiler::GenerateLoadFunctionFromCell(JSGlobalPropertyCell* cell, + JSFunction* function, + Label* miss) { + // Get the value from the cell. + __ Move(rdi, Handle(cell)); + __ movq(rdi, FieldOperand(rdi, JSGlobalPropertyCell::kValueOffset)); - // Check that the maps starting from the prototype haven't changed. - GenerateDirectLoadGlobalFunctionPrototype(masm(), - Context::STRING_FUNCTION_INDEX, - rax, - &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); + // Check that the cell contains the same function. + if (Heap::InNewSpace(function)) { + // We can't embed a pointer to a function in new space so we have + // to verify that the shared function info is unchanged. This has + // the nice side effect that multiple closures based on the same + // function can all use this call IC. Before we load through the + // function, we have to verify that it still is a function. + __ JumpIfSmi(rdi, miss); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax); + __ j(not_equal, miss); - Register receiver = rax; - Register index = rdi; - Register scratch1 = rbx; - Register scratch2 = rdx; - Register result = rax; - __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); - if (argc > 0) { - __ movq(index, Operand(rsp, (argc - 0) * kPointerSize)); + // Check the shared function info. Make sure it hasn't changed. + __ Move(rax, Handle(function->shared())); + __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax); + __ j(not_equal, miss); } else { - __ LoadRoot(index, Heap::kUndefinedValueRootIndex); + __ Cmp(rdi, Handle(function)); + __ j(not_equal, miss); } +} - StringCharAtGenerator char_at_generator(receiver, - index, - scratch1, - scratch2, - result, - &miss, // When not a string. - &miss, // When not a number. - &index_out_of_range, - STRING_INDEX_IS_NUMBER); - char_at_generator.GenerateFast(masm()); - __ ret((argc + 1) * kPointerSize); - - StubRuntimeCallHelper call_helper; - char_at_generator.GenerateSlow(masm(), call_helper); - - __ bind(&index_out_of_range); - __ LoadRoot(rax, Heap::kEmptyStringRootIndex); - __ ret((argc + 1) * kPointerSize); - __ bind(&miss); +MaybeObject* CallStubCompiler::GenerateMissBranch() { + MaybeObject* maybe_obj = + StubCache::ComputeCallMiss(arguments().immediate(), kind_); Object* obj; - { MaybeObject* maybe_obj = GenerateMissBranch(); - if (!maybe_obj->ToObject(&obj)) return maybe_obj; - } - - // Return the generated code. - return GetCode(function); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + __ Jump(Handle(Code::cast(obj)), RelocInfo::CODE_TARGET); + return obj; } -MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, + JSObject* holder, + int index, + String* name) { // ----------- S t a t e ------------- - // -- rcx : function name - // -- rsp[0] : return address - // -- rsp[(argc - n) * 8] : arg[n] (zero-based) - // -- ... - // -- rsp[(argc + 1) * 8] : receiver + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- - - // If object is not a string, bail out to regular call. - if (!object->IsString() || cell != NULL) return Heap::undefined_value(); - - const int argc = arguments().immediate(); - Label miss; - Label index_out_of_range; + GenerateNameCheck(name, &miss); - // Check that the maps starting from the prototype haven't changed. - GenerateDirectLoadGlobalFunctionPrototype(masm(), - Context::STRING_FUNCTION_INDEX, - rax, - &miss); - ASSERT(object != holder); - CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, - rbx, rdx, rdi, name, &miss); + // Get the receiver from the stack. + const int argc = arguments().immediate(); + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - Register receiver = rbx; - Register index = rdi; - Register scratch = rdx; - Register result = rax; - __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); - if (argc > 0) { - __ movq(index, Operand(rsp, (argc - 0) * kPointerSize)); - } else { - __ LoadRoot(index, Heap::kUndefinedValueRootIndex); - } + // Check that the receiver isn't a smi. + __ JumpIfSmi(rdx, &miss); - StringCharCodeAtGenerator char_code_at_generator(receiver, - index, - scratch, - result, - &miss, // When not a string. - &miss, // When not a number. - &index_out_of_range, - STRING_INDEX_IS_NUMBER); - char_code_at_generator.GenerateFast(masm()); - __ ret((argc + 1) * kPointerSize); + // Do the right check and compute the holder register. + Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, rdi, + name, &miss); - StubRuntimeCallHelper call_helper; - char_code_at_generator.GenerateSlow(masm(), call_helper); + GenerateFastPropertyLoad(masm(), rdi, reg, holder, index); - __ bind(&index_out_of_range); - __ LoadRoot(rax, Heap::kNanValueRootIndex); - __ ret((argc + 1) * kPointerSize); + // Check that the function really is a function. + __ JumpIfSmi(rdi, &miss); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rbx); + __ j(not_equal, &miss); + + // Patch the receiver on the stack with the global proxy if + // necessary. + if (object->IsGlobalObject()) { + __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); + __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); + } + + // Invoke the function. + __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION); + // Handle call cache miss. __ bind(&miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); @@ -1550,258 +1389,319 @@ MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( } // Return the generated code. - return GetCode(function); + return GetCode(FIELD, name); } -MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( - Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +MaybeObject* CallStubCompiler::CompileArrayPushCall(Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // -- rcx : function name + // -- rcx : name // -- rsp[0] : return address // -- rsp[(argc - n) * 8] : arg[n] (zero-based) // -- ... // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - const int argc = arguments().immediate(); - - // If the object is not a JSObject or we got an unexpected number of - // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return Heap::undefined_value(); + // If object is not an array, bail out to regular call. + if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); Label miss; + GenerateNameCheck(name, &miss); - if (cell == NULL) { - __ movq(rdx, Operand(rsp, 2 * kPointerSize)); + // Get the receiver from the stack. + const int argc = arguments().immediate(); + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - __ JumpIfSmi(rdx, &miss); + // Check that the receiver isn't a smi. + __ JumpIfSmi(rdx, &miss); - CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, - &miss); + CheckPrototypes(JSObject::cast(object), + rdx, + holder, + rbx, + rax, + rdi, + name, + &miss); + + if (argc == 0) { + // Noop, return the length. + __ movq(rax, FieldOperand(rdx, JSArray::kLengthOffset)); + __ ret((argc + 1) * kPointerSize); } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); - GenerateLoadFunctionFromCell(cell, function, &miss); - } + Label call_builtin; - // Load the char code argument. - Register code = rbx; - __ movq(code, Operand(rsp, 1 * kPointerSize)); + // Get the elements array of the object. + __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); - // Check the code is a smi. - Label slow; - __ JumpIfNotSmi(code, &slow); + // Check that the elements are in fast mode and writable. + __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), + Factory::fixed_array_map()); + __ j(not_equal, &call_builtin); - // Convert the smi code to uint16. - __ SmiAndConstant(code, code, Smi::FromInt(0xffff)); + if (argc == 1) { // Otherwise fall through to call builtin. + Label exit, with_write_barrier, attempt_to_grow_elements; - StringCharFromCodeGenerator char_from_code_generator(code, rax); - char_from_code_generator.GenerateFast(masm()); - __ ret(2 * kPointerSize); + // Get the array's length into rax and calculate new length. + __ SmiToInteger32(rax, FieldOperand(rdx, JSArray::kLengthOffset)); + STATIC_ASSERT(FixedArray::kMaxLength < Smi::kMaxValue); + __ addl(rax, Immediate(argc)); - StubRuntimeCallHelper call_helper; - char_from_code_generator.GenerateSlow(masm(), call_helper); + // Get the element's length into rcx. + __ SmiToInteger32(rcx, FieldOperand(rbx, FixedArray::kLengthOffset)); - // Tail call the full function. We do not have to patch the receiver - // because the function makes no use of it. - __ bind(&slow); - __ InvokeFunction(function, arguments(), JUMP_FUNCTION); + // Check if we could survive without allocation. + __ cmpl(rax, rcx); + __ j(greater, &attempt_to_grow_elements); + + // Save new length. + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rax); + + // Push the element. + __ movq(rcx, Operand(rsp, argc * kPointerSize)); + __ lea(rdx, FieldOperand(rbx, + rax, times_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize)); + __ movq(Operand(rdx, 0), rcx); + + // Check if value is a smi. + __ Integer32ToSmi(rax, rax); // Return new length as smi. + + __ JumpIfNotSmi(rcx, &with_write_barrier); + + __ bind(&exit); + __ ret((argc + 1) * kPointerSize); + + __ bind(&with_write_barrier); + + __ InNewSpace(rbx, rcx, equal, &exit); + + __ RecordWriteHelper(rbx, rdx, rcx); + + __ ret((argc + 1) * kPointerSize); + + __ bind(&attempt_to_grow_elements); + if (!FLAG_inline_new) { + __ jmp(&call_builtin); + } + + ExternalReference new_space_allocation_top = + ExternalReference::new_space_allocation_top_address(); + ExternalReference new_space_allocation_limit = + ExternalReference::new_space_allocation_limit_address(); + + const int kAllocationDelta = 4; + // Load top. + __ movq(rcx, new_space_allocation_top); + __ movq(rcx, Operand(rcx, 0)); + + // Check if it's the end of elements. + __ lea(rdx, FieldOperand(rbx, + rax, times_pointer_size, + FixedArray::kHeaderSize - argc * kPointerSize)); + __ cmpq(rdx, rcx); + __ j(not_equal, &call_builtin); + __ addq(rcx, Immediate(kAllocationDelta * kPointerSize)); + __ movq(kScratchRegister, new_space_allocation_limit); + __ cmpq(rcx, Operand(kScratchRegister, 0)); + __ j(above, &call_builtin); + + // We fit and could grow elements. + __ movq(kScratchRegister, new_space_allocation_top); + __ movq(Operand(kScratchRegister, 0), rcx); + __ movq(rcx, Operand(rsp, argc * kPointerSize)); + + // Push the argument... + __ movq(Operand(rdx, 0), rcx); + // ... and fill the rest with holes. + __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); + for (int i = 1; i < kAllocationDelta; i++) { + __ movq(Operand(rdx, i * kPointerSize), kScratchRegister); + } + + // Restore receiver to rdx as finish sequence assumes it's here. + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + + // Increment element's and array's sizes. + __ SmiAddConstant(FieldOperand(rbx, FixedArray::kLengthOffset), + Smi::FromInt(kAllocationDelta)); + + // Make new length a smi before returning it. + __ Integer32ToSmi(rax, rax); + __ movq(FieldOperand(rdx, JSArray::kLengthOffset), rax); + + // Elements are in new space, so write barrier is not required. + __ ret((argc + 1) * kPointerSize); + } + + __ bind(&call_builtin); + __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush), + argc + 1, + 1); + } __ bind(&miss); - // rcx: function name. Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); -} - - -MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { - // TODO(872): implement this. - return Heap::undefined_value(); + return GetCode(function); } -MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, - JSObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +MaybeObject* CallStubCompiler::CompileArrayPopCall(Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // -- rcx : function name + // -- rcx : name // -- rsp[0] : return address // -- rsp[(argc - n) * 8] : arg[n] (zero-based) // -- ... // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - const int argc = arguments().immediate(); + // If object is not an array, bail out to regular call. + if (!object->IsJSArray() || cell != NULL) return Heap::undefined_value(); - // If the object is not a JSObject or we got an unexpected number of - // arguments, bail out to the regular call. - if (!object->IsJSObject() || argc != 1) return Heap::undefined_value(); + Label miss, return_undefined, call_builtin; - Label miss; GenerateNameCheck(name, &miss); - if (cell == NULL) { - __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - - __ JumpIfSmi(rdx, &miss); - - CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, - &miss); - } else { - ASSERT(cell->value() == function); - GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); - GenerateLoadFunctionFromCell(cell, function, &miss); - } - - // Load the (only) argument into rax. - __ movq(rax, Operand(rsp, 1 * kPointerSize)); + // Get the receiver from the stack. + const int argc = arguments().immediate(); + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); - // Check if the argument is a smi. - Label not_smi; - STATIC_ASSERT(kSmiTag == 0); - __ JumpIfNotSmi(rax, ¬_smi); - __ SmiToInteger32(rax, rax); + // Check that the receiver isn't a smi. + __ JumpIfSmi(rdx, &miss); - // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0 - // otherwise. - __ movl(rbx, rax); - __ sarl(rbx, Immediate(kBitsPerInt - 1)); + CheckPrototypes(JSObject::cast(object), rdx, + holder, rbx, + rax, rdi, name, &miss); - // Do bitwise not or do nothing depending on ebx. - __ xorl(rax, rbx); + // Get the elements array of the object. + __ movq(rbx, FieldOperand(rdx, JSArray::kElementsOffset)); - // Add 1 or do nothing depending on ebx. - __ subl(rax, rbx); + // Check that the elements are in fast mode and writable. + __ CompareRoot(FieldOperand(rbx, HeapObject::kMapOffset), + Heap::kFixedArrayMapRootIndex); + __ j(not_equal, &call_builtin); - // If the result is still negative, go to the slow case. - // This only happens for the most negative smi. - Label slow; - __ j(negative, &slow); + // Get the array's length into rcx and calculate new length. + __ SmiToInteger32(rcx, FieldOperand(rdx, JSArray::kLengthOffset)); + __ subl(rcx, Immediate(1)); + __ j(negative, &return_undefined); - // Smi case done. - __ Integer32ToSmi(rax, rax); - __ ret(2 * kPointerSize); + // Get the last element. + __ LoadRoot(r9, Heap::kTheHoleValueRootIndex); + __ movq(rax, FieldOperand(rbx, + rcx, times_pointer_size, + FixedArray::kHeaderSize)); + // Check if element is already the hole. + __ cmpq(rax, r9); + // If so, call slow-case to also check prototypes for value. + __ j(equal, &call_builtin); - // Check if the argument is a heap number and load its value. - __ bind(¬_smi); - __ CheckMap(rax, Factory::heap_number_map(), &slow, true); - __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset)); + // Set the array's length. + __ Integer32ToSmiField(FieldOperand(rdx, JSArray::kLengthOffset), rcx); - // Check the sign of the argument. If the argument is positive, - // just return it. - Label negative_sign; - const int sign_mask_shift = - (HeapNumber::kExponentOffset - HeapNumber::kValueOffset) * kBitsPerByte; - __ movq(rdi, static_cast(HeapNumber::kSignMask) << sign_mask_shift, - RelocInfo::NONE); - __ testq(rbx, rdi); - __ j(not_zero, &negative_sign); - __ ret(2 * kPointerSize); + // Fill with the hole and return original value. + __ movq(FieldOperand(rbx, + rcx, times_pointer_size, + FixedArray::kHeaderSize), + r9); + __ ret((argc + 1) * kPointerSize); - // If the argument is negative, clear the sign, and return a new - // number. We still have the sign mask in rdi. - __ bind(&negative_sign); - __ xor_(rbx, rdi); - __ AllocateHeapNumber(rax, rdx, &slow); - __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rbx); - __ ret(2 * kPointerSize); + __ bind(&return_undefined); + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + __ ret((argc + 1) * kPointerSize); - // Tail call the full function. We do not have to patch the receiver - // because the function makes no use of it. - __ bind(&slow); - __ InvokeFunction(function, arguments(), JUMP_FUNCTION); + __ bind(&call_builtin); + __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop), + argc + 1, + 1); __ bind(&miss); - // rcx: function name. Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } // Return the generated code. - return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); + return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, - JSObject* holder, - String* name) { +MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( + Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver + // -- rcx : function name + // -- rsp[0] : return address + // -- rsp[(argc - n) * 8] : arg[n] (zero-based) + // -- ... + // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - Label miss; - GenerateNameCheck(name, &miss); + // If object is not a string, bail out to regular call. + if (!object->IsString() || cell != NULL) return Heap::undefined_value(); - // Get the number of arguments. const int argc = arguments().immediate(); - LookupResult lookup; - LookupPostInterceptor(holder, name, &lookup); + Label miss; + Label index_out_of_range; - // Get the receiver from the stack. - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + GenerateNameCheck(name, &miss); - CallInterceptorCompiler compiler(this, arguments(), rcx); - Failure* failure; - bool success = compiler.Compile(masm(), - object, - holder, - name, - &lookup, - rdx, - rbx, - rdi, - rax, - &miss, - &failure); - if (!success) { - return failure; - } + // Check that the maps starting from the prototype haven't changed. + GenerateDirectLoadGlobalFunctionPrototype(masm(), + Context::STRING_FUNCTION_INDEX, + rax, + &miss); + ASSERT(object != holder); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, + rbx, rdx, rdi, name, &miss); - // Restore receiver. - __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + Register receiver = rbx; + Register index = rdi; + Register scratch = rdx; + Register result = rax; + __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); + if (argc > 0) { + __ movq(index, Operand(rsp, (argc - 0) * kPointerSize)); + } else { + __ LoadRoot(index, Heap::kUndefinedValueRootIndex); + } - // Check that the function really is a function. - __ JumpIfSmi(rax, &miss); - __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); - __ j(not_equal, &miss); + StringCharCodeAtGenerator char_code_at_generator(receiver, + index, + scratch, + result, + &miss, // When not a string. + &miss, // When not a number. + &index_out_of_range, + STRING_INDEX_IS_NUMBER); + char_code_at_generator.GenerateFast(masm()); + __ ret((argc + 1) * kPointerSize); - // Patch the receiver on the stack with the global proxy if - // necessary. - if (object->IsGlobalObject()) { - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); - } + StubRuntimeCallHelper call_helper; + char_code_at_generator.GenerateSlow(masm(), call_helper); - // Invoke the function. - __ movq(rdi, rax); - __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION); + __ bind(&index_out_of_range); + __ LoadRoot(rax, Heap::kNanValueRootIndex); + __ ret((argc + 1) * kPointerSize); - // Handle load cache miss. __ bind(&miss); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); @@ -1809,486 +1709,610 @@ MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, } // Return the generated code. - return GetCode(INTERCEPTOR, name); + return GetCode(function); } -MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - JSFunction* function, - String* name) { +MaybeObject* CallStubCompiler::CompileStringCharAtCall( + Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // rcx : function name - // rsp[0] : return address - // rsp[8] : argument argc - // rsp[16] : argument argc - 1 - // ... - // rsp[argc * 8] : argument 1 - // rsp[(argc + 1) * 8] : argument 0 = receiver + // -- rcx : function name + // -- rsp[0] : return address + // -- rsp[(argc - n) * 8] : arg[n] (zero-based) + // -- ... + // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - SharedFunctionInfo* function_info = function->shared(); - if (function_info->HasBuiltinFunctionId()) { - BuiltinFunctionId id = function_info->builtin_function_id(); - MaybeObject* maybe_result = CompileCustomCall( - id, object, holder, cell, function, name); - Object* result; - if (!maybe_result->ToObject(&result)) return maybe_result; - // undefined means bail out to regular compiler. - if (!result->IsUndefined()) return result; - } + // If object is not a string, bail out to regular call. + if (!object->IsString() || cell != NULL) return Heap::undefined_value(); + + const int argc = arguments().immediate(); Label miss; + Label index_out_of_range; GenerateNameCheck(name, &miss); - // Get the number of arguments. - const int argc = arguments().immediate(); - - GenerateGlobalReceiverCheck(object, holder, name, &miss); - - GenerateLoadFunctionFromCell(cell, function, &miss); + // Check that the maps starting from the prototype haven't changed. + GenerateDirectLoadGlobalFunctionPrototype(masm(), + Context::STRING_FUNCTION_INDEX, + rax, + &miss); + ASSERT(object != holder); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, + rbx, rdx, rdi, name, &miss); - // Patch the receiver on the stack with the global proxy. - if (object->IsGlobalObject()) { - __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); - __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); + Register receiver = rax; + Register index = rdi; + Register scratch1 = rbx; + Register scratch2 = rdx; + Register result = rax; + __ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); + if (argc > 0) { + __ movq(index, Operand(rsp, (argc - 0) * kPointerSize)); + } else { + __ LoadRoot(index, Heap::kUndefinedValueRootIndex); } - // Setup the context (function already in edi). - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + StringCharAtGenerator char_at_generator(receiver, + index, + scratch1, + scratch2, + result, + &miss, // When not a string. + &miss, // When not a number. + &index_out_of_range, + STRING_INDEX_IS_NUMBER); + char_at_generator.GenerateFast(masm()); + __ ret((argc + 1) * kPointerSize); + + StubRuntimeCallHelper call_helper; + char_at_generator.GenerateSlow(masm(), call_helper); - // Jump to the cached code (tail call). - __ IncrementCounter(&Counters::call_global_inline, 1); - ASSERT(function->is_compiled()); - Handle code(function->code()); - ParameterCount expected(function->shared()->formal_parameter_count()); - __ InvokeCode(code, expected, arguments(), - RelocInfo::CODE_TARGET, JUMP_FUNCTION); + __ bind(&index_out_of_range); + __ LoadRoot(rax, Heap::kEmptyStringRootIndex); + __ ret((argc + 1) * kPointerSize); - // Handle call cache miss. __ bind(&miss); - __ IncrementCounter(&Counters::call_global_inline_miss, 1); Object* obj; { MaybeObject* maybe_obj = GenerateMissBranch(); if (!maybe_obj->ToObject(&obj)) return maybe_obj; } // Return the generated code. - return GetCode(NORMAL, name); + return GetCode(function); } -MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, - JSObject* object, - JSObject* holder, - AccessorInfo* callback) { +MaybeObject* CallStubCompiler::CompileStringFromCharCodeCall( + Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address + // -- rcx : function name + // -- rsp[0] : return address + // -- rsp[(argc - n) * 8] : arg[n] (zero-based) + // -- ... + // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- - Label miss; - - Failure* failure = Failure::InternalError(); - bool success = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, - callback, name, &miss, &failure); - if (!success) { - miss.Unuse(); - return failure; - } - - __ bind(&miss); - GenerateLoadMiss(masm(), Code::LOAD_IC); - // Return the generated code. - return GetCode(CALLBACKS, name); -} + const int argc = arguments().immediate(); + // If the object is not a JSObject or we got an unexpected number of + // arguments, bail out to the regular call. + if (!object->IsJSObject() || argc != 1) return Heap::undefined_value(); -MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, - JSObject* holder, - Object* value, - String* name) { - // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address - // ----------------------------------- Label miss; + GenerateNameCheck(name, &miss); - GenerateLoadConstant(object, holder, rax, rbx, rdx, rdi, value, name, &miss); - __ bind(&miss); - GenerateLoadMiss(masm(), Code::LOAD_IC); + if (cell == NULL) { + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - // Return the generated code. - return GetCode(CONSTANT_FUNCTION, name); -} + __ JumpIfSmi(rdx, &miss); + CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, + &miss); + } else { + ASSERT(cell->value() == function); + GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + GenerateLoadFunctionFromCell(cell, function, &miss); + } -MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, - JSObject* object, - JSObject* last) { - // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address - // ----------------------------------- - Label miss; + // Load the char code argument. + Register code = rbx; + __ movq(code, Operand(rsp, 1 * kPointerSize)); - // Chech that receiver is not a smi. - __ JumpIfSmi(rax, &miss); + // Check the code is a smi. + Label slow; + __ JumpIfNotSmi(code, &slow); - // Check the maps of the full prototype chain. Also check that - // global property cells up to (but not including) the last object - // in the prototype chain are empty. - CheckPrototypes(object, rax, last, rbx, rdx, rdi, name, &miss); + // Convert the smi code to uint16. + __ SmiAndConstant(code, code, Smi::FromInt(0xffff)); - // If the last object in the prototype chain is a global object, - // check that the global property cell is empty. - if (last->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(last), - name, - rdx, - &miss); - if (cell->IsFailure()) { - miss.Unuse(); - return cell; - } - } + StringCharFromCodeGenerator char_from_code_generator(code, rax); + char_from_code_generator.GenerateFast(masm()); + __ ret(2 * kPointerSize); - // Return undefined if maps of the full prototype chain are still the - // same and no global property with this name contains a value. - __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); - __ ret(0); + StubRuntimeCallHelper call_helper; + char_from_code_generator.GenerateSlow(masm(), call_helper); + + // Tail call the full function. We do not have to patch the receiver + // because the function makes no use of it. + __ bind(&slow); + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); __ bind(&miss); - GenerateLoadMiss(masm(), Code::LOAD_IC); + // rcx: function name. + Object* obj; + { MaybeObject* maybe_obj = GenerateMissBranch(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } // Return the generated code. - return GetCode(NONEXISTENT, Heap::empty_string()); + return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, - JSObject* holder, - int index, - String* name) { - // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address - // ----------------------------------- - Label miss; - - GenerateLoadField(object, holder, rax, rbx, rdx, rdi, index, name, &miss); - __ bind(&miss); - GenerateLoadMiss(masm(), Code::LOAD_IC); - - // Return the generated code. - return GetCode(FIELD, name); +MaybeObject* CallStubCompiler::CompileMathFloorCall(Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { + // TODO(872): implement this. + return Heap::undefined_value(); } -MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +MaybeObject* CallStubCompiler::CompileMathAbsCall(Object* object, + JSObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address + // -- rcx : function name + // -- rsp[0] : return address + // -- rsp[(argc - n) * 8] : arg[n] (zero-based) + // -- ... + // -- rsp[(argc + 1) * 8] : receiver // ----------------------------------- + + const int argc = arguments().immediate(); + + // If the object is not a JSObject or we got an unexpected number of + // arguments, bail out to the regular call. + if (!object->IsJSObject() || argc != 1) return Heap::undefined_value(); + Label miss; + GenerateNameCheck(name, &miss); - LookupResult lookup; - LookupPostInterceptor(holder, name, &lookup); + if (cell == NULL) { + __ movq(rdx, Operand(rsp, 2 * kPointerSize)); - // TODO(368): Compile in the whole chain: all the interceptors in - // prototypes and ultimate answer. - GenerateLoadInterceptor(receiver, - holder, - &lookup, - rax, - rcx, - rdx, - rbx, - rdi, - name, - &miss); + __ JumpIfSmi(rdx, &miss); - __ bind(&miss); - GenerateLoadMiss(masm(), Code::LOAD_IC); + CheckPrototypes(JSObject::cast(object), rdx, holder, rbx, rax, rdi, name, + &miss); + } else { + ASSERT(cell->value() == function); + GenerateGlobalReceiverCheck(JSObject::cast(object), holder, name, &miss); + GenerateLoadFunctionFromCell(cell, function, &miss); + } - // Return the generated code. - return GetCode(INTERCEPTOR, name); -} + // Load the (only) argument into rax. + __ movq(rax, Operand(rsp, 1 * kPointerSize)); + // Check if the argument is a smi. + Label not_smi; + STATIC_ASSERT(kSmiTag == 0); + __ JumpIfNotSmi(rax, ¬_smi); + __ SmiToInteger32(rax, rax); -MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, - GlobalObject* holder, - JSGlobalPropertyCell* cell, - String* name, - bool is_dont_delete) { - // ----------- S t a t e ------------- - // -- rax : receiver - // -- rcx : name - // -- rsp[0] : return address - // ----------------------------------- - Label miss; + // Set ebx to 1...1 (== -1) if the argument is negative, or to 0...0 + // otherwise. + __ movl(rbx, rax); + __ sarl(rbx, Immediate(kBitsPerInt - 1)); - // 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. - if (object != holder) { - __ JumpIfSmi(rax, &miss); - } + // Do bitwise not or do nothing depending on ebx. + __ xorl(rax, rbx); - // Check that the maps haven't changed. - CheckPrototypes(object, rax, holder, rbx, rdx, rdi, name, &miss); + // Add 1 or do nothing depending on ebx. + __ subl(rax, rbx); + + // If the result is still negative, go to the slow case. + // This only happens for the most negative smi. + Label slow; + __ j(negative, &slow); + + // Smi case done. + __ Integer32ToSmi(rax, rax); + __ ret(2 * kPointerSize); + + // Check if the argument is a heap number and load its value. + __ bind(¬_smi); + __ CheckMap(rax, Factory::heap_number_map(), &slow, true); + __ movq(rbx, FieldOperand(rax, HeapNumber::kValueOffset)); - // Get the value from the cell. - __ Move(rbx, Handle(cell)); - __ movq(rbx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset)); + // Check the sign of the argument. If the argument is positive, + // just return it. + Label negative_sign; + const int sign_mask_shift = + (HeapNumber::kExponentOffset - HeapNumber::kValueOffset) * kBitsPerByte; + __ movq(rdi, static_cast(HeapNumber::kSignMask) << sign_mask_shift, + RelocInfo::NONE); + __ testq(rbx, rdi); + __ j(not_zero, &negative_sign); + __ ret(2 * kPointerSize); - // Check for deleted property if property can actually be deleted. - if (!is_dont_delete) { - __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); - __ j(equal, &miss); - } else if (FLAG_debug_code) { - __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); - __ Check(not_equal, "DontDelete cells can't contain the hole"); - } + // If the argument is negative, clear the sign, and return a new + // number. We still have the sign mask in rdi. + __ bind(&negative_sign); + __ xor_(rbx, rdi); + __ AllocateHeapNumber(rax, rdx, &slow); + __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rbx); + __ ret(2 * kPointerSize); - __ IncrementCounter(&Counters::named_load_global_stub, 1); - __ movq(rax, rbx); - __ ret(0); + // Tail call the full function. We do not have to patch the receiver + // because the function makes no use of it. + __ bind(&slow); + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); __ bind(&miss); - __ IncrementCounter(&Counters::named_load_global_stub_miss, 1); - GenerateLoadMiss(masm(), Code::LOAD_IC); + // rcx: function name. + Object* obj; + { MaybeObject* maybe_obj = GenerateMissBranch(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } // Return the generated code. - return GetCode(NORMAL, name); + return (cell == NULL) ? GetCode(function) : GetCode(NORMAL, name); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( - String* name, - JSObject* receiver, - JSObject* holder, - AccessorInfo* callback) { +MaybeObject* CallStubCompiler::CompileCallConstant(Object* object, + JSObject* holder, + JSFunction* function, + String* name, + CheckType check) { // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- - Label miss; - - __ IncrementCounter(&Counters::keyed_load_callback, 1); - - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &miss); - Failure* failure = Failure::InternalError(); - bool success = GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, - callback, name, &miss, &failure); - if (!success) { - miss.Unuse(); - return failure; + SharedFunctionInfo* function_info = function->shared(); + if (function_info->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function_info->builtin_function_id(); + MaybeObject* maybe_result = CompileCustomCall( + id, object, holder, NULL, function, name); + Object* result; + if (!maybe_result->ToObject(&result)) return maybe_result; + // undefined means bail out to regular compiler. + if (!result->IsUndefined()) return result; } - __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_callback, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + Label miss_in_smi_check; - // Return the generated code. - return GetCode(CALLBACKS, name); -} + GenerateNameCheck(name, &miss_in_smi_check); + // Get the receiver from the stack. + const int argc = arguments().immediate(); + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); -MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { - // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - Label miss; + // Check that the receiver isn't a smi. + if (check != NUMBER_CHECK) { + __ JumpIfSmi(rdx, &miss_in_smi_check); + } - __ IncrementCounter(&Counters::keyed_load_array_length, 1); + // 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); - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &miss); + CallOptimization optimization(function); + int depth = kInvalidProtoDepth; + Label miss; - GenerateLoadArrayLength(masm(), rdx, rcx, &miss); - __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_array_length, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + switch (check) { + case RECEIVER_MAP_CHECK: + __ IncrementCounter(&Counters::call_const, 1); - // Return the generated code. - return GetCode(CALLBACKS, name); -} + 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); -MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, - JSObject* receiver, - JSObject* holder, - Object* value) { - // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - Label miss; + // Allocate space for v8::Arguments implicit values. Must be initialized + // before to call any runtime function. + __ subq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); + } - __ IncrementCounter(&Counters::keyed_load_constant_function, 1); + // Check that the maps haven't changed. + CheckPrototypes(JSObject::cast(object), rdx, holder, + rbx, rax, rdi, name, depth, &miss); - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &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); + } + break; - GenerateLoadConstant(receiver, holder, rdx, rbx, rcx, rdi, - value, name, &miss); - __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_constant_function, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + case STRING_CHECK: + if (!function->IsBuiltin()) { + // Calling non-builtins with a value as receiver requires boxing. + __ jmp(&miss); + } else { + // Check that the object is a two-byte string or a symbol. + __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax); + __ j(above_equal, &miss); + // Check that the maps starting from the prototype haven't changed. + GenerateDirectLoadGlobalFunctionPrototype( + masm(), Context::STRING_FUNCTION_INDEX, rax, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, + rbx, rdx, rdi, name, &miss); + } + break; - // Return the generated code. - return GetCode(CONSTANT_FUNCTION, name); -} + case NUMBER_CHECK: { + if (!function->IsBuiltin()) { + // Calling non-builtins with a value as receiver requires boxing. + __ jmp(&miss); + } else { + Label fast; + // Check that the object is a smi or a heap number. + __ JumpIfSmi(rdx, &fast); + __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax); + __ j(not_equal, &miss); + __ bind(&fast); + // Check that the maps starting from the prototype haven't changed. + GenerateDirectLoadGlobalFunctionPrototype( + masm(), Context::NUMBER_FUNCTION_INDEX, rax, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, + rbx, rdx, rdi, name, &miss); + } + break; + } + case BOOLEAN_CHECK: { + if (!function->IsBuiltin()) { + // Calling non-builtins with a value as receiver requires boxing. + __ jmp(&miss); + } else { + Label fast; + // Check that the object is a boolean. + __ CompareRoot(rdx, Heap::kTrueValueRootIndex); + __ j(equal, &fast); + __ CompareRoot(rdx, Heap::kFalseValueRootIndex); + __ j(not_equal, &miss); + __ bind(&fast); + // Check that the maps starting from the prototype haven't changed. + GenerateDirectLoadGlobalFunctionPrototype( + masm(), Context::BOOLEAN_FUNCTION_INDEX, rax, &miss); + CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, + rbx, rdx, rdi, name, &miss); + } + break; + } -MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { - // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - Label miss; + default: + UNREACHABLE(); + } - __ IncrementCounter(&Counters::keyed_load_function_prototype, 1); + if (depth != kInvalidProtoDepth) { + Failure* failure; + // Move the return address on top of the stack. + __ movq(rax, Operand(rsp, 3 * kPointerSize)); + __ movq(Operand(rsp, 0 * kPointerSize), rax); - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &miss); + // rsp[2 * kPointerSize] is uninitialized, rsp[3 * kPointerSize] contains + // duplicate of return address and will be overwritten. + bool success = GenerateFastApiCall(masm(), optimization, argc, &failure); + if (!success) { + return failure; + } + } else { + __ InvokeFunction(function, arguments(), JUMP_FUNCTION); + } - GenerateLoadFunctionPrototype(masm(), rdx, rcx, rbx, &miss); + // Handle call cache miss. __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_function_prototype, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + if (depth != kInvalidProtoDepth) { + __ addq(rsp, Immediate(kFastApiCallArguments * kPointerSize)); + } + + // Handle call cache miss. + __ bind(&miss_in_smi_check); + Object* obj; + { MaybeObject* maybe_obj = GenerateMissBranch(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(function); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, - JSObject* holder, - String* name) { +MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, + JSObject* holder, + String* name) { // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- Label miss; - __ IncrementCounter(&Counters::keyed_load_interceptor, 1); + GenerateNameCheck(name, &miss); - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &miss); + // Get the number of arguments. + const int argc = arguments().immediate(); LookupResult lookup; LookupPostInterceptor(holder, name, &lookup); - GenerateLoadInterceptor(receiver, - holder, - &lookup, - rdx, - rax, - rcx, - rbx, - rdi, - name, - &miss); + + // Get the receiver from the stack. + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + + CallInterceptorCompiler compiler(this, arguments(), rcx); + Failure* failure; + bool success = compiler.Compile(masm(), + object, + holder, + name, + &lookup, + rdx, + rbx, + rdi, + rax, + &miss, + &failure); + if (!success) { + return failure; + } + + // Restore receiver. + __ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); + + // Check that the function really is a function. + __ JumpIfSmi(rax, &miss); + __ CmpObjectType(rax, JS_FUNCTION_TYPE, rbx); + __ j(not_equal, &miss); + + // Patch the receiver on the stack with the global proxy if + // necessary. + if (object->IsGlobalObject()) { + __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); + __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); + } + + // Invoke the function. + __ movq(rdi, rax); + __ InvokeFunction(rdi, arguments(), JUMP_FUNCTION); + + // Handle load cache miss. __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_interceptor, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + Object* obj; + { MaybeObject* maybe_obj = GenerateMissBranch(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } // Return the generated code. return GetCode(INTERCEPTOR, name); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { +MaybeObject* CallStubCompiler::CompileCallGlobal(JSObject* object, + GlobalObject* holder, + JSGlobalPropertyCell* cell, + JSFunction* function, + String* name) { // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address + // rcx : function name + // rsp[0] : return address + // rsp[8] : argument argc + // rsp[16] : argument argc - 1 + // ... + // rsp[argc * 8] : argument 1 + // rsp[(argc + 1) * 8] : argument 0 = receiver // ----------------------------------- + + SharedFunctionInfo* function_info = function->shared(); + if (function_info->HasBuiltinFunctionId()) { + BuiltinFunctionId id = function_info->builtin_function_id(); + MaybeObject* maybe_result = CompileCustomCall( + id, object, holder, cell, function, name); + Object* result; + if (!maybe_result->ToObject(&result)) return maybe_result; + // undefined means bail out to regular compiler. + if (!result->IsUndefined()) return result; + } + Label miss; - __ IncrementCounter(&Counters::keyed_load_string_length, 1); + GenerateNameCheck(name, &miss); - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &miss); + // Get the number of arguments. + const int argc = arguments().immediate(); - GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss); + GenerateGlobalReceiverCheck(object, holder, name, &miss); + + GenerateLoadFunctionFromCell(cell, function, &miss); + + // Patch the receiver on the stack with the global proxy. + if (object->IsGlobalObject()) { + __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); + __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); + } + + // Setup the context (function already in edi). + __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + + // Jump to the cached code (tail call). + __ IncrementCounter(&Counters::call_global_inline, 1); + ASSERT(function->is_compiled()); + Handle 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); - __ DecrementCounter(&Counters::keyed_load_string_length, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + __ IncrementCounter(&Counters::call_global_inline_miss, 1); + Object* obj; + { MaybeObject* maybe_obj = GenerateMissBranch(); + if (!maybe_obj->ToObject(&obj)) return maybe_obj; + } // Return the generated code. - return GetCode(CALLBACKS, name); + return GetCode(NORMAL, name); } -MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) { +MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, + int index, + Map* transition, + String* name) { // ----------- S t a t e ------------- - // -- rax : key + // -- rax : value + // -- rcx : name // -- rdx : receiver - // -- esp[0] : return address + // -- rsp[0] : return address // ----------------------------------- Label miss; - // Check that the receiver isn't a smi. - __ JumpIfSmi(rdx, &miss); - - // Check that the map matches. - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), - Handle(receiver->map())); - __ j(not_equal, &miss); - - // Check that the key is a smi. - __ JumpIfNotSmi(rax, &miss); - - // Get the elements array. - __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); - __ AssertFastElements(rcx); - - // Check that the key is within bounds. - __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset)); - __ j(above_equal, &miss); - - // Load the result and make sure it's not the hole. - SmiIndex index = masm()->SmiToIndex(rbx, rax, kPointerSizeLog2); - __ movq(rbx, FieldOperand(rcx, - index.reg, - index.scale, - FixedArray::kHeaderSize)); - __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); - __ j(equal, &miss); - __ movq(rax, rbx); - __ ret(0); + // Generate store field code. Preserves receiver and name on jump to miss. + GenerateStoreField(masm(), + object, + index, + transition, + rdx, rcx, rbx, + &miss); + // Handle store cache miss. __ bind(&miss); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + Handle ic(Builtins::builtin(Builtins::StoreIC_Miss)); + __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(NORMAL, NULL); + return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); } @@ -2321,46 +2345,16 @@ MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); __ pop(rbx); // remove the return address - __ push(rdx); // receiver - __ Push(Handle(callback)); // callback info - __ push(rcx); // name - __ push(rax); // value - __ push(rbx); // restore return address - - // Do tail-call to the runtime system. - ExternalReference store_callback_property = - ExternalReference(IC_Utility(IC::kStoreCallbackProperty)); - __ TailCallExternalReference(store_callback_property, 4, 1); - - // Handle store cache miss. - __ bind(&miss); - Handle ic(Builtins::builtin(Builtins::StoreIC_Miss)); - __ Jump(ic, RelocInfo::CODE_TARGET); - - // Return the generated code. - return GetCode(CALLBACKS, name); -} - - -MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, - int index, - Map* transition, - String* name) { - // ----------- S t a t e ------------- - // -- rax : value - // -- rcx : name - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - Label miss; - - // Generate store field code. Preserves receiver and name on jump to miss. - GenerateStoreField(masm(), - object, - index, - transition, - rdx, rcx, rbx, - &miss); + __ push(rdx); // receiver + __ Push(Handle(callback)); // callback info + __ push(rcx); // name + __ push(rax); // value + __ push(rbx); // restore return address + + // Do tail-call to the runtime system. + ExternalReference store_callback_property = + ExternalReference(IC_Utility(IC::kStoreCallbackProperty)); + __ TailCallExternalReference(store_callback_property, 4, 1); // Handle store cache miss. __ bind(&miss); @@ -2368,7 +2362,7 @@ MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, __ Jump(ic, RelocInfo::CODE_TARGET); // Return the generated code. - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); + return GetCode(CALLBACKS, name); } @@ -2455,34 +2449,6 @@ MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, } -MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, - JSObject* receiver, - JSObject* holder, - int index) { - // ----------- S t a t e ------------- - // -- rax : key - // -- rdx : receiver - // -- rsp[0] : return address - // ----------------------------------- - Label miss; - - __ IncrementCounter(&Counters::keyed_load_field, 1); - - // Check that the name has not changed. - __ Cmp(rax, Handle(name)); - __ j(not_equal, &miss); - - GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss); - - __ bind(&miss); - __ DecrementCounter(&Counters::keyed_load_field, 1); - GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); - - // Return the generated code. - return GetCode(FIELD, name); -} - - MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, int index, Map* transition, @@ -2577,414 +2543,444 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized( } -void StubCompiler::GenerateLoadInterceptor(JSObject* object, - JSObject* interceptor_holder, - LookupResult* lookup, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - String* name, - Label* miss) { - ASSERT(interceptor_holder->HasNamedInterceptor()); - ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined()); +MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, + JSObject* object, + JSObject* last) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, miss); + // Chech that receiver is not a smi. + __ JumpIfSmi(rax, &miss); - // So far the most popular follow ups for interceptor loads are FIELD - // and CALLBACKS, so inline only them, other cases may be added - // later. - bool compile_followup_inline = false; - if (lookup->IsProperty() && lookup->IsCacheable()) { - if (lookup->type() == FIELD) { - compile_followup_inline = true; - } else if (lookup->type() == CALLBACKS && - lookup->GetCallbackObject()->IsAccessorInfo() && - AccessorInfo::cast(lookup->GetCallbackObject())->getter() != NULL) { - compile_followup_inline = true; + // Check the maps of the full prototype chain. Also check that + // global property cells up to (but not including) the last object + // in the prototype chain are empty. + CheckPrototypes(object, rax, last, rbx, rdx, rdi, name, &miss); + + // If the last object in the prototype chain is a global object, + // check that the global property cell is empty. + if (last->IsGlobalObject()) { + MaybeObject* cell = GenerateCheckPropertyCell(masm(), + GlobalObject::cast(last), + name, + rdx, + &miss); + if (cell->IsFailure()) { + miss.Unuse(); + return cell; } } - if (compile_followup_inline) { - // Compile the interceptor call, followed by inline code to load the - // property from further up the prototype chain if the call fails. - // Check that the maps haven't changed. - Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder, - scratch1, scratch2, scratch3, - name, miss); - ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1)); + // Return undefined if maps of the full prototype chain are still the + // same and no global property with this name contains a value. + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + __ ret(0); + + __ bind(&miss); + GenerateLoadMiss(masm(), Code::LOAD_IC); + + // Return the generated code. + return GetCode(NONEXISTENT, Heap::empty_string()); +} + + +MaybeObject* LoadStubCompiler::CompileLoadField(JSObject* object, + JSObject* holder, + int index, + String* name) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + GenerateLoadField(object, holder, rax, rbx, rdx, rdi, index, name, &miss); + __ bind(&miss); + GenerateLoadMiss(masm(), Code::LOAD_IC); + + // Return the generated code. + return GetCode(FIELD, name); +} + + +MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, + JSObject* object, + JSObject* holder, + AccessorInfo* callback) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + Failure* failure = Failure::InternalError(); + bool success = GenerateLoadCallback(object, holder, rax, rcx, rdx, rbx, rdi, + callback, name, &miss, &failure); + if (!success) { + miss.Unuse(); + return failure; + } + + __ bind(&miss); + GenerateLoadMiss(masm(), Code::LOAD_IC); + + // Return the generated code. + return GetCode(CALLBACKS, name); +} + + +MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, + JSObject* holder, + Object* value, + String* name) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + GenerateLoadConstant(object, holder, rax, rbx, rdx, rdi, value, name, &miss); + __ bind(&miss); + GenerateLoadMiss(masm(), Code::LOAD_IC); + + // Return the generated code. + return GetCode(CONSTANT_FUNCTION, name); +} + + +MaybeObject* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, + JSObject* holder, + String* name) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + LookupResult lookup; + LookupPostInterceptor(holder, name, &lookup); + + // TODO(368): Compile in the whole chain: all the interceptors in + // prototypes and ultimate answer. + GenerateLoadInterceptor(receiver, + holder, + &lookup, + rax, + rcx, + rdx, + rbx, + rdi, + name, + &miss); + + __ bind(&miss); + GenerateLoadMiss(masm(), Code::LOAD_IC); + + // Return the generated code. + return GetCode(INTERCEPTOR, name); +} + + +MaybeObject* LoadStubCompiler::CompileLoadGlobal(JSObject* object, + GlobalObject* holder, + JSGlobalPropertyCell* cell, + String* name, + bool is_dont_delete) { + // ----------- S t a t e ------------- + // -- rax : receiver + // -- rcx : name + // -- rsp[0] : return address + // ----------------------------------- + Label miss; + + // 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. + if (object != holder) { + __ JumpIfSmi(rax, &miss); + } + + // Check that the maps haven't changed. + CheckPrototypes(object, rax, holder, rbx, rdx, rdi, name, &miss); + + // Get the value from the cell. + __ Move(rbx, Handle(cell)); + __ movq(rbx, FieldOperand(rbx, JSGlobalPropertyCell::kValueOffset)); + + // Check for deleted property if property can actually be deleted. + if (!is_dont_delete) { + __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); + __ j(equal, &miss); + } else if (FLAG_debug_code) { + __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); + __ Check(not_equal, "DontDelete cells can't contain the hole"); + } + + __ IncrementCounter(&Counters::named_load_global_stub, 1); + __ movq(rax, rbx); + __ ret(0); + + __ bind(&miss); + __ IncrementCounter(&Counters::named_load_global_stub_miss, 1); + GenerateLoadMiss(masm(), Code::LOAD_IC); - // Save necessary data before invoking an interceptor. - // Requires a frame to make GC aware of pushed pointers. - __ EnterInternalFrame(); + // Return the generated code. + return GetCode(NORMAL, name); +} - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - // CALLBACKS case needs a receiver to be passed into C++ callback. - __ push(receiver); - } - __ push(holder_reg); - __ push(name_reg); - // Invoke an interceptor. Note: map checks from receiver to - // interceptor's holder has been compiled before (see a caller - // of this method.) - CompileCallLoadPropertyWithInterceptor(masm(), - receiver, - holder_reg, - name_reg, - interceptor_holder); +MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, + JSObject* receiver, + JSObject* holder, + int index) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - // Check if interceptor provided a value for property. If it's - // the case, return immediately. - Label interceptor_failed; - __ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex); - __ j(equal, &interceptor_failed); - __ LeaveInternalFrame(); - __ ret(0); + __ IncrementCounter(&Counters::keyed_load_field, 1); - __ bind(&interceptor_failed); - __ pop(name_reg); - __ pop(holder_reg); - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { - __ pop(receiver); - } + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); - __ LeaveInternalFrame(); + GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss); - // Check that the maps from interceptor's holder to lookup's holder - // haven't changed. And load lookup's holder into |holder| register. - if (interceptor_holder != lookup->holder()) { - holder_reg = CheckPrototypes(interceptor_holder, - holder_reg, - lookup->holder(), - scratch1, - scratch2, - scratch3, - name, - miss); - } + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_field, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); - if (lookup->type() == FIELD) { - // We found FIELD property in prototype chain of interceptor's holder. - // Retrieve a field from field's holder. - GenerateFastPropertyLoad(masm(), rax, holder_reg, - lookup->holder(), lookup->GetFieldIndex()); - __ ret(0); - } else { - // We found CALLBACKS property in prototype chain of interceptor's - // holder. - ASSERT(lookup->type() == CALLBACKS); - ASSERT(lookup->GetCallbackObject()->IsAccessorInfo()); - AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject()); - ASSERT(callback != NULL); - ASSERT(callback->getter() != NULL); + // Return the generated code. + return GetCode(FIELD, name); +} - // Tail call to runtime. - // Important invariant in CALLBACKS case: the code above must be - // structured to never clobber |receiver| register. - __ pop(scratch2); // return address - __ push(receiver); - __ push(holder_reg); - __ Move(holder_reg, Handle(callback)); - __ push(FieldOperand(holder_reg, AccessorInfo::kDataOffset)); - __ push(holder_reg); - __ push(name_reg); - __ push(scratch2); // restore return address - ExternalReference ref = - ExternalReference(IC_Utility(IC::kLoadCallbackProperty)); - __ TailCallExternalReference(ref, 5, 1); - } - } else { // !compile_followup_inline - // Call the runtime system to load the interceptor. - // Check that the maps haven't changed. - Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder, - scratch1, scratch2, scratch3, - name, miss); - __ pop(scratch2); // save old return address - PushInterceptorArguments(masm(), receiver, holder_reg, - name_reg, interceptor_holder); - __ push(scratch2); // restore old return address +MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( + String* name, + JSObject* receiver, + JSObject* holder, + AccessorInfo* callback) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - ExternalReference ref = ExternalReference( - IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); - __ TailCallExternalReference(ref, 5, 1); - } -} + __ IncrementCounter(&Counters::keyed_load_callback, 1); + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); -bool StubCompiler::GenerateLoadCallback(JSObject* object, - JSObject* holder, - Register receiver, - Register name_reg, - Register scratch1, - Register scratch2, - Register scratch3, - AccessorInfo* callback, - String* name, - Label* miss, - Failure** failure) { - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, miss); + Failure* failure = Failure::InternalError(); + bool success = GenerateLoadCallback(receiver, holder, rdx, rax, rbx, rcx, rdi, + callback, name, &miss, &failure); + if (!success) { + miss.Unuse(); + return failure; + } - // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, scratch1, - scratch2, scratch3, name, miss); + __ bind(&miss); - Handle callback_handle(callback); + __ DecrementCounter(&Counters::keyed_load_callback, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); - // Insert additional parameters into the stack frame above return address. - ASSERT(!scratch2.is(reg)); - __ pop(scratch2); // Get return address to place it below. + // Return the generated code. + return GetCode(CALLBACKS, name); +} - __ push(receiver); // receiver - __ push(reg); // holder - if (Heap::InNewSpace(callback_handle->data())) { - __ Move(scratch1, callback_handle); - __ push(FieldOperand(scratch1, AccessorInfo::kDataOffset)); // data - } else { - __ Push(Handle(callback_handle->data())); - } - __ push(name_reg); // name - // Save a pointer to where we pushed the arguments pointer. - // This will be passed as the const AccessorInfo& to the C++ callback. -#ifdef _WIN64 - // Win64 uses first register--rcx--for returned value. - Register accessor_info_arg = r8; - Register name_arg = rdx; -#else - Register accessor_info_arg = rsi; - Register name_arg = rdi; -#endif +MaybeObject* KeyedLoadStubCompiler::CompileLoadConstant(String* name, + JSObject* receiver, + JSObject* holder, + Object* value) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - ASSERT(!name_arg.is(scratch2)); - __ movq(name_arg, rsp); - __ push(scratch2); // Restore return address. + __ IncrementCounter(&Counters::keyed_load_constant_function, 1); - // Do call through the api. - Address getter_address = v8::ToCData
(callback->getter()); - ApiFunction fun(getter_address); + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); - // 3 elements array for v8::Agruments::values_ and handler for name. - const int kStackSpace = 4; + GenerateLoadConstant(receiver, holder, rdx, rbx, rcx, rdi, + value, name, &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_constant_function, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); - // Allocate v8::AccessorInfo in non-GCed stack space. - const int kArgStackSpace = 1; + // Return the generated code. + return GetCode(CONSTANT_FUNCTION, name); +} - __ PrepareCallApiFunction(kArgStackSpace); - __ lea(rax, Operand(name_arg, 3 * kPointerSize)); - // v8::AccessorInfo::args_. - __ movq(StackSpaceOperand(0), rax); +MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, + JSObject* holder, + String* name) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - // The context register (rsi) has been saved in PrepareCallApiFunction and - // could be used to pass arguments. - __ lea(accessor_info_arg, StackSpaceOperand(0)); + __ IncrementCounter(&Counters::keyed_load_interceptor, 1); - // Emitting a stub call may try to allocate (if the code is not - // already generated). Do not allow the assembler to perform a - // garbage collection but instead return the allocation failure - // object. - MaybeObject* result = masm()->TryCallApiFunctionAndReturn(&fun, kStackSpace); - if (result->IsFailure()) { - *failure = Failure::cast(result); - return false; - } - return true; + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); + + LookupResult lookup; + LookupPostInterceptor(holder, name, &lookup); + GenerateLoadInterceptor(receiver, + holder, + &lookup, + rdx, + rax, + rcx, + rbx, + rdi, + name, + &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_interceptor, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + + // Return the generated code. + return GetCode(INTERCEPTOR, name); } -Register StubCompiler::CheckPrototypes(JSObject* object, - Register object_reg, - JSObject* holder, - Register holder_reg, - Register scratch1, - Register scratch2, - String* name, - int save_at_depth, - Label* miss) { - // Make sure there's no overlap between holder and object registers. - ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg)); - ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg) - && !scratch2.is(scratch1)); +MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - // Keep track of the current object in register reg. On the first - // iteration, reg is an alias for object_reg, on later iterations, - // it is an alias for holder_reg. - Register reg = object_reg; - int depth = 0; + __ IncrementCounter(&Counters::keyed_load_array_length, 1); - if (save_at_depth == depth) { - __ movq(Operand(rsp, kPointerSize), object_reg); - } + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); - // Check the maps in the prototype chain. - // Traverse the prototype chain from the object and do map checks. - JSObject* current = object; - while (current != holder) { - depth++; + GenerateLoadArrayLength(masm(), rdx, rcx, &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_array_length, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); - // Only global objects and objects that do not require access - // checks are allowed in stubs. - ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); + // Return the generated code. + return GetCode(CALLBACKS, name); +} - JSObject* prototype = JSObject::cast(current->GetPrototype()); - if (!current->HasFastProperties() && - !current->IsJSGlobalObject() && - !current->IsJSGlobalProxy()) { - if (!name->IsSymbol()) { - MaybeObject* lookup_result = Heap::LookupSymbol(name); - if (lookup_result->IsFailure()) { - set_failure(Failure::cast(lookup_result)); - return reg; - } else { - name = String::cast(lookup_result->ToObjectUnchecked()); - } - } - ASSERT(current->property_dictionary()->FindEntry(name) == - StringDictionary::kNotFound); - GenerateDictionaryNegativeLookup(masm(), - miss, - reg, - name, - scratch1, - scratch2); - __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - reg = holder_reg; // from now the object is in holder_reg - __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); - } else if (Heap::InNewSpace(prototype)) { - // Get the map of the current object. - __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - __ Cmp(scratch1, Handle(current->map())); - // Branch on the result of the map check. - __ j(not_equal, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); +MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - // Restore scratch register to be the map of the object. - // We load the prototype from the map in the scratch register. - __ movq(scratch1, FieldOperand(reg, HeapObject::kMapOffset)); - } - // The prototype is in new space; we cannot store a reference - // to it in the code. Load it from the map. - reg = holder_reg; // from now the object is in holder_reg - __ movq(reg, FieldOperand(scratch1, Map::kPrototypeOffset)); + __ IncrementCounter(&Counters::keyed_load_string_length, 1); - } else { - // Check the map of the current object. - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), - Handle(current->map())); - // Branch on the result of the map check. - __ j(not_equal, miss); - // Check access rights to the global object. This has to happen - // after the map check so that we know that the object is - // actually a global object. - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - } - // The prototype is in old space; load it directly. - reg = holder_reg; // from now the object is in holder_reg - __ Move(reg, Handle(prototype)); - } + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); - if (save_at_depth == depth) { - __ movq(Operand(rsp, kPointerSize), reg); - } + GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_string_length, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); - // Go to the next object in the prototype chain. - current = prototype; - } + // Return the generated code. + return GetCode(CALLBACKS, name); +} - // Check the holder map. - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle(holder->map())); - __ j(not_equal, miss); - // Log the check depth. - LOG(IntEvent("check-maps-depth", depth + 1)); +MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- rsp[0] : return address + // ----------------------------------- + Label miss; - // Perform security check for access to the global object and return - // the holder register. - ASSERT(current == holder); - ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); - if (current->IsJSGlobalProxy()) { - __ CheckAccessGlobalProxy(reg, scratch1, miss); - } + __ IncrementCounter(&Counters::keyed_load_function_prototype, 1); - // 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 - // property cell for the property is still empty. - current = object; - while (current != holder) { - if (current->IsGlobalObject()) { - MaybeObject* cell = GenerateCheckPropertyCell(masm(), - GlobalObject::cast(current), - name, - scratch1, - miss); - if (cell->IsFailure()) { - set_failure(Failure::cast(cell)); - return reg; - } - } - current = JSObject::cast(current->GetPrototype()); - } + // Check that the name has not changed. + __ Cmp(rax, Handle(name)); + __ j(not_equal, &miss); - // Return the register containing the holder. - return reg; + GenerateLoadFunctionPrototype(masm(), rdx, rcx, rbx, &miss); + __ bind(&miss); + __ DecrementCounter(&Counters::keyed_load_function_prototype, 1); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + + // Return the generated code. + return GetCode(CALLBACKS, name); } -void StubCompiler::GenerateLoadField(JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - int index, - String* name, - Label* miss) { - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, miss); +MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) { + // ----------- S t a t e ------------- + // -- rax : key + // -- rdx : receiver + // -- esp[0] : return address + // ----------------------------------- + Label miss; - // Check the prototype chain. - Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + // Check that the receiver isn't a smi. + __ JumpIfSmi(rdx, &miss); - // Get the value from the properties. - GenerateFastPropertyLoad(masm(), rax, reg, holder, index); - __ ret(0); -} + // Check that the map matches. + __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), + Handle(receiver->map())); + __ j(not_equal, &miss); + // Check that the key is a smi. + __ JumpIfNotSmi(rax, &miss); -void StubCompiler::GenerateLoadConstant(JSObject* object, - JSObject* holder, - Register receiver, - Register scratch1, - Register scratch2, - Register scratch3, - Object* value, - String* name, - Label* miss) { - // Check that the receiver isn't a smi. - __ JumpIfSmi(receiver, miss); + // Get the elements array. + __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); + __ AssertFastElements(rcx); - // Check that the maps haven't changed. - Register reg = - CheckPrototypes(object, receiver, holder, - scratch1, scratch2, scratch3, name, miss); + // Check that the key is within bounds. + __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset)); + __ j(above_equal, &miss); - // Return the constant value. - __ Move(rax, Handle(value)); + // Load the result and make sure it's not the hole. + SmiIndex index = masm()->SmiToIndex(rbx, rax, kPointerSizeLog2); + __ movq(rbx, FieldOperand(rcx, + index.reg, + index.scale, + FixedArray::kHeaderSize)); + __ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); + __ j(equal, &miss); + __ movq(rax, rbx); __ ret(0); + + __ bind(&miss); + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); + + // Return the generated code. + return GetCode(NORMAL, NULL); } -- 2.7.4