Chromium Code Reviews| 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 __ |