Commit 5b4bacdd authored by ager@chromium.org's avatar ager@chromium.org

x64 code generation for construct calls, declaring global variables

and for runtime calls.

We could not handle functions with no explicit return statement.  I
added support for that as well.  The place was hard to find because
code was left out from the codegenerator with no TODO comment.  We
need to make sure to comment if we leave out code when porting
something. :-)

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2257 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent f66ea38c
......@@ -77,7 +77,7 @@ Operand::Operand(Register base, int32_t disp): rex_(0) {
len_ = 1;
if (base.is(rsp) || base.is(r12)) {
// SIB byte is needed to encode (rsp + offset) or (r12 + offset).
set_sib(kTimes1, rsp, base);
set_sib(times_1, rsp, base);
}
if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
......
......@@ -278,12 +278,12 @@ class Immediate BASE_EMBEDDED {
// Machine instruction Operands
enum ScaleFactor {
kTimes1 = 0,
kTimes2 = 1,
kTimes4 = 2,
kTimes8 = 3,
kTimesIntSize = kTimes4,
kTimesPointerSize = kTimes8
times_1 = 0,
times_2 = 1,
times_4 = 2,
times_8 = 3,
times_int_size = times_4,
times_pointer_size = times_8
};
......
......@@ -50,10 +50,10 @@ static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
__ push(rdi);
// Preserve the number of arguments on the stack. Must preserve both
// eax and ebx because these registers are used when copying the
// rax and rbx because these registers are used when copying the
// arguments and the receiver.
ASSERT(kSmiTagSize == 1);
__ lea(rcx, Operand(rax, rax, kTimes1, kSmiTag));
__ lea(rcx, Operand(rax, rax, times_1, kSmiTag));
__ push(rcx);
}
......@@ -71,7 +71,7 @@ static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
ASSERT_EQ(kSmiTagSize, 1 && kSmiTag == 0);
ASSERT_EQ(kPointerSize, (1 << kSmiTagSize) * 4);
__ pop(rcx);
__ lea(rsp, Operand(rsp, rbx, kTimes4, 1 * kPointerSize)); // 1 ~ receiver
__ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize)); // 1 ~ receiver
__ push(rcx);
}
......@@ -98,7 +98,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// Copy receiver and all expected arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(rax, Operand(rbp, rax, kTimesPointerSize, offset));
__ lea(rax, Operand(rbp, rax, times_pointer_size, offset));
__ movq(rcx, Immediate(-1)); // account for receiver
Label copy;
......@@ -117,7 +117,7 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
// Copy receiver and all actual arguments.
const int offset = StandardFrameConstants::kCallerSPOffset;
__ lea(rdi, Operand(rbp, rax, kTimesPointerSize, offset));
__ lea(rdi, Operand(rbp, rax, times_pointer_size, offset));
__ movq(rcx, Immediate(-1)); // account for receiver
Label copy;
......@@ -167,14 +167,133 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
}
void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
// ----------- S t a t e -------------
// -- rax: number of arguments
// -- rdi: constructor function
// -----------------------------------
Label non_function_call;
// Check that function is not a smi.
__ testl(rdi, Immediate(kSmiTagMask));
__ j(zero, &non_function_call);
// Check that function is a JSFunction.
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
__ j(not_equal, &non_function_call);
// Jump to the function-specific construct stub.
__ movq(rbx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ movq(rbx, FieldOperand(rbx, SharedFunctionInfo::kConstructStubOffset));
__ lea(rbx, FieldOperand(rbx, Code::kHeaderSize));
__ jmp(rbx);
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
// Set expected number of arguments to zero (not changing eax).
__ movq(rbx, Immediate(0));
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
}
void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
masm->int3(); // UNIMPLEMENTED.
// Enter a construct frame.
__ EnterConstructFrame();
// Store a smi-tagged arguments count on the stack.
__ shl(rax, Immediate(kSmiTagSize));
__ push(rax);
// Push the function to invoke on the stack.
__ push(rdi);
// Try to allocate the object without transitioning into C code. If any of the
// preconditions is not met, the code bails out to the runtime call.
Label rt_call, allocated;
// TODO(x64): Implement inlined allocation.
// Allocate the new receiver object using the runtime call.
// rdi: function (constructor)
__ bind(&rt_call);
// Must restore edi (constructor) before calling runtime.
__ movq(rdi, Operand(rsp, 0));
__ push(rdi);
__ CallRuntime(Runtime::kNewObject, 1);
__ movq(rbx, rax); // store result in rbx
// New object allocated.
// rbx: newly allocated object
__ bind(&allocated);
// Retrieve the function from the stack.
__ pop(rdi);
// Retrieve smi-tagged arguments count from the stack.
__ movq(rax, Operand(rsp, 0));
__ shr(rax, Immediate(kSmiTagSize));
// Push the allocated receiver to the stack. We need two copies
// because we may have to return the original one and the calling
// conventions dictate that the called function pops the receiver.
__ push(rbx);
__ push(rbx);
// Setup pointer to last argument.
__ lea(rbx, Operand(rbp, StandardFrameConstants::kCallerSPOffset));
// Copy arguments and receiver to the expression stack.
Label loop, entry;
__ movq(rcx, rax);
__ jmp(&entry);
__ bind(&loop);
__ push(Operand(rbx, rcx, times_pointer_size, 0));
__ bind(&entry);
__ decq(rcx);
__ j(greater_equal, &loop);
// Call the function.
ParameterCount actual(rax);
__ InvokeFunction(rdi, actual, CALL_FUNCTION);
// Restore context from the frame.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
// If the result is an object (in the ECMA sense), we should get rid
// of the receiver and use the result; see ECMA-262 section 13.2.2-7
// on page 74.
Label use_receiver, exit;
// If the result is a smi, it is *not* an object in the ECMA sense.
__ testl(rax, Immediate(kSmiTagMask));
__ j(zero, &use_receiver);
// If the type of the result (stored in its map) is less than
// FIRST_JS_OBJECT_TYPE, it is not an object in the ECMA sense.
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
__ j(greater_equal, &exit);
// Throw away the result of the constructor invocation and use the
// on-stack receiver as the result.
__ bind(&use_receiver);
__ movq(rax, Operand(rsp, 0));
// Restore the arguments count and leave the construct frame.
__ bind(&exit);
__ movq(rbx, Operand(rsp, kPointerSize)); // get arguments count
__ LeaveConstructFrame();
// Remove caller arguments from the stack and return.
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ pop(rcx);
__ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize)); // 1 ~ receiver
__ push(rcx);
__ ret(0);
}
static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
bool is_construct) {
// Expects five C++ function parameters.
......@@ -258,7 +377,7 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
__ xor_(rcx, rcx); // Set loop variable to 0.
__ jmp(&entry);
__ bind(&loop);
__ movq(kScratchRegister, Operand(rbx, rcx, kTimesPointerSize, 0));
__ movq(kScratchRegister, Operand(rbx, rcx, times_pointer_size, 0));
__ push(Operand(kScratchRegister, 0)); // dereference handle
__ addq(rcx, Immediate(1));
__ bind(&entry);
......
This diff is collapsed.
......@@ -206,7 +206,7 @@ void CallIC::Generate(MacroAssembler* masm,
// Check if the receiver is a global object of some sort.
Label invoke, global;
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize)); // receiver
__ testq(rdx, Immediate(kSmiTagMask));
__ testl(rdx, Immediate(kSmiTagMask));
__ j(zero, &invoke);
__ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset));
__ movzxbq(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset));
......
......@@ -756,7 +756,7 @@ void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
// Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(r15, Operand(rbp, rdi, kTimesPointerSize, offset));
lea(r15, Operand(rbp, rdi, times_pointer_size, offset));
#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
......
......@@ -65,7 +65,7 @@ void VirtualFrame::Enter() {
#ifdef DEBUG
// Verify that rdi contains a JS function. The following code
// relies on rax being available for use.
__ testq(rdi, Immediate(kSmiTagMask));
__ testl(rdi, Immediate(kSmiTagMask));
__ Check(not_zero,
"VirtualFrame::Enter - rdi is not a function (smi check).");
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
......@@ -906,6 +906,30 @@ Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
}
Result VirtualFrame::CallConstructor(int arg_count) {
// Arguments, receiver, and function are on top of the frame. The
// IC expects arg count in rax, function in rdi, and the arguments
// and receiver on the stack.
Handle<Code> ic(Builtins::builtin(Builtins::JSConstructCall));
// Duplicate the function before preparing the frame.
PushElementAt(arg_count + 1);
Result function = Pop();
PrepareForCall(arg_count + 1, arg_count + 1); // Spill args and receiver.
function.ToRegister(rdi);
// Constructors are called with the number of arguments in register
// eax for now. Another option would be to have separate construct
// call trampolines per different arguments counts encountered.
Result num_args = cgen()->allocator()->Allocate(rax);
ASSERT(num_args.is_valid());
__ movq(num_args.reg(), Immediate(arg_count));
function.Unuse();
num_args.Unuse();
return RawCallCodeObject(ic, RelocInfo::CONSTRUCT_CALL);
}
Result VirtualFrame::CallStoreIC() {
// Name, value, and receiver are on top of the frame. The IC
// expects name in rcx, value in rax, and receiver on the stack. It
......
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