Chromium Code Reviews| Index: src/x64/stub-cache-x64.cc |
| =================================================================== |
| --- src/x64/stub-cache-x64.cc (revision 6136) |
| +++ src/x64/stub-cache-x64.cc (working copy) |
| @@ -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 @@ |
| } |
| -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<Code> 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<Map>(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<Object>(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 @@ |
| } |
| -// 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); |
| +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)); |
| +} |
| - // Check that the map of the object hasn't changed. |
| - __ Cmp(FieldOperand(receiver_reg, HeapObject::kMapOffset), |
| - Handle<Map>(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<Map>(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<Map>(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); |
| +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<Map>(function->initial_map())); |
| + // Load the prototype from the initial map. |
| + __ movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); |
| } |
| @@ -469,6 +332,54 @@ |
| } |
| +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<Object>(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 @@ |
| } |
| - |
| -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. |
| // |
| @@ -833,6 +734,100 @@ |
| }; |
| +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<Code> 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<Map>(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<Map>(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<Map>(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. |
| @@ -857,10 +852,420 @@ |
| #undef __ |
| - |
| #define __ ACCESS_MASM((masm())) |
| +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; |
| + |
| + if (save_at_depth == depth) { |
| + __ movq(Operand(rsp, kPointerSize), object_reg); |
| + } |
| + |
| + // 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++; |
| + |
| + // Only global objects and objects that do not require access |
| + // checks are allowed in stubs. |
| + ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); |
| + |
| + 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<Map>(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)); |
| + |
| + } else { |
| + // Check the map of the current object. |
| + __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), |
| + Handle<Map>(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<JSObject>(prototype)); |
| + } |
| + |
| + if (save_at_depth == depth) { |
| + __ movq(Operand(rsp, kPointerSize), reg); |
| + } |
| + |
| + // Go to the next object in the prototype chain. |
| + current = prototype; |
| + } |
| + |
| + // Check the holder map. |
| + __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map())); |
| + __ j(not_equal, miss); |
| + |
| + // Log the check depth. |
| + LOG(IntEvent("check-maps-depth", depth + 1)); |
| + |
| + // 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); |
| + } |
| + |
| + // 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()); |
| + } |
| + |
| + // Return the register containing the holder. |
| + return reg; |
| +} |
| + |
| + |
| +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); |
| + |
| + // Check the prototype chain. |
| + Register reg = |
| + CheckPrototypes(object, receiver, holder, |
| + scratch1, scratch2, scratch3, name, miss); |
| + |
| + // Get the value from the properties. |
| + GenerateFastPropertyLoad(masm(), rax, reg, holder, index); |
| + __ ret(0); |
| +} |
| + |
| + |
| +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); |
| + |
| + // Check that the maps haven't changed. |
| + Register reg = |
| + CheckPrototypes(object, receiver, holder, scratch1, |
| + scratch2, scratch3, name, miss); |
| + |
| + Handle<AccessorInfo> callback_handle(callback); |
| + |
| + // Insert additional parameters into the stack frame above return address. |
| + ASSERT(!scratch2.is(reg)); |
| + __ pop(scratch2); // Get return address to place it below. |
| + |
| + __ 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<Object>(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 |
| + |
| + ASSERT(!name_arg.is(scratch2)); |
| + __ movq(name_arg, rsp); |
| + __ push(scratch2); // Restore return address. |
| + |
| + // Do call through the api. |
| + Address getter_address = v8::ToCData<Address>(callback->getter()); |
| + ApiFunction fun(getter_address); |
| + |
| + // 3 elements array for v8::Agruments::values_ and handler for name. |
| + const int kStackSpace = 4; |
| + |
| + // Allocate v8::AccessorInfo in non-GCed stack space. |
| + const int kArgStackSpace = 1; |
| + |
| + __ PrepareCallApiFunction(kArgStackSpace); |
| + __ lea(rax, Operand(name_arg, 3 * kPointerSize)); |
| + |
| + // v8::AccessorInfo::args_. |
| + __ movq(StackSpaceOperand(0), rax); |
| + |
| + // The context register (rsi) has been saved in PrepareCallApiFunction and |
| + // could be used to pass arguments. |
| + __ lea(accessor_info_arg, StackSpaceOperand(0)); |
| + |
| + // 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; |
| +} |
| + |
| + |
| +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 that the maps haven't changed. |
| + Register reg = |
| + CheckPrototypes(object, receiver, holder, |
| + scratch1, scratch2, scratch3, name, miss); |
| + |
| + // Return the constant value. |
| + __ Move(rax, Handle<Object>(value)); |
| + __ ret(0); |
| +} |
| + |
| + |
| +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()); |
| + |
| + // Check that the receiver isn't a smi. |
| + __ JumpIfSmi(receiver, 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; |
| + } |
| + } |
| + |
| + 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)); |
| + |
| + // Save necessary data before invoking an interceptor. |
| + // Requires a frame to make GC aware of pushed pointers. |
| + __ EnterInternalFrame(); |
| + |
| + 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); |
| + |
| + // 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); |
| + |
| + __ bind(&interceptor_failed); |
| + __ pop(name_reg); |
| + __ pop(holder_reg); |
| + if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { |
| + __ pop(receiver); |
| + } |
| + |
| + __ LeaveInternalFrame(); |
| + |
| + // 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); |
| + } |
| + |
| + 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); |
| + |
| + // 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<AccessorInfo>(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 |
| + |
| + ExternalReference ref = ExternalReference( |
| + IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); |
| + __ TailCallExternalReference(ref, 5, 1); |
| + } |
| +} |
| + |
| + |
| void CallStubCompiler::GenerateNameCheck(String* name, Label* miss) { |
| if (kind_ == Code::KEYED_CALL_IC) { |
| __ Cmp(rcx, Handle<String>(name)); |
| @@ -932,177 +1337,6 @@ |
| } |
| -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); |
| - } |
| - |
| - // 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); |
| - } |
| - 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; |
| - } |
| - |
| - // Return the generated code. |
| - return GetCode(function); |
| -} |
| - |
| - |
| MaybeObject* CallStubCompiler::CompileCallField(JSObject* object, |
| JSObject* holder, |
| int index, |
| @@ -1428,7 +1662,6 @@ |
| Label miss; |
| Label index_out_of_range; |
| - |
|
Lasse Reichstein
2011/01/03 16:50:11
I actually prefer the whitespace here, between dec
William Hesse
2011/01/04 08:50:56
Actually, the functions CompileStringCharAtCall an
|
| GenerateNameCheck(name, &miss); |
| // Check that the maps starting from the prototype haven't changed. |
| @@ -1740,6 +1973,177 @@ |
| } |
| +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); |
| + } |
| + |
| + // 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); |
| + } |
| + 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; |
| + } |
| + |
| + // Return the generated code. |
| + return GetCode(function); |
| +} |
| + |
| + |
| MaybeObject* CallStubCompiler::CompileCallInterceptor(JSObject* object, |
| JSObject* holder, |
| String* name) { |
| @@ -1880,53 +2284,263 @@ |
| } |
| -MaybeObject* LoadStubCompiler::CompileLoadCallback(String* name, |
| - JSObject* object, |
| - JSObject* holder, |
| - AccessorInfo* callback) { |
| +MaybeObject* StoreStubCompiler::CompileStoreField(JSObject* object, |
| + int index, |
| + Map* transition, |
| + String* name) { |
| // ----------- S t a t e ------------- |
| - // -- rax : receiver |
| + // -- rax : value |
| // -- rcx : name |
| + // -- rdx : receiver |
| // -- 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; |
| + // 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); |
| + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| + __ Jump(ic, RelocInfo::CODE_TARGET); |
| + |
| + // Return the generated code. |
| + return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); |
| +} |
| + |
| + |
| +MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, |
| + AccessorInfo* callback, |
| + String* name) { |
| + // ----------- S t a t e ------------- |
| + // -- rax : value |
| + // -- rcx : name |
| + // -- rdx : receiver |
| + // -- rsp[0] : return address |
| + // ----------------------------------- |
| + Label miss; |
| + |
| + // Check that the object isn't a smi. |
| + __ JumpIfSmi(rdx, &miss); |
| + |
| + // Check that the map of the object hasn't changed. |
| + __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| + Handle<Map>(object->map())); |
| + __ j(not_equal, &miss); |
| + |
| + // Perform global security token check if needed. |
| + if (object->IsJSGlobalProxy()) { |
| + __ CheckAccessGlobalProxy(rdx, rbx, &miss); |
| } |
| + // Stub never generated for non-global objects that require access |
| + // checks. |
| + ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); |
| + |
| + __ pop(rbx); // remove the return address |
| + __ push(rdx); // receiver |
| + __ Push(Handle<AccessorInfo>(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); |
| - GenerateLoadMiss(masm(), Code::LOAD_IC); |
| + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| + __ Jump(ic, RelocInfo::CODE_TARGET); |
| // Return the generated code. |
| return GetCode(CALLBACKS, name); |
| } |
| -MaybeObject* LoadStubCompiler::CompileLoadConstant(JSObject* object, |
| - JSObject* holder, |
| - Object* value, |
| +MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, |
| + String* name) { |
| + // ----------- S t a t e ------------- |
| + // -- rax : value |
| + // -- rcx : name |
| + // -- rdx : receiver |
| + // -- rsp[0] : return address |
| + // ----------------------------------- |
| + Label miss; |
| + |
| + // Check that the object isn't a smi. |
| + __ JumpIfSmi(rdx, &miss); |
| + |
| + // Check that the map of the object hasn't changed. |
| + __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| + Handle<Map>(receiver->map())); |
| + __ j(not_equal, &miss); |
| + |
| + // Perform global security token check if needed. |
| + if (receiver->IsJSGlobalProxy()) { |
| + __ CheckAccessGlobalProxy(rdx, rbx, &miss); |
| + } |
| + |
| + // Stub never generated for non-global objects that require access |
| + // checks. |
| + ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded()); |
| + |
| + __ pop(rbx); // remove the return address |
| + __ push(rdx); // receiver |
| + __ push(rcx); // name |
| + __ push(rax); // value |
| + __ push(rbx); // restore return address |
| + |
| + // Do tail-call to the runtime system. |
| + ExternalReference store_ic_property = |
| + ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); |
| + __ TailCallExternalReference(store_ic_property, 3, 1); |
| + |
| + // Handle store cache miss. |
| + __ bind(&miss); |
| + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| + __ Jump(ic, RelocInfo::CODE_TARGET); |
| + |
| + // Return the generated code. |
| + return GetCode(INTERCEPTOR, name); |
| +} |
| + |
| + |
| +MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, |
| + JSGlobalPropertyCell* cell, |
| String* name) { |
| // ----------- S t a t e ------------- |
| - // -- rax : receiver |
| + // -- rax : value |
| // -- rcx : name |
| + // -- rdx : receiver |
| // -- rsp[0] : return address |
| // ----------------------------------- |
| Label miss; |
| - GenerateLoadConstant(object, holder, rax, rbx, rdx, rdi, value, name, &miss); |
| + // Check that the map of the global has not changed. |
| + __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| + Handle<Map>(object->map())); |
| + __ j(not_equal, &miss); |
| + |
| + // Store the value in the cell. |
| + __ Move(rcx, Handle<JSGlobalPropertyCell>(cell)); |
| + __ movq(FieldOperand(rcx, JSGlobalPropertyCell::kValueOffset), rax); |
| + |
| + // Return the value (register rax). |
| + __ IncrementCounter(&Counters::named_store_global_inline, 1); |
| + __ ret(0); |
| + |
| + // Handle store cache miss. |
| __ bind(&miss); |
| - GenerateLoadMiss(masm(), Code::LOAD_IC); |
| + __ IncrementCounter(&Counters::named_store_global_inline_miss, 1); |
| + Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| + __ Jump(ic, RelocInfo::CODE_TARGET); |
| // Return the generated code. |
| - return GetCode(CONSTANT_FUNCTION, name); |
| + return GetCode(NORMAL, name); |
| } |
| +MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, |
| + int index, |
| + Map* transition, |
| + String* name) { |
| + // ----------- S t a t e ------------- |
| + // -- rax : value |
| + // -- rcx : key |
| + // -- rdx : receiver |
| + // -- rsp[0] : return address |
| + // ----------------------------------- |
| + Label miss; |
| + |
| + __ IncrementCounter(&Counters::keyed_store_field, 1); |
| + |
| + // Check that the name has not changed. |
| + __ Cmp(rcx, Handle<String>(name)); |
| + __ j(not_equal, &miss); |
| + |
| + // 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); |
| + __ DecrementCounter(&Counters::keyed_store_field, 1); |
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss)); |
| + __ Jump(ic, RelocInfo::CODE_TARGET); |
| + |
| + // Return the generated code. |
| + return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); |
| +} |
| + |
| + |
| +MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized( |
| + JSObject* receiver) { |
| + // ----------- S t a t e ------------- |
| + // -- rax : value |
| + // -- rcx : key |
| + // -- rdx : receiver |
| + // -- 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<Map>(receiver->map())); |
| + __ j(not_equal, &miss); |
| + |
| + // Check that the key is a smi. |
| + __ JumpIfNotSmi(rcx, &miss); |
| + |
| + // Get the elements array and make sure it is a fast element array, not 'cow'. |
| + __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset)); |
| + __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset), |
| + Factory::fixed_array_map()); |
| + __ j(not_equal, &miss); |
| + |
| + // Check that the key is within bounds. |
| + if (receiver->IsJSArray()) { |
| + __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset)); |
| + __ j(above_equal, &miss); |
| + } else { |
| + __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset)); |
| + __ j(above_equal, &miss); |
| + } |
| + |
| + // Do the store and update the write barrier. Make sure to preserve |
| + // the value in register eax. |
| + __ movq(rdx, rax); |
| + __ SmiToInteger32(rcx, rcx); |
| + __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize), |
| + rax); |
| + __ RecordWrite(rdi, 0, rdx, rcx); |
| + |
| + // Done. |
| + __ ret(0); |
| + |
| + // Handle store cache miss. |
| + __ bind(&miss); |
| + Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss)); |
| + __ jmp(ic, RelocInfo::CODE_TARGET); |
| + |
| + // Return the generated code. |
| + return GetCode(NORMAL, NULL); |
| +} |
| + |
| + |
| MaybeObject* LoadStubCompiler::CompileLoadNonexistent(String* name, |
| JSObject* object, |
| JSObject* last) { |
| @@ -1992,6 +2606,53 @@ |
| } |
| +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) { |
| @@ -2074,11 +2735,10 @@ |
| } |
| -MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( |
| - String* name, |
| - JSObject* receiver, |
| - JSObject* holder, |
| - AccessorInfo* callback) { |
| +MaybeObject* KeyedLoadStubCompiler::CompileLoadField(String* name, |
| + JSObject* receiver, |
| + JSObject* holder, |
| + int index) { |
| // ----------- S t a t e ------------- |
| // -- rax : key |
| // -- rdx : receiver |
| @@ -2086,46 +2746,51 @@ |
| // ----------------------------------- |
| Label miss; |
| - __ IncrementCounter(&Counters::keyed_load_callback, 1); |
| + __ IncrementCounter(&Counters::keyed_load_field, 1); |
| // Check that the name has not changed. |
| __ Cmp(rax, Handle<String>(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; |
| - } |
| + GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss); |
| __ bind(&miss); |
| - __ DecrementCounter(&Counters::keyed_load_callback, 1); |
| + __ DecrementCounter(&Counters::keyed_load_field, 1); |
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); |
| // Return the generated code. |
| - return GetCode(CALLBACKS, name); |
| + return GetCode(FIELD, name); |
| } |
| -MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { |
| +MaybeObject* KeyedLoadStubCompiler::CompileLoadCallback( |
| + String* name, |
| + JSObject* receiver, |
| + JSObject* holder, |
| + AccessorInfo* callback) { |
| // ----------- S t a t e ------------- |
| - // -- rax : key |
| - // -- rdx : receiver |
| + // -- rax : key |
| + // -- rdx : receiver |
| // -- rsp[0] : return address |
| // ----------------------------------- |
| Label miss; |
| - __ IncrementCounter(&Counters::keyed_load_array_length, 1); |
| + __ IncrementCounter(&Counters::keyed_load_callback, 1); |
| // Check that the name has not changed. |
| __ Cmp(rax, Handle<String>(name)); |
| __ j(not_equal, &miss); |
| - GenerateLoadArrayLength(masm(), rdx, rcx, &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; |
| + } |
| + |
| __ bind(&miss); |
| - __ DecrementCounter(&Counters::keyed_load_array_length, 1); |
| + __ DecrementCounter(&Counters::keyed_load_callback, 1); |
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); |
| // Return the generated code. |
| @@ -2161,30 +2826,6 @@ |
| } |
| -MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { |
| - // ----------- S t a t e ------------- |
| - // -- rax : key |
| - // -- rdx : receiver |
| - // -- rsp[0] : return address |
| - // ----------------------------------- |
| - Label miss; |
| - |
| - __ IncrementCounter(&Counters::keyed_load_function_prototype, 1); |
| - |
| - // Check that the name has not changed. |
| - __ Cmp(rax, Handle<String>(name)); |
| - __ j(not_equal, &miss); |
| - |
| - 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); |
| -} |
| - |
| - |
| MaybeObject* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver, |
| JSObject* holder, |
| String* name) { |
| @@ -2222,23 +2863,23 @@ |
| } |
| -MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { |
| +MaybeObject* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) { |
| // ----------- S t a t e ------------- |
| // -- rax : key |
| // -- rdx : receiver |
| - // -- rsp[0] : return address |
| + // -- rsp[0] : return address |
| // ----------------------------------- |
| Label miss; |
| - __ IncrementCounter(&Counters::keyed_load_string_length, 1); |
| + __ IncrementCounter(&Counters::keyed_load_array_length, 1); |
| // Check that the name has not changed. |
| __ Cmp(rax, Handle<String>(name)); |
| __ j(not_equal, &miss); |
| - GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss); |
| + GenerateLoadArrayLength(masm(), rdx, rcx, &miss); |
| __ bind(&miss); |
| - __ DecrementCounter(&Counters::keyed_load_string_length, 1); |
| + __ DecrementCounter(&Counters::keyed_load_array_length, 1); |
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); |
| // Return the generated code. |
| @@ -2246,287 +2887,59 @@ |
| } |
| -MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) { |
| +MaybeObject* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) { |
| // ----------- S t a t e ------------- |
| // -- rax : key |
| // -- rdx : receiver |
| - // -- esp[0] : return address |
| + // -- rsp[0] : return address |
| // ----------------------------------- |
| Label miss; |
| - // Check that the receiver isn't a smi. |
| - __ JumpIfSmi(rdx, &miss); |
| + __ IncrementCounter(&Counters::keyed_load_string_length, 1); |
| - // Check that the map matches. |
| - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| - Handle<Map>(receiver->map())); |
| + // Check that the name has not changed. |
| + __ Cmp(rax, Handle<String>(name)); |
| __ 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); |
| - |
| + GenerateLoadStringLength(masm(), rdx, rcx, rbx, &miss); |
| __ bind(&miss); |
| + __ DecrementCounter(&Counters::keyed_load_string_length, 1); |
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); |
| // Return the generated code. |
| - return GetCode(NORMAL, NULL); |
| -} |
| - |
| - |
| -MaybeObject* StoreStubCompiler::CompileStoreCallback(JSObject* object, |
| - AccessorInfo* callback, |
| - String* name) { |
| - // ----------- S t a t e ------------- |
| - // -- rax : value |
| - // -- rcx : name |
| - // -- rdx : receiver |
| - // -- rsp[0] : return address |
| - // ----------------------------------- |
| - Label miss; |
| - |
| - // Check that the object isn't a smi. |
| - __ JumpIfSmi(rdx, &miss); |
| - |
| - // Check that the map of the object hasn't changed. |
| - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| - Handle<Map>(object->map())); |
| - __ j(not_equal, &miss); |
| - |
| - // Perform global security token check if needed. |
| - if (object->IsJSGlobalProxy()) { |
| - __ CheckAccessGlobalProxy(rdx, rbx, &miss); |
| - } |
| - |
| - // Stub never generated for non-global objects that require access |
| - // checks. |
| - ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded()); |
| - |
| - __ pop(rbx); // remove the return address |
| - __ push(rdx); // receiver |
| - __ Push(Handle<AccessorInfo>(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<Code> 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) { |
| +MaybeObject* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) { |
| // ----------- S t a t e ------------- |
| - // -- rax : value |
| - // -- rcx : name |
| + // -- rax : key |
| // -- 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); |
| - |
| - // Handle store cache miss. |
| - __ bind(&miss); |
| - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| - __ Jump(ic, RelocInfo::CODE_TARGET); |
| - |
| - // Return the generated code. |
| - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); |
| -} |
| - |
| - |
| -MaybeObject* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver, |
| - String* name) { |
| - // ----------- S t a t e ------------- |
| - // -- rax : value |
| - // -- rcx : name |
| - // -- rdx : receiver |
| - // -- rsp[0] : return address |
| - // ----------------------------------- |
| - Label miss; |
| - |
| - // Check that the object isn't a smi. |
| - __ JumpIfSmi(rdx, &miss); |
| - |
| - // Check that the map of the object hasn't changed. |
| - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| - Handle<Map>(receiver->map())); |
| - __ j(not_equal, &miss); |
| - |
| - // Perform global security token check if needed. |
| - if (receiver->IsJSGlobalProxy()) { |
| - __ CheckAccessGlobalProxy(rdx, rbx, &miss); |
| - } |
| - |
| - // Stub never generated for non-global objects that require access |
| - // checks. |
| - ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded()); |
| - |
| - __ pop(rbx); // remove the return address |
| - __ push(rdx); // receiver |
| - __ push(rcx); // name |
| - __ push(rax); // value |
| - __ push(rbx); // restore return address |
| - |
| - // Do tail-call to the runtime system. |
| - ExternalReference store_ic_property = |
| - ExternalReference(IC_Utility(IC::kStoreInterceptorProperty)); |
| - __ TailCallExternalReference(store_ic_property, 3, 1); |
| - |
| - // Handle store cache miss. |
| - __ bind(&miss); |
| - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| - __ Jump(ic, RelocInfo::CODE_TARGET); |
| - |
| - // Return the generated code. |
| - return GetCode(INTERCEPTOR, name); |
| -} |
| - |
| - |
| -MaybeObject* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object, |
| - JSGlobalPropertyCell* cell, |
| - String* name) { |
| - // ----------- S t a t e ------------- |
| - // -- rax : value |
| - // -- rcx : name |
| - // -- rdx : receiver |
| - // -- rsp[0] : return address |
| - // ----------------------------------- |
| - Label miss; |
| - |
| - // Check that the map of the global has not changed. |
| - __ Cmp(FieldOperand(rdx, HeapObject::kMapOffset), |
| - Handle<Map>(object->map())); |
| - __ j(not_equal, &miss); |
| - |
| - // Store the value in the cell. |
| - __ Move(rcx, Handle<JSGlobalPropertyCell>(cell)); |
| - __ movq(FieldOperand(rcx, JSGlobalPropertyCell::kValueOffset), rax); |
| - |
| - // Return the value (register rax). |
| - __ IncrementCounter(&Counters::named_store_global_inline, 1); |
| - __ ret(0); |
| - |
| - // Handle store cache miss. |
| - __ bind(&miss); |
| - __ IncrementCounter(&Counters::named_store_global_inline_miss, 1); |
| - Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss)); |
| - __ Jump(ic, RelocInfo::CODE_TARGET); |
| - |
| - // Return the generated code. |
| - return GetCode(NORMAL, name); |
| -} |
| - |
| - |
| -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); |
| + __ IncrementCounter(&Counters::keyed_load_function_prototype, 1); |
| // Check that the name has not changed. |
| __ Cmp(rax, Handle<String>(name)); |
| __ j(not_equal, &miss); |
| - GenerateLoadField(receiver, holder, rdx, rbx, rcx, rdi, index, name, &miss); |
| - |
| + GenerateLoadFunctionPrototype(masm(), rdx, rcx, rbx, &miss); |
| __ bind(&miss); |
| - __ DecrementCounter(&Counters::keyed_load_field, 1); |
| + __ DecrementCounter(&Counters::keyed_load_function_prototype, 1); |
| GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); |
| // Return the generated code. |
| - return GetCode(FIELD, name); |
| + return GetCode(CALLBACKS, name); |
| } |
| -MaybeObject* KeyedStoreStubCompiler::CompileStoreField(JSObject* object, |
| - int index, |
| - Map* transition, |
| - String* name) { |
| +MaybeObject* KeyedLoadStubCompiler::CompileLoadSpecialized(JSObject* receiver) { |
| // ----------- S t a t e ------------- |
| - // -- rax : value |
| - // -- rcx : key |
| - // -- rdx : receiver |
| - // -- rsp[0] : return address |
| - // ----------------------------------- |
| - Label miss; |
| - |
| - __ IncrementCounter(&Counters::keyed_store_field, 1); |
| - |
| - // Check that the name has not changed. |
| - __ Cmp(rcx, Handle<String>(name)); |
| - __ j(not_equal, &miss); |
| - |
| - // 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); |
| - __ DecrementCounter(&Counters::keyed_store_field, 1); |
| - Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss)); |
| - __ Jump(ic, RelocInfo::CODE_TARGET); |
| - |
| - // Return the generated code. |
| - return GetCode(transition == NULL ? FIELD : MAP_TRANSITION, name); |
| -} |
| - |
| - |
| -MaybeObject* KeyedStoreStubCompiler::CompileStoreSpecialized( |
| - JSObject* receiver) { |
| - // ----------- S t a t e ------------- |
| - // -- rax : value |
| - // -- rcx : key |
| + // -- rax : key |
| // -- rdx : receiver |
| - // -- rsp[0] : return address |
| + // -- esp[0] : return address |
| // ----------------------------------- |
| Label miss; |
| @@ -2539,455 +2952,35 @@ |
| __ j(not_equal, &miss); |
| // Check that the key is a smi. |
| - __ JumpIfNotSmi(rcx, &miss); |
| + __ JumpIfNotSmi(rax, &miss); |
| - // Get the elements array and make sure it is a fast element array, not 'cow'. |
| - __ movq(rdi, FieldOperand(rdx, JSObject::kElementsOffset)); |
| - __ Cmp(FieldOperand(rdi, HeapObject::kMapOffset), |
| - Factory::fixed_array_map()); |
| - __ j(not_equal, &miss); |
| + // Get the elements array. |
| + __ movq(rcx, FieldOperand(rdx, JSObject::kElementsOffset)); |
| + __ AssertFastElements(rcx); |
| // Check that the key is within bounds. |
| - if (receiver->IsJSArray()) { |
| - __ SmiCompare(rcx, FieldOperand(rdx, JSArray::kLengthOffset)); |
| - __ j(above_equal, &miss); |
| - } else { |
| - __ SmiCompare(rcx, FieldOperand(rdi, FixedArray::kLengthOffset)); |
| - __ j(above_equal, &miss); |
| - } |
| + __ SmiCompare(rax, FieldOperand(rcx, FixedArray::kLengthOffset)); |
| + __ j(above_equal, &miss); |
| - // Do the store and update the write barrier. Make sure to preserve |
| - // the value in register eax. |
| - __ movq(rdx, rax); |
| - __ SmiToInteger32(rcx, rcx); |
| - __ movq(FieldOperand(rdi, rcx, times_pointer_size, FixedArray::kHeaderSize), |
| - rax); |
| - __ RecordWrite(rdi, 0, rdx, rcx); |
| - |
| - // Done. |
| + // 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); |
| - // Handle store cache miss. |
| __ bind(&miss); |
| - Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Miss)); |
| - __ jmp(ic, RelocInfo::CODE_TARGET); |
| + GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC); |
| // Return the generated code. |
| return GetCode(NORMAL, NULL); |
| } |
| -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()); |
| - |
| - // Check that the receiver isn't a smi. |
| - __ JumpIfSmi(receiver, 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; |
| - } |
| - } |
| - |
| - 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)); |
| - |
| - // Save necessary data before invoking an interceptor. |
| - // Requires a frame to make GC aware of pushed pointers. |
| - __ EnterInternalFrame(); |
| - |
| - 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); |
| - |
| - // 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); |
| - |
| - __ bind(&interceptor_failed); |
| - __ pop(name_reg); |
| - __ pop(holder_reg); |
| - if (lookup->type() == CALLBACKS && !receiver.is(holder_reg)) { |
| - __ pop(receiver); |
| - } |
| - |
| - __ LeaveInternalFrame(); |
| - |
| - // 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); |
| - } |
| - |
| - 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); |
| - |
| - // 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<AccessorInfo>(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 |
| - |
| - ExternalReference ref = ExternalReference( |
| - IC_Utility(IC::kLoadPropertyWithInterceptorForLoad)); |
| - __ TailCallExternalReference(ref, 5, 1); |
| - } |
| -} |
| - |
| - |
| -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); |
| - |
| - // Check that the maps haven't changed. |
| - Register reg = |
| - CheckPrototypes(object, receiver, holder, scratch1, |
| - scratch2, scratch3, name, miss); |
| - |
| - Handle<AccessorInfo> callback_handle(callback); |
| - |
| - // Insert additional parameters into the stack frame above return address. |
| - ASSERT(!scratch2.is(reg)); |
| - __ pop(scratch2); // Get return address to place it below. |
| - |
| - __ 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<Object>(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 |
| - |
| - ASSERT(!name_arg.is(scratch2)); |
| - __ movq(name_arg, rsp); |
| - __ push(scratch2); // Restore return address. |
| - |
| - // Do call through the api. |
| - Address getter_address = v8::ToCData<Address>(callback->getter()); |
| - ApiFunction fun(getter_address); |
| - |
| - // 3 elements array for v8::Agruments::values_ and handler for name. |
| - const int kStackSpace = 4; |
| - |
| - // Allocate v8::AccessorInfo in non-GCed stack space. |
| - const int kArgStackSpace = 1; |
| - |
| - __ PrepareCallApiFunction(kArgStackSpace); |
| - __ lea(rax, Operand(name_arg, 3 * kPointerSize)); |
| - |
| - // v8::AccessorInfo::args_. |
| - __ movq(StackSpaceOperand(0), rax); |
| - |
| - // The context register (rsi) has been saved in PrepareCallApiFunction and |
| - // could be used to pass arguments. |
| - __ lea(accessor_info_arg, StackSpaceOperand(0)); |
| - |
| - // 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; |
| -} |
| - |
| - |
| -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; |
| - |
| - if (save_at_depth == depth) { |
| - __ movq(Operand(rsp, kPointerSize), object_reg); |
| - } |
| - |
| - // 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++; |
| - |
| - // Only global objects and objects that do not require access |
| - // checks are allowed in stubs. |
| - ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded()); |
| - |
| - 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<Map>(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)); |
| - |
| - } else { |
| - // Check the map of the current object. |
| - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), |
| - Handle<Map>(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<JSObject>(prototype)); |
| - } |
| - |
| - if (save_at_depth == depth) { |
| - __ movq(Operand(rsp, kPointerSize), reg); |
| - } |
| - |
| - // Go to the next object in the prototype chain. |
| - current = prototype; |
| - } |
| - |
| - // Check the holder map. |
| - __ Cmp(FieldOperand(reg, HeapObject::kMapOffset), Handle<Map>(holder->map())); |
| - __ j(not_equal, miss); |
| - |
| - // Log the check depth. |
| - LOG(IntEvent("check-maps-depth", depth + 1)); |
| - |
| - // 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); |
| - } |
| - |
| - // 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()); |
| - } |
| - |
| - // Return the register containing the holder. |
| - return reg; |
| -} |
| - |
| - |
| -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); |
| - |
| - // Check the prototype chain. |
| - Register reg = |
| - CheckPrototypes(object, receiver, holder, |
| - scratch1, scratch2, scratch3, name, miss); |
| - |
| - // Get the value from the properties. |
| - GenerateFastPropertyLoad(masm(), rax, reg, holder, index); |
| - __ ret(0); |
| -} |
| - |
| - |
| -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 that the maps haven't changed. |
| - Register reg = |
| - CheckPrototypes(object, receiver, holder, |
| - scratch1, scratch2, scratch3, name, miss); |
| - |
| - // Return the constant value. |
| - __ Move(rax, Handle<Object>(value)); |
| - __ ret(0); |
| -} |
| - |
| - |
| // Specialized stub for constructing objects from functions which only have only |
| // simple assignments of the form this.x = ...; in their body. |
| MaybeObject* ConstructStubCompiler::CompileConstructStub(JSFunction* function) { |