summaryrefslogtreecommitdiff
path: root/deps/v8/src/x64/codegen-x64.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/x64/codegen-x64.cc')
-rw-r--r--deps/v8/src/x64/codegen-x64.cc466
1 files changed, 458 insertions, 8 deletions
diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc
index ca58e09a75..dc32227b44 100644
--- a/deps/v8/src/x64/codegen-x64.cc
+++ b/deps/v8/src/x64/codegen-x64.cc
@@ -30,6 +30,8 @@
#include "macro-assembler.h"
#include "register-allocator-inl.h"
#include "codegen.h"
+// TEST
+#include "compiler.h"
namespace v8 {
namespace internal {
@@ -41,6 +43,37 @@ void DeferredCode::SaveRegisters() { UNIMPLEMENTED(); }
void DeferredCode::RestoreRegisters() { UNIMPLEMENTED(); }
+// -------------------------------------------------------------------------
+// CodeGenState implementation.
+
+CodeGenState::CodeGenState(CodeGenerator* owner)
+ : owner_(owner),
+ typeof_state_(NOT_INSIDE_TYPEOF),
+ destination_(NULL),
+ previous_(NULL) {
+ owner_->set_state(this);
+}
+
+
+CodeGenState::CodeGenState(CodeGenerator* owner,
+ TypeofState typeof_state,
+ ControlDestination* destination)
+ : owner_(owner),
+ typeof_state_(typeof_state),
+ destination_(destination),
+ previous_(owner->state()) {
+ owner_->set_state(this);
+}
+
+
+CodeGenState::~CodeGenState() {
+ ASSERT(owner_->state() == this);
+ owner_->set_state(previous_);
+}
+
+
+// -----------------------------------------------------------------------------
+// CodeGenerator implementation.
CodeGenerator::CodeGenerator(int buffer_size,
Handle<Script> script,
@@ -58,17 +91,127 @@ CodeGenerator::CodeGenerator(int buffer_size,
in_spilled_code_(false) {
}
-#define __ masm->
+#define __ ACCESS_MASM(masm_)
void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) {
UNIMPLEMENTED();
}
-void CodeGenerator::GenCode(FunctionLiteral* a) {
- masm_->int3(); // UNIMPLEMENTED
+void CodeGenerator::TestCodeGenerator() {
+ // Compile a function from a string, and run it.
+ Handle<JSFunction> test_function = Compiler::Compile(
+ Factory::NewStringFromAscii(CStrVector("42")),
+ Factory::NewStringFromAscii(CStrVector("CodeGeneratorTestScript")),
+ 0,
+ 0,
+ NULL,
+ NULL);
+
+ Code* code_object = test_function->code(); // Local for debugging ease.
+ USE(code_object);
+
+ // Create a dummy function and context.
+ Handle<JSFunction> bridge =
+ Factory::NewFunction(Factory::empty_symbol(), Factory::undefined_value());
+ Handle<Context> context =
+ Factory::NewFunctionContext(Context::MIN_CONTEXT_SLOTS, bridge);
+
+ test_function = Factory::NewFunctionFromBoilerplate(
+ test_function,
+ context);
+
+ bool pending_exceptions;
+ Handle<Object> result =
+ Execution::Call(test_function,
+ Handle<Object>::cast(test_function),
+ 0,
+ NULL,
+ &pending_exceptions);
+ CHECK(result->IsSmi());
+ CHECK_EQ(42, Smi::cast(*result)->value());
+}
+
+
+void CodeGenerator::GenCode(FunctionLiteral* function) {
+ // Record the position for debugging purposes.
+ CodeForFunctionPosition(function);
+ // ZoneList<Statement*>* body = fun->body();
+
+ // Initialize state.
+ ASSERT(scope_ == NULL);
+ scope_ = function->scope();
+ ASSERT(allocator_ == NULL);
+ RegisterAllocator register_allocator(this);
+ allocator_ = &register_allocator;
+ ASSERT(frame_ == NULL);
+ frame_ = new VirtualFrame();
+ set_in_spilled_code(false);
+
+ // Adjust for function-level loop nesting.
+ loop_nesting_ += function->loop_nesting();
+
+ JumpTarget::set_compiling_deferred_code(false);
+
+#ifdef DEBUG
+ if (strlen(FLAG_stop_at) > 0 &&
+ // fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
+ false) {
+ frame_->SpillAll();
+ __ int3();
+ }
+#endif
+
+ // New scope to get automatic timing calculation.
+ { // NOLINT
+ HistogramTimerScope codegen_timer(&Counters::code_generation);
+ CodeGenState state(this);
+
+ // Entry:
+ // Stack: receiver, arguments, return address.
+ // ebp: caller's frame pointer
+ // esp: stack pointer
+ // edi: called JS function
+ // esi: callee's context
+ allocator_->Initialize();
+ frame_->Enter();
+
+ Result return_register = allocator_->Allocate(rax);
+
+ __ movq(return_register.reg(), Immediate(0x54)); // Smi 42
+
+ GenerateReturnSequence(&return_register);
+ }
+}
+
+void CodeGenerator::GenerateReturnSequence(Result* return_value) {
+ // The return value is a live (but not currently reference counted)
+ // reference to rax. This is safe because the current frame does not
+ // contain a reference to rax (it is prepared for the return by spilling
+ // all registers).
+ if (FLAG_trace) {
+ frame_->Push(return_value);
+ // *return_value = frame_->CallRuntime(Runtime::kTraceExit, 1);
+ }
+ return_value->ToRegister(rax);
+
+ // Add a label for checking the size of the code used for returning.
+ Label check_exit_codesize;
+ masm_->bind(&check_exit_codesize);
+
+ // Leave the frame and return popping the arguments and the
+ // receiver.
+ frame_->Exit();
+ masm_->ret((scope_->num_parameters() + 1) * kPointerSize);
+ DeleteFrame();
+
+ // Check that the size of the code used for returning matches what is
+ // expected by the debugger.
+ // ASSERT_EQ(Debug::kIa32JSReturnSequenceLength,
+ // masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
}
+
void CodeGenerator::GenerateFastCaseSwitchJumpTable(SwitchStatement* a,
int b,
int c,
@@ -235,9 +378,316 @@ void CodeGenerator::VisitThisFunction(ThisFunction* a) {
UNIMPLEMENTED();
}
+void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateIsSmi(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateLog(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList<Expression*>* args) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateSetValueOf(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* a) {
+ UNIMPLEMENTED();
+}
+
+#undef __
+// End of CodeGenerator implementation.
+
+// -----------------------------------------------------------------------------
+// Implementation of stubs.
+
+// Stub classes have public member named masm, not masm_.
+#define __ ACCESS_MASM(masm)
+
+void CEntryStub::GenerateThrowTOS(MacroAssembler* masm) {
+ // Check that stack should contain frame pointer, code pointer, state and
+ // return address in that order.
+ ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
+ StackHandlerConstants::kStateOffset);
+ ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
+ StackHandlerConstants::kPCOffset);
+
+ ExternalReference handler_address(Top::k_handler_address);
+ __ movq(kScratchRegister, handler_address);
+ __ movq(rdx, Operand(kScratchRegister, 0));
+ // get next in chain
+ __ movq(rcx, Operand(rdx, 0));
+ __ movq(Operand(kScratchRegister, 0), rcx);
+ __ movq(rsp, rdx);
+ __ pop(rbp); // pop frame pointer
+ __ pop(rdx); // remove code pointer
+ __ pop(rdx); // remove state
+
+ // Before returning we restore the context from the frame pointer if not NULL.
+ // The frame pointer is NULL in the exception handler of a JS entry frame.
+ __ xor_(rsi, rsi); // tentatively set context pointer to NULL
+ Label skip;
+ __ cmpq(rbp, Immediate(0));
+ __ j(equal, &skip);
+ __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
+ __ bind(&skip);
+
+ __ ret(0);
+}
+
+
+
+void CEntryStub::GenerateCore(MacroAssembler* masm,
+ Label* throw_normal_exception,
+ Label* throw_out_of_memory_exception,
+ StackFrame::Type frame_type,
+ bool do_gc,
+ bool always_allocate_scope) {
+ // rax: result parameter for PerformGC, if any.
+ // rbx: pointer to C function (C callee-saved).
+ // rbp: frame pointer (restored after C call).
+ // rsp: stack pointer (restored after C call).
+ // rdi: number of arguments including receiver.
+ // r15: pointer to the first argument (C callee-saved).
+ // This pointer is reused in LeaveExitFrame(), so it is stored in a
+ // callee-saved register.
+
+ if (do_gc) {
+ __ movq(Operand(rsp, 0), rax); // Result.
+ __ movq(kScratchRegister,
+ FUNCTION_ADDR(Runtime::PerformGC),
+ RelocInfo::RUNTIME_ENTRY);
+ __ call(kScratchRegister);
+ }
+
+ ExternalReference scope_depth =
+ ExternalReference::heap_always_allocate_scope_depth();
+ if (always_allocate_scope) {
+ __ movq(kScratchRegister, scope_depth);
+ __ incl(Operand(kScratchRegister, 0));
+ }
+
+ // Call C function.
+#ifdef __MSVC__
+ // MSVC passes arguments in rcx, rdx, r8, r9
+ __ movq(rcx, rdi); // argc.
+ __ movq(rdx, r15); // argv.
+#else // ! defined(__MSVC__)
+ // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
+ // First argument is already in rdi.
+ __ movq(rsi, r15); // argv.
+#endif
+ __ call(rbx);
+ // Result is in rax - do not destroy this register!
+
+ if (always_allocate_scope) {
+ __ movq(kScratchRegister, scope_depth);
+ __ decl(Operand(kScratchRegister, 0));
+ }
+
+ // Check for failure result.
+ Label failure_returned;
+ ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
+ __ lea(rcx, Operand(rax, 1));
+ // Lower 2 bits of rcx are 0 iff rax has failure tag.
+ __ testl(rcx, Immediate(kFailureTagMask));
+ __ j(zero, &failure_returned);
+
+ // Exit the JavaScript to C++ exit frame.
+ __ LeaveExitFrame(frame_type);
+ __ ret(0);
+
+ // Handling of failure.
+ __ bind(&failure_returned);
+
+ Label retry;
+ // If the returned exception is RETRY_AFTER_GC continue at retry label
+ ASSERT(Failure::RETRY_AFTER_GC == 0);
+ __ testq(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
+ __ j(zero, &retry);
+
+ Label continue_exception;
+ // If the returned failure is EXCEPTION then promote Top::pending_exception().
+ __ movq(kScratchRegister, Failure::Exception(), RelocInfo::NONE);
+ __ cmpq(rax, kScratchRegister);
+ __ j(not_equal, &continue_exception);
+
+ // Retrieve the pending exception and clear the variable.
+ ExternalReference pending_exception_address(Top::k_pending_exception_address);
+ __ movq(kScratchRegister, pending_exception_address);
+ __ movq(rax, Operand(kScratchRegister, 0));
+ __ movq(rdx, ExternalReference::the_hole_value_location());
+ __ movq(rdx, Operand(rdx, 0));
+ __ movq(Operand(kScratchRegister, 0), rdx);
+
+ __ bind(&continue_exception);
+ // Special handling of out of memory exception.
+ __ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
+ __ cmpq(rax, kScratchRegister);
+ __ j(equal, throw_out_of_memory_exception);
+
+ // Handle normal exception.
+ __ jmp(throw_normal_exception);
+
+ // Retry.
+ __ bind(&retry);
+}
+
+
+void CEntryStub::GenerateThrowOutOfMemory(MacroAssembler* masm) {
+ // Fetch top stack handler.
+ ExternalReference handler_address(Top::k_handler_address);
+ __ movq(kScratchRegister, handler_address);
+ __ movq(rdx, Operand(kScratchRegister, 0));
+
+ // Unwind the handlers until the ENTRY handler is found.
+ Label loop, done;
+ __ bind(&loop);
+ // Load the type of the current stack handler.
+ __ cmpq(Operand(rdx, StackHandlerConstants::kStateOffset),
+ Immediate(StackHandler::ENTRY));
+ __ j(equal, &done);
+ // Fetch the next handler in the list.
+ __ movq(rdx, Operand(rdx, StackHandlerConstants::kNextOffset));
+ __ jmp(&loop);
+ __ bind(&done);
+
+ // Set the top handler address to next handler past the current ENTRY handler.
+ __ movq(rax, Operand(rdx, StackHandlerConstants::kNextOffset));
+ __ store_rax(handler_address);
+
+ // Set external caught exception to false.
+ __ movq(rax, Immediate(false));
+ ExternalReference external_caught(Top::k_external_caught_exception_address);
+ __ store_rax(external_caught);
+
+ // Set pending exception and rax to out of memory exception.
+ __ movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE);
+ ExternalReference pending_exception(Top::k_pending_exception_address);
+ __ store_rax(pending_exception);
+
+ // Restore the stack to the address of the ENTRY handler
+ __ movq(rsp, rdx);
+
+ // Clear the context pointer;
+ __ xor_(rsi, rsi);
+
+ // Restore registers from handler.
+
+ __ pop(rbp); // FP
+ ASSERT_EQ(StackHandlerConstants::kFPOffset + kPointerSize,
+ StackHandlerConstants::kStateOffset);
+ __ pop(rdx); // State
+
+ ASSERT_EQ(StackHandlerConstants::kStateOffset + kPointerSize,
+ StackHandlerConstants::kPCOffset);
+ __ ret(0);
+}
+
void CEntryStub::GenerateBody(MacroAssembler* masm, bool is_debug_break) {
- masm->int3(); // TODO(X64): UNIMPLEMENTED.
+ // rax: number of arguments including receiver
+ // rbx: pointer to C function (C callee-saved)
+ // rbp: frame pointer (restored after C call)
+ // rsp: stack pointer (restored after C call)
+ // rsi: current context (C callee-saved)
+ // rdi: caller's parameter pointer pp (C callee-saved)
+
+ // NOTE: Invocations of builtins may return failure objects
+ // instead of a proper result. The builtin entry handles
+ // this by performing a garbage collection and retrying the
+ // builtin once.
+
+ StackFrame::Type frame_type = is_debug_break ?
+ StackFrame::EXIT_DEBUG :
+ StackFrame::EXIT;
+
+ // Enter the exit frame that transitions from JavaScript to C++.
+ __ EnterExitFrame(frame_type);
+
+ // rax: result parameter for PerformGC, if any (setup below).
+ // Holds the result of a previous call to GenerateCore that
+ // returned a failure. On next call, it's used as parameter
+ // to Runtime::PerformGC.
+ // rbx: pointer to builtin function (C callee-saved).
+ // rbp: frame pointer (restored after C call).
+ // rsp: stack pointer (restored after C call).
+ // rdi: number of arguments including receiver (destroyed by C call).
+ // The rdi register is not callee-save in Unix 64-bit ABI, so
+ // we must treat it as volatile.
+ // r15: argv pointer (C callee-saved).
+
+ Label throw_out_of_memory_exception;
+ Label throw_normal_exception;
+
+ // Call into the runtime system. Collect garbage before the call if
+ // running with --gc-greedy set.
+ if (FLAG_gc_greedy) {
+ Failure* failure = Failure::RetryAfterGC(0);
+ __ movq(rax, failure, RelocInfo::NONE);
+ }
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_out_of_memory_exception,
+ frame_type,
+ FLAG_gc_greedy,
+ false);
+
+ // Do space-specific GC and retry runtime call.
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_out_of_memory_exception,
+ frame_type,
+ true,
+ false);
+
+ // Do full GC and retry runtime call one final time.
+ Failure* failure = Failure::InternalError();
+ __ movq(rax, failure, RelocInfo::NONE);
+ GenerateCore(masm,
+ &throw_normal_exception,
+ &throw_out_of_memory_exception,
+ frame_type,
+ true,
+ true);
+
+ __ bind(&throw_out_of_memory_exception);
+ GenerateThrowOutOfMemory(masm);
+ // control flow for generated will not return.
+
+ __ bind(&throw_normal_exception);
+ GenerateThrowTOS(masm);
}
@@ -281,7 +731,6 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
// Invoke: Link this frame into the handler chain.
__ bind(&invoke);
__ PushTryHandler(IN_JS_ENTRY, JS_ENTRY_HANDLER);
- __ push(rax); // flush TOS
// Clear any pending exceptions.
__ load_rax(ExternalReference::the_hole_value_location());
@@ -302,13 +751,14 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
ExternalReference entry(Builtins::JSEntryTrampoline);
__ load_rax(entry);
}
- __ call(FieldOperand(rax, Code::kHeaderSize));
+ __ lea(kScratchRegister, FieldOperand(rax, Code::kHeaderSize));
+ __ call(kScratchRegister);
// Unlink this frame from the handler chain.
__ movq(kScratchRegister, ExternalReference(Top::k_handler_address));
__ pop(Operand(kScratchRegister, 0));
// Pop next_sp.
- __ add(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
+ __ addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize));
// Restore the top frame descriptor from the stack.
__ bind(&exit);
@@ -323,7 +773,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
__ pop(r14);
__ pop(r13);
__ pop(r12);
- __ add(rsp, Immediate(2 * kPointerSize)); // remove markers
+ __ addq(rsp, Immediate(2 * kPointerSize)); // remove markers
// Restore frame pointer and return.
__ pop(rbp);