X64: Implement CEntryStub and JSEntryTrampoline.
authorlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 10 Jun 2009 09:48:15 +0000 (09:48 +0000)
committerlrn@chromium.org <lrn@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 10 Jun 2009 09:48:15 +0000 (09:48 +0000)
Still some supporting functions missing.

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2130 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ia32/macro-assembler-ia32.h
src/x64/assembler-x64-inl.h
src/x64/assembler-x64.cc
src/x64/assembler-x64.h
src/x64/builtins-x64.cc
src/x64/codegen-x64.cc
src/x64/frames-x64.h
src/x64/macro-assembler-x64.cc
src/x64/simulator-x64.h
test/cctest/test-assembler-x64.cc

index ce6bc59..42620dd 100644 (file)
@@ -285,7 +285,7 @@ class MacroAssembler: public Assembler {
   List<Unresolved> unresolved_;
   bool generating_stub_;
   bool allow_stub_calls_;
-  Handle<Object> code_object_;  // This handle will be patched with the code
+  Handle<Object> code_object_;  // This handle will be patched with the
                                 // code object on installation.
 
   // Helper functions for generating invokes.
index 1822568..6427649 100644 (file)
@@ -249,24 +249,6 @@ Object** RelocInfo::call_object_address() {
 // -----------------------------------------------------------------------------
 // Implementation of Operand
 
-Operand::Operand(Register base, int32_t disp) {
-  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);
-  }
-
-  if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
-    set_modrm(0, rsp);
-  } else if (is_int8(disp)) {
-    set_modrm(1, base);
-    set_disp8(disp);
-  } else {
-    set_modrm(2, base);
-    set_disp32(disp);
-  }
-}
-
 void Operand::set_modrm(int mod, Register rm) {
   ASSERT((mod & -4) == 0);
   buf_[0] = mod << 6 | (rm.code() & 0x7);
index f52efc6..daed68b 100644 (file)
@@ -72,7 +72,49 @@ XMMRegister xmm13 = { 13 };
 XMMRegister xmm14 = { 14 };
 XMMRegister xmm15 = { 15 };
 
+
+Operand::Operand(Register base, int32_t disp) {
+  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);
+  }
+
+  if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
+    set_modrm(0, rsp);
+  } else if (is_int8(disp)) {
+    set_modrm(1, base);
+    set_disp8(disp);
+  } else {
+    set_modrm(2, base);
+    set_disp32(disp);
+  }
+}
+
+
+Operand::Operand(Register base,
+                 Register index,
+                 ScaleFactor scale,
+                 int32_t disp) {
+  ASSERT(!index.is(rsp) && !index.is(r12));
+  len_ = 1;
+  set_sib(scale, index, base);
+  if (disp == 0 && !base.is(rbp) && !base.is(r13)) {
+    // The call to set_modrm doesn't overwrite the REX.B bit possibly set
+    // by set_sib.
+    set_modrm(0, rsp);
+  } else if (is_int8(disp)) {
+    set_modrm(1, rsp);
+    set_disp8(disp);
+  } else {
+    set_modrm(2, rsp);
+    set_disp32(disp);
+  }
+}
+
+
 // Safe default is no features.
+// TODO(X64): Safe defaults include SSE2 for X64.
 uint64_t CpuFeatures::supported_ = 0;
 uint64_t CpuFeatures::enabled_ = 0;
 
@@ -487,6 +529,7 @@ void Assembler::call(Register adr) {
   emit_modrm(0x2, adr);
 }
 
+
 void Assembler::cpuid() {
   ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID));
   EnsureSpace ensure_space(this);
@@ -844,6 +887,31 @@ void Assembler::movq(Register dst, ExternalReference ref) {
 }
 
 
+void Assembler::movq(const Operand& dst, Immediate value) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  emit_rex_64(dst);
+  emit(0xC7);
+  emit_operand(0, dst);
+  emit(value);
+}
+
+
+void Assembler::movq(Register dst, Handle<Object> value, RelocInfo::Mode mode) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  ASSERT(!Heap::InNewSpace(*value));
+  emit_rex_64(dst);
+  emit(0xB8 | dst.code() & 0x7);
+  if (value->IsHeapObject()) {
+    emitq(reinterpret_cast<uintptr_t>(value.location()), mode);
+  } else {
+    ASSERT_EQ(RelocInfo::NONE, mode);
+    emitq(reinterpret_cast<uintptr_t>(*value), RelocInfo::NONE);
+  }
+}
+
+
 void Assembler::mul(Register src) {
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
@@ -1129,6 +1197,7 @@ void Assembler::store_rax(ExternalReference ref) {
 
 
 void Assembler::testb(Register reg, Immediate mask) {
+  ASSERT(is_int8(mask.value_));
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
   if (reg.is(rax)) {
@@ -1147,6 +1216,7 @@ void Assembler::testb(Register reg, Immediate mask) {
 
 
 void Assembler::testb(const Operand& op, Immediate mask) {
+  ASSERT(is_int8(mask.value_));
   EnsureSpace ensure_space(this);
   last_pc_ = pc_;
   emit_optional_rex_32(rax, op);
@@ -1199,6 +1269,22 @@ void Assembler::testq(Register dst, Register src) {
 }
 
 
+void Assembler::testq(Register dst, Immediate mask) {
+  EnsureSpace ensure_space(this);
+  last_pc_ = pc_;
+  if (dst.is(rax)) {
+    emit_rex_64();
+    emit(0xA9);
+    emit(mask);
+  } else {
+    emit_rex_64(dst);
+    emit(0xF7);
+    emit_modrm(0, dst);
+    emit(mask);
+  }
+}
+
+
 // Relocation information implementations
 
 void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
index b488257..b75602c 100644 (file)
@@ -77,7 +77,7 @@ static inline bool is_int32(int64_t x) {
 
 struct Register {
   static Register toRegister(int code) {
-    Register r = {code};
+    Register r = { code };
     return r;
   }
   bool is_valid() const  { return 0 <= code_ && code_ < 16; }
@@ -89,11 +89,11 @@ struct Register {
     return code_;
   }
   int bit() const  {
-    UNIMPLEMENTED();
-    return 0;
+    return 1 << code_;
   }
 
-  // (unfortunately we can't make this private in a struct)
+  // (unfortunately we can't make this private in a struct when initializing
+  // by assignment.)
   int code_;
 };
 
@@ -250,7 +250,7 @@ enum ScaleFactor {
 class Operand BASE_EMBEDDED {
  public:
   // [base + disp/r]
-  INLINE(Operand(Register base, int32_t disp));
+  Operand(Register base, int32_t disp);
 
   // [base + index*scale + disp/r]
   Operand(Register base,
@@ -434,7 +434,8 @@ class Assembler : public Malloced {
 
   // Move 64 bit register value to 64-bit memory location.
   void movq(const Operand& dst, Register src);
-
+  // Move sign extended immediate to memory location.
+  void movq(const Operand& dst, Immediate value);
   // New x64 instructions to load a 64-bit immediate into a register.
   // All 64-bit immediates must have a relocation mode.
   void movq(Register dst, void* ptr, RelocInfo::Mode rmode);
@@ -444,7 +445,6 @@ class Assembler : public Malloced {
   void movq(Register dst, ExternalReference ext);
   void movq(Register dst, Handle<Object> handle, RelocInfo::Mode rmode);
 
-
   // New x64 instruction to load from an immediate 64-bit pointer into RAX.
   void load_rax(void* ptr, RelocInfo::Mode rmode);
   void load_rax(ExternalReference ext);
@@ -647,6 +647,7 @@ class Assembler : public Malloced {
   void testl(const Operand& op, Immediate mask);
   void testq(const Operand& op, Register reg);
   void testq(Register dst, Register src);
+  void testq(Register dst, Immediate mask);
 
   void xor_(Register dst, Register src) {
     arithmetic_op(0x33, dst, src);
index 3f1cd9f..cb7ba30 100644 (file)
 
 #include "v8.h"
 #include "codegen-inl.h"
+#include "macro-assembler.h"
 
 namespace v8 {
 namespace internal {
 
+#define __ ACCESS_MASM(masm)
+
 void Builtins::Generate_Adaptor(MacroAssembler* masm,
                                 Builtins::CFunctionId id) {
   masm->int3();  // UNIMPLEMENTED.
@@ -52,12 +55,125 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
   masm->int3();  // UNIMPLEMENTED.
 }
 
-void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
-  masm->int3();  // UNIMPLEMENTED.
+static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
+                                             bool is_construct) {
+  // Expects five C++ function parameters.
+  // - Address entry (ignored)
+  // - JSFunction* function (
+  // - Object* receiver
+  // - int argc
+  // - Object*** argv
+  // (see Handle::Invoke in execution.cc).
+
+  // Platform specific argument handling. After this, the stack contains
+  // an internal frame and the pushed function and receiver, and
+  // register rax and rbx holds the argument count and argument array,
+  // while rdi holds the function pointer and rsi the context.
+#ifdef __MSVC__
+  // MSVC parameters in:
+  // rcx : entry (ignored)
+  // rdx : function
+  // r8 : receiver
+  // r9 : argc
+  // [rsp+0x20] : argv
+
+  // Clear the context before we push it when entering the JS frame.
+  __ xor_(rsi, rsi);
+  // Enter an internal frame.
+  __ EnterInternalFrame();
+
+
+  // Load the function context into rsi.
+  __ movq(rsi, FieldOperand(rdx, JSFunction::kContextOffset));
+
+  // Push the function and the receiver onto the stack.
+  __ push(rdx);
+  __ push(r8);
+
+  // Load the number of arguments and setup pointer to the arguments.
+  __ movq(rax, r9);
+  // Load the previous frame pointer to access C argument on stack
+  __ movq(kScratchRegister, Operand(rbp, 0));
+  __ movq(rbx, Operand(kScratchRegister, EntryFrameConstants::kArgvOffset));
+  // Load the function pointer into rdi.
+  __ movq(rdi, rdx);
+#else  // !defined(__MSVC__)
+  // GCC parameters in:
+  // rdi : entry (ignored)
+  // rsi : function
+  // rdx : receiver
+  // rcx : argc
+  // r8  : argv
+
+  __ movq(rdi, rsi);
+  // rdi : function
+
+  // Clear the context before we push it when entering the JS frame.
+  __ xor_(rsi, rsi);
+  // Enter an internal frame.
+  __ EnterInternalFrame();
+
+  // Push the function and receiver and setup the context.
+  __ push(rdi);
+  __ push(rdx);
+  __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
+  // Load the number of arguments and setup pointer to the arguments.
+  __ movq(rax, rcx);
+  __ movq(rbx, r8);
+#endif  // __MSVC__
+  // Current stack contents:
+  // [rsp + 2 * kPointerSize ... ]: Internal frame
+  // [rsp + kPointerSize]         : function
+  // [rsp]                        : receiver
+  // Current register contents:
+  // rax : argc
+  // rbx : argv
+  // rsi : context
+  // rdi : function
+
+  // Copy arguments to the stack in a loop.
+  // Register rbx points to array of pointers to handle locations.
+  // Push the values of these handles.
+  Label loop, entry;
+  __ xor_(rcx, rcx);  // Set loop variable to 0.
+  __ jmp(&entry);
+  __ bind(&loop);
+  __ movq(kScratchRegister, Operand(rbx, rcx, kTimesPointerSize, 0));
+  __ push(Operand(kScratchRegister, 0));  // dereference handle
+  __ add(rcx, Immediate(1));
+  __ bind(&entry);
+  __ cmp(rcx, rax);
+  __ j(not_equal, &loop);
+
+  // Invoke the code.
+  if (is_construct) {
+    // Expects rdi to hold function pointer.
+    __ movq(kScratchRegister,
+            Handle<Code>(Builtins::builtin(Builtins::JSConstructCall)),
+            RelocInfo::CODE_TARGET);
+    __ call(kScratchRegister);
+  } else {
+    ParameterCount actual(rax);
+    __ InvokeFunction(rdi, actual, CALL_FUNCTION);
+  }
+
+  // Exit the JS frame. Notice that this also removes the empty
+  // context and the function left on the stack by the code
+  // invocation.
+  __ LeaveInternalFrame();
+  // TODO(X64): Is argument correct? Is there a receiver to remove?
+  __ ret(1 * kPointerSize);  // remove receiver
 }
 
+
 void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
-  masm->int3();  // UNIMPLEMENTED.
+  Generate_JSEntryTrampolineHelper(masm, false);
+}
+
+
+void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
+  Generate_JSEntryTrampolineHelper(masm, true);
 }
 
 } }  // namespace v8::internal
index 9bafb11..ad105a1 100644 (file)
@@ -58,7 +58,7 @@ CodeGenerator::CodeGenerator(int buffer_size,
       in_spilled_code_(false) {
 }
 
-#define __ masm->
+#define __ ACCESS_MASM(masm)
 
 
 void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) {
@@ -236,8 +236,250 @@ void CodeGenerator::VisitThisFunction(ThisFunction* a) {
 }
 
 
+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;
+  __ cmp(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  (C callee-saved)
+  // rsi: pointer to the first argument (C callee-saved)
+
+  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);
+    __ inc(Operand(kScratchRegister, 0));
+  }
+
+  // Call C function.
+#ifdef __MSVC__
+  // MSVC passes arguments in rcx, rdx, r8, r9
+  __ movq(rcx, rdi);  // argc.
+  __ movq(rdx, rsi);  // argv.
+#else  // ! defined(__MSVC__)
+  // GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
+  // First two arguments are already in rdi, rsi.
+#endif
+  __ call(rbx);
+  // Result is in rax - do not destroy this register!
+
+  if (always_allocate_scope) {
+    __ movq(kScratchRegister, scope_depth);
+    __ dec(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);
+  __ cmp(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);
+  __ cmp(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.
+  __ cmp(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)
+  // 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 (C callee-saved)
+  // rsi: 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);
 }
 
 
index 9a28b61..b4b55b3 100644 (file)
@@ -32,11 +32,18 @@ namespace v8 {
 namespace internal {
 
 // TODO(x64): This is a stub, mostly just a copy of the ia32 bit version.
-// This will all need to change to be correct for x64.
+// This might all need to change to be correct for x64.
 
 static const int kNumRegs = 8;
-static const RegList kJSCallerSaved = 0;
+static const RegList kJSCallerSaved =
+    1 << 0 |  // rax
+    1 << 1 |  // rcx
+    1 << 2 |  // rdx
+    1 << 3 |  // rbx - used as a caller-saved register in JavaScript code
+    1 << 7;   // rdi - callee function
+
 static const int kNumJSCallerSaved = 5;
+
 typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
 
 class StackHandlerConstants : public AllStatic {
@@ -52,24 +59,26 @@ class StackHandlerConstants : public AllStatic {
 
 class EntryFrameConstants : public AllStatic {
  public:
-  static const int kCallerFPOffset      = -1 * kPointerSize;
+  static const int kCallerFPOffset      = 0 * kPointerSize;
 
-  static const int kFunctionArgOffset   = -1 * kPointerSize;
-  static const int kReceiverArgOffset   = -1 * kPointerSize;
-  static const int kArgcOffset          = -1 * kPointerSize;
-  static const int kArgvOffset          = -1 * kPointerSize;
+  static const int kFunctionArgOffset   = 1 * kPointerSize;
+  static const int kReceiverArgOffset   = 2 * kPointerSize;
+  static const int kArgcOffset          = 3 * kPointerSize;
+  static const int kArgvOffset          = 4 * kPointerSize;
 };
 
 
 class ExitFrameConstants : public AllStatic {
  public:
-  static const int kDebugMarkOffset = -1 * kPointerSize;
+  static const int kDebugMarkOffset = -2 * kPointerSize;
   static const int kSPOffset        = -1 * kPointerSize;
 
-  static const int kPPDisplacement = -1 * kPointerSize;
+  // TODO(X64): Remove usage of PP in exit frames?
+  // Still used though StackFrame::pp()
+  static const int kPPDisplacement  = +2 * kPointerSize;
 
-  static const int kCallerFPOffset = -1 * kPointerSize;
-  static const int kCallerPCOffset = -1 * kPointerSize;
+  static const int kCallerFPOffset  = +0 * kPointerSize;
+  static const int kCallerPCOffset  = +1 * kPointerSize;
 };
 
 
index e5de25e..9e989d1 100644 (file)
@@ -29,7 +29,9 @@
 
 #include "bootstrapper.h"
 #include "codegen-inl.h"
+#include "assembler-x64.h"
 #include "macro-assembler-x64.h"
+#include "debug.h"
 
 namespace v8 {
 namespace internal {
@@ -43,6 +45,53 @@ MacroAssembler::MacroAssembler(void* buffer, int size)
 }
 
 
+void MacroAssembler::Assert(Condition cc, const char* msg) {
+  if (FLAG_debug_code) Check(cc, msg);
+}
+
+
+void MacroAssembler::Check(Condition cc, const char* msg) {
+  Label L;
+  j(cc, &L);
+  Abort(msg);
+  // will not return here
+  bind(&L);
+}
+
+
+void MacroAssembler::Abort(const char* msg) {
+  // We want to pass the msg string like a smi to avoid GC
+  // problems, however msg is not guaranteed to be aligned
+  // properly. Instead, we pass an aligned pointer that is
+  // a proper v8 smi, but also pass the alignment difference
+  // from the real pointer as a smi.
+  intptr_t p1 = reinterpret_cast<intptr_t>(msg);
+  intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
+  // Note: p0 might not be a valid Smi *value*, but it has a valid Smi tag.
+  ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
+#ifdef DEBUG
+  if (msg != NULL) {
+    RecordComment("Abort message: ");
+    RecordComment(msg);
+  }
+#endif
+  push(rax);
+  movq(kScratchRegister, p0, RelocInfo::NONE);
+  push(kScratchRegister);
+  movq(kScratchRegister,
+       reinterpret_cast<intptr_t>(Smi::FromInt(p1 - p0)),
+       RelocInfo::NONE);
+  push(kScratchRegister);
+  CallRuntime(Runtime::kAbort, 2);
+  // will not return here
+}
+
+
+void MacroAssembler::CallRuntime(Runtime::FunctionId id, int argc) {
+  UNIMPLEMENTED();
+}
+
+
 void MacroAssembler::TailCallRuntime(ExternalReference const& a, int b) {
   UNIMPLEMENTED();
 }
@@ -109,4 +158,228 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location,
 }
 
 
+#ifdef ENABLE_DEBUGGER_SUPPORT
+
+void MacroAssembler::PushRegistersFromMemory(RegList regs) {
+  ASSERT((regs & ~kJSCallerSaved) == 0);
+  // Push the content of the memory location to the stack.
+  for (int i = 0; i < kNumJSCallerSaved; i++) {
+    int r = JSCallerSavedCode(i);
+    if ((regs & (1 << r)) != 0) {
+      ExternalReference reg_addr =
+          ExternalReference(Debug_Address::Register(i));
+      movq(kScratchRegister, reg_addr);
+      push(Operand(kScratchRegister, 0));
+    }
+  }
+}
+
+void MacroAssembler::SaveRegistersToMemory(RegList regs) {
+  ASSERT((regs & ~kJSCallerSaved) == 0);
+  // Copy the content of registers to memory location.
+  for (int i = 0; i < kNumJSCallerSaved; i++) {
+    int r = JSCallerSavedCode(i);
+    if ((regs & (1 << r)) != 0) {
+      Register reg = { r };
+      ExternalReference reg_addr =
+          ExternalReference(Debug_Address::Register(i));
+      movq(kScratchRegister, reg_addr);
+      movq(Operand(kScratchRegister, 0), reg);
+    }
+  }
+}
+
+
+void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
+  ASSERT((regs & ~kJSCallerSaved) == 0);
+  // Copy the content of memory location to registers.
+  for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
+    int r = JSCallerSavedCode(i);
+    if ((regs & (1 << r)) != 0) {
+      Register reg = { r };
+      ExternalReference reg_addr =
+          ExternalReference(Debug_Address::Register(i));
+      movq(kScratchRegister, reg_addr);
+      movq(reg, Operand(kScratchRegister, 0));
+    }
+  }
+}
+
+
+void MacroAssembler::PopRegistersToMemory(RegList regs) {
+  ASSERT((regs & ~kJSCallerSaved) == 0);
+  // Pop the content from the stack to the memory location.
+  for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
+    int r = JSCallerSavedCode(i);
+    if ((regs & (1 << r)) != 0) {
+      ExternalReference reg_addr =
+          ExternalReference(Debug_Address::Register(i));
+      movq(kScratchRegister, reg_addr);
+      pop(Operand(kScratchRegister, 0));
+    }
+  }
+}
+
+
+void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
+                                                    Register scratch,
+                                                    RegList regs) {
+  ASSERT(!scratch.is(kScratchRegister));
+  ASSERT(!base.is(kScratchRegister));
+  ASSERT(!base.is(scratch));
+  ASSERT((regs & ~kJSCallerSaved) == 0);
+  // Copy the content of the stack to the memory location and adjust base.
+  for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
+    int r = JSCallerSavedCode(i);
+    if ((regs & (1 << r)) != 0) {
+      movq(scratch, Operand(base, 0));
+      ExternalReference reg_addr =
+          ExternalReference(Debug_Address::Register(i));
+      movq(kScratchRegister, reg_addr);
+      movq(Operand(kScratchRegister, 0), scratch);
+      lea(base, Operand(base, kPointerSize));
+    }
+  }
+}
+
+#endif  // ENABLE_DEBUGGER_SUPPORT
+
+
+
+
+void MacroAssembler::InvokeFunction(Register fun,
+                                    const ParameterCount& actual,
+                                    InvokeFlag flag) {
+  UNIMPLEMENTED();
+}
+
+
+void MacroAssembler::EnterFrame(StackFrame::Type type) {
+  push(rbp);
+  movq(rbp, rsp);
+  push(rsi);  // Context.
+  push(Immediate(Smi::FromInt(type)));
+  movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
+  push(kScratchRegister);
+  if (FLAG_debug_code) {
+    movq(kScratchRegister,
+         Factory::undefined_value(),
+         RelocInfo::EMBEDDED_OBJECT);
+    cmp(Operand(rsp, 0), kScratchRegister);
+    Check(not_equal, "code object not properly patched");
+  }
+}
+
+
+void MacroAssembler::LeaveFrame(StackFrame::Type type) {
+  if (FLAG_debug_code) {
+    movq(kScratchRegister, Immediate(Smi::FromInt(type)));
+    cmp(Operand(rbp, StandardFrameConstants::kMarkerOffset), kScratchRegister);
+    Check(equal, "stack frame types must match");
+  }
+  movq(rsp, rbp);
+  pop(rbp);
+}
+
+
+
+void MacroAssembler::EnterExitFrame(StackFrame::Type type) {
+  ASSERT(type == StackFrame::EXIT || type == StackFrame::EXIT_DEBUG);
+
+  // Setup the frame structure on the stack.
+  ASSERT(ExitFrameConstants::kPPDisplacement == +2 * kPointerSize);
+  ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
+  ASSERT(ExitFrameConstants::kCallerFPOffset ==  0 * kPointerSize);
+  push(rbp);
+  movq(rbp, rsp);
+
+  // Reserve room for entry stack pointer and push the debug marker.
+  ASSERT(ExitFrameConstants::kSPOffset  == -1 * kPointerSize);
+  push(Immediate(0));  // saved entry sp, patched before call
+  push(Immediate(type == StackFrame::EXIT_DEBUG ? 1 : 0));
+
+  // Save the frame pointer and the context in top.
+  ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
+  ExternalReference context_address(Top::k_context_address);
+  movq(kScratchRegister, rax);
+  movq(rax, rbp);
+  store_rax(c_entry_fp_address);
+  movq(rax, rsi);
+  store_rax(context_address);
+  movq(rax, kScratchRegister);
+
+  // Setup argc and argv in callee-saved registers.
+  int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
+  movq(rdi, rax);
+  lea(rsi, Operand(rbp, rax, kTimesPointerSize, offset));
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+  // Save the state of all registers to the stack from the memory
+  // location. This is needed to allow nested break points.
+  if (type == StackFrame::EXIT_DEBUG) {
+    // TODO(1243899): This should be symmetric to
+    // CopyRegistersFromStackToMemory() but it isn't! esp is assumed
+    // correct here, but computed for the other call. Very error
+    // prone! FIX THIS.  Actually there are deeper problems with
+    // register saving than this asymmetry (see the bug report
+    // associated with this issue).
+    PushRegistersFromMemory(kJSCallerSaved);
+  }
+#endif
+
+  // Reserve space for two arguments: argc and argv.
+  sub(rsp, Immediate(2 * kPointerSize));
+
+  // Get the required frame alignment for the OS.
+  static const int kFrameAlignment = OS::ActivationFrameAlignment();
+  if (kFrameAlignment > 0) {
+    ASSERT(IsPowerOf2(kFrameAlignment));
+    movq(r10, Immediate(-kFrameAlignment));
+    and_(rsp, r10);
+  }
+
+  // Patch the saved entry sp.
+  movq(Operand(rbp, ExitFrameConstants::kSPOffset), rsp);
+}
+
+
+void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+  // Restore the memory copy of the registers by digging them out from
+  // the stack. This is needed to allow nested break points.
+  if (type == StackFrame::EXIT_DEBUG) {
+    // It's okay to clobber register ebx below because we don't need
+    // the function pointer after this.
+    const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
+    int kOffset = ExitFrameConstants::kDebugMarkOffset - kCallerSavedSize;
+    lea(rbx, Operand(rbp, kOffset));
+    CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
+  }
+#endif
+
+  // Get the return address from the stack and restore the frame pointer.
+  movq(rcx, Operand(rbp, 1 * kPointerSize));
+  movq(rbp, Operand(rbp, 0 * kPointerSize));
+
+  // Pop the arguments and the receiver from the caller stack.
+  lea(rsp, Operand(rsi, 1 * kPointerSize));
+
+  // Restore current context from top and clear it in debug mode.
+  ExternalReference context_address(Top::k_context_address);
+  movq(kScratchRegister, context_address);
+  movq(rsi, Operand(kScratchRegister, 0));
+#ifdef DEBUG
+  movq(Operand(kScratchRegister, 0), Immediate(0));
+#endif
+
+  // Push the return address to get ready to return.
+  push(rcx);
+
+  // Clear the top frame.
+  ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
+  movq(kScratchRegister, c_entry_fp_address);
+  movq(Operand(kScratchRegister, 0), Immediate(0));
+}
+
+
 } }  // namespace v8::internal
index 8160e53..6b4d718 100644 (file)
@@ -31,6 +31,7 @@
 
 // Since there is no simulator for the ia32 architecture the only thing we can
 // do is to call the entry directly.
+// TODO(X64): Don't pass p0, since it isn't used?
 #define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
   entry(p0, p1, p2, p3, p4);
 
index 43ba4e9..bc611e6 100644 (file)
@@ -248,4 +248,5 @@ TEST(AssemblerX64LoopImmediates) {
   int result =  FUNCTION_CAST<F0>(buffer)();
   CHECK_EQ(1, result);
 }
+
 #undef __