Commit 29bcccb4 authored by whesse@chromium.org's avatar whesse@chromium.org

X64 Implementation: Implement Generate_Function(Call,Apply)

Review URL: http://codereview.chromium.org/147205

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2287 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent b9995e03
......@@ -654,6 +654,12 @@ Object* Object::GetProperty(String* key, PropertyAttributes* attributes) {
#define WRITE_INT_FIELD(p, offset, value) \
(*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
#define READ_INTPTR_FIELD(p, offset) \
(*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)))
#define WRITE_INTPTR_FIELD(p, offset, value) \
(*reinterpret_cast<intptr_t*>(FIELD_ADDR(p, offset)) = value)
#define READ_UINT32_FIELD(p, offset) \
(*reinterpret_cast<uint32_t*>(FIELD_ADDR(p, offset)))
......@@ -2303,12 +2309,12 @@ void JSBuiltinsObject::set_javascript_builtin(Builtins::JavaScript id,
Address Proxy::proxy() {
return AddressFrom<Address>(READ_INT_FIELD(this, kProxyOffset));
return AddressFrom<Address>(READ_INTPTR_FIELD(this, kProxyOffset));
}
void Proxy::set_proxy(Address value) {
WRITE_INT_FIELD(this, kProxyOffset, OffsetFrom(value));
WRITE_INTPTR_FIELD(this, kProxyOffset, OffsetFrom(value));
}
......
......@@ -168,14 +168,292 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
}
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xBEFA)); // Debugging aid.
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Stack Layout:
// rsp: return address
// +1: Argument n
// +2: Argument n-1
// ...
// +n: Argument 1 = receiver
// +n+1: Argument 0 = function to call
//
// rax contains the number of arguments, n, not counting the function.
//
// 1. Make sure we have at least one argument.
{ Label done;
__ testq(rax, rax);
__ j(not_zero, &done);
__ pop(rbx);
__ Push(Factory::undefined_value());
__ push(rbx);
__ incq(rax);
__ bind(&done);
}
// 2. Get the function to call from the stack.
{ Label done, non_function, function;
// The function to call is at position n+1 on the stack.
__ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize));
__ testl(rdi, Immediate(kSmiTagMask));
__ j(zero, &non_function);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
__ j(equal, &function);
// Non-function called: Clear the function to force exception.
__ bind(&non_function);
__ xor_(rdi, rdi);
__ jmp(&done);
// Function called: Change context eagerly to get the right global object.
__ bind(&function);
__ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
__ bind(&done);
}
// 3. Make sure first argument is an object; convert if necessary.
{ Label call_to_object, use_global_receiver, patch_receiver, done;
__ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
__ testl(rbx, Immediate(kSmiTagMask));
__ j(zero, &call_to_object);
__ Cmp(rbx, Factory::null_value());
__ j(equal, &use_global_receiver);
__ Cmp(rbx, Factory::undefined_value());
__ j(equal, &use_global_receiver);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
__ j(below, &call_to_object);
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
__ j(below_equal, &done);
__ bind(&call_to_object);
__ EnterInternalFrame(); // preserves rax, rbx, rdi
// Store the arguments count on the stack (smi tagged).
ASSERT(kSmiTag == 0);
__ shl(rax, Immediate(kSmiTagSize));
__ push(rax);
__ push(rdi); // save edi across the call
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
__ pop(rdi); // restore edi after the call
// Get the arguments count and untag it.
__ pop(rax);
__ shr(rax, Immediate(kSmiTagSize));
__ LeaveInternalFrame();
__ jmp(&patch_receiver);
// Use the global receiver object from the called function as the receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ movq(rbx, FieldOperand(rsi, kGlobalIndex));
__ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
__ bind(&patch_receiver);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
__ bind(&done);
}
// 4. Shift stuff one slot down the stack.
{ Label loop;
__ lea(rcx, Operand(rax, +1)); // +1 ~ copy receiver too
__ bind(&loop);
__ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
__ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
__ decq(rcx);
__ j(not_zero, &loop);
}
// 5. Remove TOS (copy of last arguments), but keep return address.
__ pop(rbx);
__ pop(rcx);
__ push(rbx);
__ decq(rax);
// 6. Check that function really was a function and get the code to
// call from the function and check that the number of expected
// arguments matches what we're providing.
{ Label invoke, trampoline;
__ testq(rdi, rdi);
__ j(not_zero, &invoke);
__ xor_(rbx, rbx);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
__ bind(&trampoline);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
__ bind(&invoke);
__ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ movsxlq(rbx,
FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
__ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
__ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
__ cmpq(rax, rbx);
__ j(not_equal, &trampoline);
}
// 7. Jump (tail-call) to the code in register edx without checking arguments.
ParameterCount expected(0);
__ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
}
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xBEFC)); // Debugging aid.
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// Stack at entry:
// rsp: return address
// rsp+8: arguments
// rsp+16: receiver ("this")
// rsp+24: function
__ EnterInternalFrame();
// Stack frame:
// rbp: Old base pointer
// rbp[1]: return address
// rbp[2]: function arguments
// rbp[3]: receiver
// rbp[4]: function
static const int kArgumentsOffset = 2 * kPointerSize;
static const int kReceiverOffset = 3 * kPointerSize;
static const int kFunctionOffset = 4 * kPointerSize;
__ push(Operand(rbp, kFunctionOffset));
__ push(Operand(rbp, kArgumentsOffset));
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
if (FLAG_check_stack) {
// We need to catch preemptions right here, otherwise an unlucky preemption
// could show up as a failed apply.
Label retry_preemption;
Label no_preemption;
__ bind(&retry_preemption);
ExternalReference stack_guard_limit =
ExternalReference::address_of_stack_guard_limit();
__ movq(kScratchRegister, stack_guard_limit);
__ movq(rcx, rsp);
__ subq(rcx, Operand(kScratchRegister, 0));
// rcx contains the difference between the stack limit and the stack top.
// We use it below to check that there is enough room for the arguments.
__ j(above, &no_preemption);
// Preemption!
// Because runtime functions always remove the receiver from the stack, we
// have to fake one to avoid underflowing the stack.
__ push(rax);
__ push(Immediate(Smi::FromInt(0)));
// Do call to runtime routine.
__ CallRuntime(Runtime::kStackGuard, 1);
__ pop(rax);
__ jmp(&retry_preemption);
__ bind(&no_preemption);
Label okay;
// Make rdx the space we need for the array when it is unrolled onto the
// stack.
__ movq(rdx, rax);
__ shl(rdx, Immediate(kPointerSizeLog2 - kSmiTagSize));
__ cmpq(rcx, rdx);
__ j(greater, &okay);
// Too bad: Out of stack space.
__ push(Operand(rbp, kFunctionOffset));
__ push(rax);
__ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
__ bind(&okay);
}
// Push current index and limit.
const int kLimitOffset =
StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize;
const int kIndexOffset = kLimitOffset - 1 * kPointerSize;
__ push(rax); // limit
__ push(Immediate(0)); // index
// Change context eagerly to get the right global object if
// necessary.
__ movq(rdi, Operand(rbp, kFunctionOffset));
__ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
// Compute the receiver.
Label call_to_object, use_global_receiver, push_receiver;
__ movq(rbx, Operand(rbp, kReceiverOffset));
__ testl(rbx, Immediate(kSmiTagMask));
__ j(zero, &call_to_object);
__ Cmp(rbx, Factory::null_value());
__ j(equal, &use_global_receiver);
__ Cmp(rbx, Factory::undefined_value());
__ j(equal, &use_global_receiver);
// If given receiver is already a JavaScript object then there's no
// reason for converting it.
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
__ j(less, &call_to_object);
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
__ j(less_equal, &push_receiver);
// Convert the receiver to an object.
__ bind(&call_to_object);
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
__ jmp(&push_receiver);
// Use the current global receiver object as the receiver.
__ bind(&use_global_receiver);
const int kGlobalOffset =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
__ movq(rbx, FieldOperand(rsi, kGlobalOffset));
__ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
// Push the receiver.
__ bind(&push_receiver);
__ push(rbx);
// Copy all arguments from the array to the stack.
Label entry, loop;
__ movq(rax, Operand(rbp, kIndexOffset));
__ jmp(&entry);
__ bind(&loop);
__ movq(rcx, Operand(rbp, kArgumentsOffset)); // load arguments
__ push(rcx);
__ push(rax);
// Use inline caching to speed up access to arguments.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
// It is important that we do not have a test instruction after the
// call. A test instruction after the call is used to indicate that
// we have generated an inline version of the keyed load. In this
// case, we know that we are not generating a test instruction next.
// Remove IC arguments from the stack and push the nth argument.
__ addq(rsp, Immediate(2 * kPointerSize));
__ push(rax);
// Update the index on the stack and in register rax.
__ movq(rax, Operand(rbp, kIndexOffset));
__ addq(rax, Immediate(Smi::FromInt(1)));
__ movq(Operand(rbp, kIndexOffset), rax);
__ bind(&entry);
__ cmpq(rax, Operand(rbp, kLimitOffset));
__ j(not_equal, &loop);
// Invoke the function.
ParameterCount actual(rax);
__ shr(rax, Immediate(kSmiTagSize));
__ movq(rdi, Operand(rbp, kFunctionOffset));
__ InvokeFunction(rdi, actual, CALL_FUNCTION);
__ LeaveInternalFrame();
__ ret(3 * kPointerSize); // remove function, receiver, and arguments
}
......
......@@ -2301,7 +2301,8 @@ void CodeGenerator::VisitAssignment(Assignment* node) {
// the target, with an implicit promise that it will be written to again
// before it is read.
// TODO(X64): Implement TakeValue optimization.
if (false && literal != NULL || (right_var != NULL && right_var != var)) {
if (false) {
// if (literal != NULL || (right_var != NULL && right_var != var)) {
// target.TakeValue(NOT_INSIDE_TYPEOF);
} else {
target.GetValue(NOT_INSIDE_TYPEOF);
......@@ -3342,7 +3343,9 @@ void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) {
void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* a) {
UNIMPLEMENTED();
// TODO(X64): Implement this function.
// Ignore arguments and return undefined, to signal failure.
frame_->Push(Factory::undefined_value());
}
......@@ -3371,10 +3374,27 @@ void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateLog(ZoneList<Expression*>* a) {
UNIMPLEMENTED();
void CodeGenerator::GenerateLog(ZoneList<Expression*>* args) {
// Conditionally generate a log call.
// Args:
// 0 (literal string): The type of logging (corresponds to the flags).
// This is used to determine whether or not to generate the log call.
// 1 (string): Format string. Access the string at argument index 2
// with '%2s' (see Logger::LogRuntime for all the formats).
// 2 (array): Arguments to the format string.
ASSERT_EQ(args->length(), 3);
#ifdef ENABLE_LOGGING_AND_PROFILING
if (ShouldGenerateLog(args->at(0))) {
Load(args->at(1));
Load(args->at(2));
frame_->CallRuntime(Runtime::kLog, 2);
}
#endif
// Finally, we're expected to leave a value on the top of the stack.
frame_->Push(Factory::undefined_value());
}
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
ASSERT(args->length() == 2);
......@@ -3446,9 +3466,29 @@ void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* args) {
void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) {
UNIMPLEMENTED();
ASSERT(args->length() == 1);
JumpTarget leave;
Load(args->at(0)); // Load the object.
frame_->Dup();
Result object = frame_->Pop();
object.ToRegister();
ASSERT(object.is_valid());
// if (object->IsSmi()) return object.
__ testl(object.reg(), Immediate(kSmiTagMask));
leave.Branch(zero);
// It is a heap object - get map.
Result temp = allocator()->Allocate();
ASSERT(temp.is_valid());
// if (!object->IsJSValue()) return object.
__ CmpObjectType(object.reg(), JS_VALUE_TYPE, temp.reg());
leave.Branch(not_equal);
__ movq(temp.reg(), FieldOperand(object.reg(), JSValue::kValueOffset));
object.Unuse();
frame_->SetElementAt(0, &temp);
leave.Bind();
}
// -----------------------------------------------------------------------------
// CodeGenerator implementation of Expressions
......
......@@ -79,10 +79,12 @@ void KeyedLoadIC::Generate(MacroAssembler* masm,
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC0AB)); // Debugging aid.
}
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC1AB)); // Debugging aid.
}
bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
......@@ -162,10 +164,12 @@ void KeyedStoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) {
void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC2AB)); // Debugging aid.
}
void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC3AB)); // Debugging aid.
}
Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
......@@ -265,26 +269,32 @@ void LoadIC::Generate(MacroAssembler* masm, ExternalReference const& f) {
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC4AB)); // Debugging aid.
}
void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC5AB)); // Debugging aid.
}
void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC6AB)); // Debugging aid.
}
void LoadIC::GenerateMiss(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC7AB)); // Debugging aid.
}
void LoadIC::GenerateNormal(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC8AB)); // Debugging aid.
}
void LoadIC::GenerateStringLength(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xC9AB)); // Debugging aid.
}
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int index) {
......@@ -312,10 +322,12 @@ void StoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) {
void StoreIC::GenerateExtendStorage(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xCAAB)); // Debugging aid.
}
void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
masm->movq(kScratchRegister, Immediate(0xCBAB)); // Debugging aid.
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment