Index: src/x64/stub-cache-x64.cc |
=================================================================== |
--- src/x64/stub-cache-x64.cc (revision 6139) |
+++ 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. |
// |
@@ -553,7 +454,6 @@ |
// -- 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<JSFunction>(function)); |
@@ -833,6 +733,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 +851,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 +1336,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, |
@@ -1407,7 +1640,7 @@ |
} |
-MaybeObject* CallStubCompiler::CompileStringCharAtCall( |
+MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( |
Object* object, |
JSObject* holder, |
JSGlobalPropertyCell* cell, |
@@ -1440,10 +1673,9 @@ |
CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, |
rbx, rdx, rdi, name, &miss); |
- Register receiver = rax; |
+ Register receiver = rbx; |
Register index = rdi; |
- Register scratch1 = rbx; |
- Register scratch2 = rdx; |
+ Register scratch = rdx; |
Register result = rax; |
__ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); |
if (argc > 0) { |
@@ -1452,23 +1684,22 @@ |
__ LoadRoot(index, Heap::kUndefinedValueRootIndex); |
} |
- 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()); |
+ 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); |
StubRuntimeCallHelper call_helper; |
- char_at_generator.GenerateSlow(masm(), call_helper); |
+ char_code_at_generator.GenerateSlow(masm(), call_helper); |
__ bind(&index_out_of_range); |
- __ LoadRoot(rax, Heap::kEmptyStringRootIndex); |
+ __ LoadRoot(rax, Heap::kNanValueRootIndex); |
__ ret((argc + 1) * kPointerSize); |
__ bind(&miss); |
@@ -1482,7 +1713,7 @@ |
} |
-MaybeObject* CallStubCompiler::CompileStringCharCodeAtCall( |
+MaybeObject* CallStubCompiler::CompileStringCharAtCall( |
Object* object, |
JSObject* holder, |
JSGlobalPropertyCell* cell, |
@@ -1503,6 +1734,7 @@ |
Label miss; |
Label index_out_of_range; |
+ |
GenerateNameCheck(name, &miss); |
// Check that the maps starting from the prototype haven't changed. |
@@ -1514,9 +1746,10 @@ |
CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder, |
rbx, rdx, rdi, name, &miss); |
- Register receiver = rbx; |
+ Register receiver = rax; |
Register index = rdi; |
- Register scratch = rdx; |
+ Register scratch1 = rbx; |
+ Register scratch2 = rdx; |
Register result = rax; |
__ movq(receiver, Operand(rsp, (argc + 1) * kPointerSize)); |
if (argc > 0) { |
@@ -1525,22 +1758,23 @@ |
__ LoadRoot(index, Heap::kUndefinedValueRootIndex); |
} |
- 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()); |
+ 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_code_at_generator.GenerateSlow(masm(), call_helper); |
+ char_at_generator.GenerateSlow(masm(), call_helper); |
__ bind(&index_out_of_range); |
- __ LoadRoot(rax, Heap::kNanValueRootIndex); |
+ __ LoadRoot(rax, Heap::kEmptyStringRootIndex); |
__ ret((argc + 1) * kPointerSize); |
__ bind(&miss); |
@@ -1740,6 +1974,178 @@ |
} |
+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 +2286,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 +2608,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 +2737,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 +2748,52 @@ |
// ----------------------------------- |
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 +2829,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 +2866,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 +2890,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 +2955,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) { |