// the sentinel as its context, it is an arguments adaptor frame. It
// must be tagged as a small integer to avoid GC issues. Crud.
enum {
- SENTINEL = (1 << kSmiTagSize) | kSmiTag
+ SENTINEL = (1 << kSmiTagSize) | kSmiTag,
+ NON_SENTINEL = ~SENTINEL
};
virtual Type type() const { return ARGUMENTS_ADAPTOR; }
return static_cast<Condition>(cc ^ 1);
}
+// -----------------------------------------------------------------------------
+
+Immediate::Immediate(Smi* value) {
+ value_ = static_cast<int32_t>(reinterpret_cast<intptr_t>(value));
+}
// -----------------------------------------------------------------------------
// Implementation of Assembler
void Assembler::emitq(uint64_t x, RelocInfo::Mode rmode) {
Memory::uint64_at(pc_) = x;
- RecordRelocInfo(rmode, x);
+ if (rmode != RelocInfo::NONE) {
+ RecordRelocInfo(rmode, x);
+ }
+ pc_ += sizeof(uint64_t);
}
emit_modrm(0x2, adr);
}
-
void Assembler::cpuid() {
ASSERT(CpuFeatures::IsEnabled(CpuFeatures::CPUID));
EnsureSpace ensure_space(this);
}
+void Assembler::call(const Operand& op) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ // Opcode: FF /2 m64
+ emit_rex_64(op);
+ emit(0xFF);
+ emit_operand(2, op);
+}
+
+
void Assembler::cqo() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
}
+void Assembler::load_rax(void* value, RelocInfo::Mode mode) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit(0x48); // REX.W
+ emit(0xA1);
+ emitq(reinterpret_cast<uintptr_t>(value), mode);
+}
+
+
+void Assembler::load_rax(ExternalReference ref) {
+ load_rax(ref.address(), RelocInfo::EXTERNAL_REFERENCE);
+}
+
+
void Assembler::leave() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
}
+void Assembler::movq(const Operand& dst, Register src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit_rex_64(src, dst);
+ emit(0x89);
+ emit_operand(src, dst);
+}
+
+
+void Assembler::movq(Register dst, void* value, RelocInfo::Mode rmode) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit_rex_64(dst);
+ emit(0xB8 | (dst.code() & 0x7));
+ emitq(reinterpret_cast<uintptr_t>(value), rmode);
+}
+
+
void Assembler::movq(Register dst, int64_t value, RelocInfo::Mode rmode) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
}
-void Assembler::movq(const Operand& dst, Register src) {
+void Assembler::movq(Register dst, ExternalReference ref) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
- emit_rex_64(src, dst);
- emit(0x89);
- emit_operand(src, dst);
+ emit_rex_64(dst);
+ emit(0xB8 | (dst.code() & 0x7));
+ emitq(reinterpret_cast<uintptr_t>(ref.address()),
+ RelocInfo::EXTERNAL_REFERENCE);
}
}
+void Assembler::store_rax(void* dst, RelocInfo::Mode mode) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit(0x48); // REX.W
+ emit(0xA3);
+ emitq(reinterpret_cast<uintptr_t>(dst), mode);
+}
+
+
+void Assembler::store_rax(ExternalReference ref) {
+ store_rax(ref.address(), RelocInfo::EXTERNAL_REFERENCE);
+}
+
+
void Assembler::testb(Register reg, Immediate mask) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
void movq(Register dst, void* ptr, RelocInfo::Mode rmode);
void movq(Register dst, int64_t value, RelocInfo::Mode rmode);
void movq(Register dst, const char* s, RelocInfo::Mode rmode);
- void movq(Register dst, const ExternalReference& ext, RelocInfo::Mode rmode);
+ // Moves the address of the external reference into the register.
+ 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);
void movsx_b(Register dst, const Operand& src);
shift(dst, 0x5);
}
+ void store_rax(void* dst, RelocInfo::Mode mode);
+ void store_rax(ExternalReference ref);
+
void sub(Register dst, Register src) {
arithmetic_op(0x2B, dst, src);
}
// Call near absolute indirect, address in register
void call(Register adr);
+ // Call near indirect
+ void call(const Operand& operand);
+
// Jumps
// Jump short or near relative.
void jmp(Label* L); // unconditional jump to L
in_spilled_code_(false) {
}
+#define __ masm->
+
+
void CodeGenerator::DeclareGlobals(Handle<FixedArray> a) {
UNIMPLEMENTED();
}
masm->int3(); // TODO(X64): UNIMPLEMENTED.
}
-void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
- masm->int3(); // TODO(X64): UNIMPLEMENTED.
-}
-
+void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
+ Label invoke, exit;
+
+ // Setup frame.
+ __ push(rbp);
+ __ movq(rbp, rsp);
+
+ // Save callee-saved registers (X64 calling conventions).
+ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
+ // Push something that is not an arguments adaptor.
+ __ push(Immediate(ArgumentsAdaptorFrame::NON_SENTINEL));
+ __ push(Immediate(Smi::FromInt(marker))); // @ function offset
+ __ push(r12);
+ __ push(r13);
+ __ push(r14);
+ __ push(r15);
+ __ push(rdi);
+ __ push(rsi);
+ __ push(rbx);
+ // TODO(X64): Push XMM6-XMM15 (low 64 bits) as well, or make them
+ // callee-save in JS code as well.
+
+ // Save copies of the top frame descriptor on the stack.
+ ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
+ __ load_rax(c_entry_fp);
+ __ push(rax);
+
+ // Call a faked try-block that does the invoke.
+ __ call(&invoke);
+
+ // Caught exception: Store result (exception) in the pending
+ // exception field in the JSEnv and return a failure sentinel.
+ ExternalReference pending_exception(Top::k_pending_exception_address);
+ __ store_rax(pending_exception);
+ __ movq(rax, Failure::Exception(), RelocInfo::NONE);
+ __ jmp(&exit);
+
+ // 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());
+ __ store_rax(pending_exception);
+
+ // Fake a receiver (NULL).
+ __ push(Immediate(0)); // receiver
+
+ // Invoke the function by calling through JS entry trampoline
+ // builtin and pop the faked function when we return. We load the address
+ // from an external reference instead of inlining the call target address
+ // directly in the code, because the builtin stubs may not have been
+ // generated yet at the time this code is generated.
+ if (is_construct) {
+ ExternalReference construct_entry(Builtins::JSConstructEntryTrampoline);
+ __ load_rax(construct_entry);
+ } else {
+ ExternalReference entry(Builtins::JSEntryTrampoline);
+ __ load_rax(entry);
+ }
+ __ call(FieldOperand(rax, Code::kHeaderSize));
+
+ // 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));
+
+ // Restore the top frame descriptor from the stack.
+ __ bind(&exit);
+ __ movq(kScratchRegister, ExternalReference(Top::k_c_entry_fp_address));
+ __ pop(Operand(kScratchRegister, 0));
+
+ // Restore callee-saved registers (X64 conventions).
+ __ pop(rbx);
+ __ pop(rsi);
+ __ pop(rdi);
+ __ pop(r15);
+ __ pop(r14);
+ __ pop(r13);
+ __ pop(r12);
+ __ add(rsp, Immediate(2 * kPointerSize)); // remove markers
+
+ // Restore frame pointer and return.
+ __ pop(rbp);
+ __ ret(0);
+}
+
+
+#undef __
} } // namespace v8::internal
class StackHandlerConstants : public AllStatic {
public:
- static const int kNextOffset = -1 * kPointerSize;
- static const int kPPOffset = -1 * kPointerSize;
- static const int kFPOffset = -1 * kPointerSize;
+ static const int kNextOffset = 0 * kPointerSize;
+ static const int kPPOffset = 1 * kPointerSize;
+ static const int kFPOffset = 2 * kPointerSize;
- static const int kCodeOffset = -1 * kPointerSize;
+ static const int kCodeOffset = 3 * kPointerSize;
- static const int kStateOffset = -1 * kPointerSize;
- static const int kPCOffset = -1 * kPointerSize;
+ static const int kStateOffset = 4 * kPointerSize;
+ static const int kPCOffset = 5 * kPointerSize;
static const int kAddressDisplacement = -1 * kPointerSize;
- static const int kSize = kPCOffset + kPointerSize;
+ static const int kSize = 6 * kPointerSize;
};
code_object_(Heap::undefined_value()) {
}
+
void MacroAssembler::TailCallRuntime(ExternalReference const& a, int b) {
UNIMPLEMENTED();
}
}
+void MacroAssembler::PushTryHandler(CodeLocation try_location,
+ HandlerType type) {
+ // The pc (return address) is already on TOS.
+ // This code pushes state, code, frame pointer and parameter pointer.
+ // Check that they are expected next on the stack, int that order.
+ ASSERT_EQ(StackHandlerConstants::kStateOffset,
+ StackHandlerConstants::kPCOffset - kPointerSize);
+ ASSERT_EQ(StackHandlerConstants::kCodeOffset,
+ StackHandlerConstants::kStateOffset - kPointerSize);
+ ASSERT_EQ(StackHandlerConstants::kFPOffset,
+ StackHandlerConstants::kCodeOffset - kPointerSize);
+ ASSERT_EQ(StackHandlerConstants::kPPOffset,
+ StackHandlerConstants::kFPOffset - kPointerSize);
+
+ if (try_location == IN_JAVASCRIPT) {
+ if (type == TRY_CATCH_HANDLER) {
+ push(Immediate(StackHandler::TRY_CATCH));
+ } else {
+ push(Immediate(StackHandler::TRY_FINALLY));
+ }
+ push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent)));
+ push(rbp);
+ push(rdi);
+ } else {
+ ASSERT(try_location == IN_JS_ENTRY);
+ // The parameter pointer is meaningless here and ebp does not
+ // point to a JS frame. So we save NULL for both pp and ebp. We
+ // expect the code throwing an exception to check ebp before
+ // dereferencing it to restore the context.
+ push(Immediate(StackHandler::ENTRY));
+ push(Immediate(Smi::FromInt(StackHandler::kCodeNotPresent)));
+ push(Immediate(0)); // NULL frame pointer
+ push(Immediate(0)); // NULL parameter pointer
+ }
+ movq(kScratchRegister, ExternalReference(Top::k_handler_address));
+ // Cached TOS.
+ movq(rax, Operand(kScratchRegister, 0));
+ // Link this handler.
+ movq(Operand(kScratchRegister, 0), rsp);
+}
+
+
} } // namespace v8::internal
namespace v8 {
namespace internal {
+// Default scratch register used by MacroAssembler (and other code that needs
+// a spare register). The register isn't callee save, and not used by the
+// function calling convention.
static const Register kScratchRegister = r10;
// Forward declaration.
// Push a new try handler and link into try handler chain.
// The return address must be pushed before calling this helper.
- // On exit, eax contains TOS (next_sp).
+ // On exit, rax contains TOS (next_sp).
void PushTryHandler(CodeLocation try_location, HandlerType type);