Index: src/x64/stub-cache-x64.cc |
=================================================================== |
--- src/x64/stub-cache-x64.cc (revision 2483) |
+++ src/x64/stub-cache-x64.cc (working copy) |
@@ -36,18 +36,141 @@ |
namespace v8 { |
namespace internal { |
-#define __ ACCESS_MASM((&masm_)) |
+#define __ ACCESS_MASM((masm())) |
-Object* CallStubCompiler::CompileCallConstant(Object* a, |
- JSObject* b, |
- JSFunction* c, |
- String* d, |
- StubCompiler::CheckType e) { |
- // TODO(X64): Implement a real stub. |
- return Failure::InternalError(); |
+Object* CallStubCompiler::CompileCallConstant(Object* object, |
+ JSObject* holder, |
+ JSFunction* function, |
+ String* name, |
+ StubCompiler::CheckType check) { |
+ // ----------- S t a t e ------------- |
+ // ----------------------------------- |
+ // rsp[0] return address |
+ // rsp[8] argument argc |
+ // rsp[16] argument argc - 1 |
+ // ... |
+ // rsp[argc * 8] argument 1 |
+ // rsp[(argc + 1) * 8] argument 0 = reciever |
+ // rsp[(argc + 2) * 8] function name |
+ |
+ Label miss; |
+ |
+ // 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) { |
+ __ testl(rdx, Immediate(kSmiTagMask)); |
+ __ j(zero, &miss); |
+ } |
+ |
+ // Make sure that it's okay not to patch the on stack receiver |
+ // unless we're doing a receiver map check. |
+ ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK); |
+ |
+ switch (check) { |
+ case RECEIVER_MAP_CHECK: |
+ // Check that the maps haven't changed. |
+ CheckPrototypes(JSObject::cast(object), rdx, holder, |
+ rbx, rcx, name, &miss); |
+ |
+ // Patch the receiver on the stack with the global proxy if |
+ // necessary. |
+ if (object->IsGlobalObject()) { |
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset)); |
+ __ movq(Operand(rsp, (argc + 1) * kPointerSize), rdx); |
+ } |
+ break; |
+ |
+ case STRING_CHECK: |
+ // Check that the object is a two-byte string or a symbol. |
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx); |
+ __ j(above_equal, &miss); |
+ // Check that the maps starting from the prototype haven't changed. |
+ GenerateLoadGlobalFunctionPrototype(masm(), |
+ Context::STRING_FUNCTION_INDEX, |
+ rcx); |
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder, |
+ rbx, rdx, name, &miss); |
+ break; |
+ |
+ case NUMBER_CHECK: { |
+ Label fast; |
+ // Check that the object is a smi or a heap number. |
+ __ testl(rdx, Immediate(kSmiTagMask)); |
+ __ j(zero, &fast); |
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx); |
+ __ j(not_equal, &miss); |
+ __ bind(&fast); |
+ // Check that the maps starting from the prototype haven't changed. |
+ GenerateLoadGlobalFunctionPrototype(masm(), |
+ Context::NUMBER_FUNCTION_INDEX, |
+ rcx); |
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder, |
+ rbx, rdx, name, &miss); |
+ break; |
+ } |
+ |
+ case BOOLEAN_CHECK: { |
+ Label fast; |
+ // Check that the object is a boolean. |
+ __ Cmp(rdx, Factory::true_value()); |
+ __ j(equal, &fast); |
+ __ Cmp(rdx, Factory::false_value()); |
+ __ j(not_equal, &miss); |
+ __ bind(&fast); |
+ // Check that the maps starting from the prototype haven't changed. |
+ GenerateLoadGlobalFunctionPrototype(masm(), |
+ Context::BOOLEAN_FUNCTION_INDEX, |
+ rcx); |
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder, |
+ rbx, rdx, name, &miss); |
+ break; |
+ } |
+ |
+ case JSARRAY_HAS_FAST_ELEMENTS_CHECK: |
+ CheckPrototypes(JSObject::cast(object), rdx, holder, |
+ rbx, rcx, name, &miss); |
+ // Make sure object->elements()->map() != Heap::dictionary_array_map() |
+ // Get the elements array of the object. |
+ __ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset)); |
+ // Check that the object is in fast mode (not dictionary). |
+ __ Cmp(FieldOperand(rbx, HeapObject::kMapOffset), |
+ Factory::hash_table_map()); |
+ __ j(equal, &miss); |
+ break; |
+ |
+ default: |
+ UNREACHABLE(); |
+ } |
+ |
+ // Get the function and setup the context. |
+ __ Move(rdi, Handle<JSFunction>(function)); |
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
+ |
+ // Jump to the cached code (tail call). |
+ ASSERT(function->is_compiled()); |
+ Handle<Code> code(function->code()); |
+ ParameterCount expected(function->shared()->formal_parameter_count()); |
+ __ InvokeCode(code, expected, arguments(), |
+ RelocInfo::CODE_TARGET, JUMP_FUNCTION); |
+ |
+ // Handle call cache miss. |
+ __ bind(&miss); |
+ Handle<Code> ic = ComputeCallMiss(arguments().immediate()); |
+ __ Jump(ic, RelocInfo::CODE_TARGET); |
+ |
+ // Return the generated code. |
+ String* function_name = NULL; |
+ if (function->shared()->name()->IsString()) { |
+ function_name = String::cast(function->shared()->name()); |
+ } |
+ return GetCode(CONSTANT_FUNCTION, function_name); |
} |
+ |
Object* CallStubCompiler::CompileCallField(Object* a, |
JSObject* b, |
int c, |
@@ -175,6 +298,61 @@ |
return GetCodeWithFlags(flags, "LazyCompileStub"); |
} |
+ |
+Register StubCompiler::CheckPrototypes(JSObject* object, |
+ Register object_reg, |
+ JSObject* holder, |
+ Register holder_reg, |
+ Register scratch, |
+ String* name, |
+ Label* miss) { |
+ // Check that the maps haven't changed. |
+ Register result = |
+ __ CheckMaps(object, object_reg, holder, holder_reg, scratch, miss); |
+ |
+ // If we've skipped any global objects, it's not enough to verify |
+ // that their maps haven't changed. |
+ while (object != holder) { |
+ if (object->IsGlobalObject()) { |
+ GlobalObject* global = GlobalObject::cast(object); |
+ Object* probe = global->EnsurePropertyCell(name); |
+ if (probe->IsFailure()) { |
+ set_failure(Failure::cast(probe)); |
+ return result; |
+ } |
+ JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(probe); |
+ ASSERT(cell->value()->IsTheHole()); |
+ __ Move(scratch, Handle<Object>(cell)); |
+ __ Cmp(FieldOperand(scratch, JSGlobalPropertyCell::kValueOffset), |
+ Factory::the_hole_value()); |
+ __ j(not_equal, miss); |
+ } |
+ object = JSObject::cast(object->GetPrototype()); |
+ } |
+ |
+ // Return the register containing the holder. |
+ return result; |
+} |
+ |
+ |
+void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm, |
+ int index, |
+ Register prototype) { |
+ // Load the global or builtins object from the current context. |
Kasper Lund
2009/07/16 12:36:20
Why are we passing masm to this function? Isn't th
|
+ masm->movq(prototype, |
+ Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); |
+ // Load the global context from the global or builtins object. |
+ masm->movq(prototype, |
+ FieldOperand(prototype, GlobalObject::kGlobalContextOffset)); |
+ // Load the function from the global context. |
+ masm->movq(prototype, Operand(prototype, Context::SlotOffset(index))); |
+ // Load the initial map. The global functions all have initial maps. |
+ masm->movq(prototype, |
+ FieldOperand(prototype, JSFunction::kPrototypeOrInitialMapOffset)); |
+ // Load the prototype from the initial map. |
+ masm->movq(prototype, FieldOperand(prototype, Map::kPrototypeOffset)); |
+} |
+ |
#undef __ |