// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #ifndef VM_ASSEMBLER_X64_H_ #define VM_ASSEMBLER_X64_H_ #ifndef VM_ASSEMBLER_H_ #error Do not include assembler_x64.h directly; use assembler.h instead. #endif #include "platform/assert.h" #include "platform/utils.h" #include "vm/constants_x64.h" namespace dart { // Forward declarations. class RuntimeEntry; class Immediate : public ValueObject { public: explicit Immediate(int64_t value) : value_(value) { } Immediate(const Immediate& other) : ValueObject(), value_(other.value_) { } int64_t value() const { return value_; } bool is_int8() const { return Utils::IsInt(8, value_); } bool is_uint8() const { return Utils::IsUint(8, value_); } bool is_uint16() const { return Utils::IsUint(16, value_); } bool is_int32() const { return Utils::IsInt(32, value_); } private: const int64_t value_; // TODO(5411081): Add DISALLOW_COPY_AND_ASSIGN(Immediate) once the mac // build issue is resolved. // And remove the unnecessary copy constructor. }; class Operand : public ValueObject { public: uint8_t rex() const { return rex_; } uint8_t mod() const { return (encoding_at(0) >> 6) & 3; } Register rm() const { int rm_rex = (rex_ & REX_B) << 3; return static_cast(rm_rex + (encoding_at(0) & 7)); } ScaleFactor scale() const { return static_cast((encoding_at(1) >> 6) & 3); } Register index() const { int index_rex = (rex_ & REX_X) << 2; return static_cast(index_rex + ((encoding_at(1) >> 3) & 7)); } Register base() const { int base_rex = (rex_ & REX_B) << 3; return static_cast(base_rex + (encoding_at(1) & 7)); } int8_t disp8() const { ASSERT(length_ >= 2); return static_cast(encoding_[length_ - 1]); } int32_t disp32() const { ASSERT(length_ >= 5); return bit_copy(encoding_[length_ - 4]); } Operand(const Operand& other) : ValueObject(), length_(other.length_), rex_(other.rex_) { memmove(&encoding_[0], &other.encoding_[0], other.length_); } Operand& operator=(const Operand& other) { length_ = other.length_; rex_ = other.rex_; memmove(&encoding_[0], &other.encoding_[0], other.length_); return *this; } protected: Operand() : length_(0), rex_(REX_NONE) { } // Needed by subclass Address. void SetModRM(int mod, Register rm) { ASSERT((mod & ~3) == 0); if ((rm > 7) && !((rm == R12) && (mod != 3))) { rex_ |= REX_B; } encoding_[0] = (mod << 6) | (rm & 7); length_ = 1; } void SetSIB(ScaleFactor scale, Register index, Register base) { ASSERT(length_ == 1); ASSERT((scale & ~3) == 0); if (base > 7) { ASSERT((rex_ & REX_B) == 0); // Must not have REX.B already set. rex_ |= REX_B; } if (index > 7) rex_ |= REX_X; encoding_[1] = (scale << 6) | ((index & 7) << 3) | (base & 7); length_ = 2; } void SetDisp8(int8_t disp) { ASSERT(length_ == 1 || length_ == 2); encoding_[length_++] = static_cast(disp); } void SetDisp32(int32_t disp) { ASSERT(length_ == 1 || length_ == 2); memmove(&encoding_[length_], &disp, sizeof(disp)); length_ += sizeof(disp); } private: uint8_t length_; uint8_t rex_; uint8_t encoding_[6]; explicit Operand(Register reg) : rex_(REX_NONE) { SetModRM(3, reg); } // Get the operand encoding byte at the given index. uint8_t encoding_at(int index) const { ASSERT(index >= 0 && index < length_); return encoding_[index]; } // Returns whether or not this operand is really the given register in // disguise. Used from the assembler to generate better encodings. bool IsRegister(Register reg) const { return ((reg > 7 ? 1 : 0) == (rex_ & REX_B)) // REX.B match. && ((encoding_at(0) & 0xF8) == 0xC0) // Addressing mode is register. && ((encoding_at(0) & 0x07) == reg); // Register codes match. } friend class Assembler; }; class Address : public Operand { public: Address(Register base, int32_t disp) { if ((disp == 0) && ((base & 7) != RBP)) { SetModRM(0, base); if ((base & 7) == RSP) { SetSIB(TIMES_1, RSP, base); } } else if (Utils::IsInt(8, disp)) { SetModRM(1, base); if ((base & 7) == RSP) { SetSIB(TIMES_1, RSP, base); } SetDisp8(disp); } else { SetModRM(2, base); if ((base & 7) == RSP) { SetSIB(TIMES_1, RSP, base); } SetDisp32(disp); } } Address(Register index, ScaleFactor scale, int32_t disp) { ASSERT(index != RSP); // Illegal addressing mode. SetModRM(0, RSP); SetSIB(scale, index, RBP); SetDisp32(disp); } Address(Register base, Register index, ScaleFactor scale, int32_t disp) { ASSERT(index != RSP); // Illegal addressing mode. if ((disp == 0) && ((base & 7) != RBP)) { SetModRM(0, RSP); SetSIB(scale, index, base); } else if (Utils::IsInt(8, disp)) { SetModRM(1, RSP); SetSIB(scale, index, base); SetDisp8(disp); } else { SetModRM(2, RSP); SetSIB(scale, index, base); SetDisp32(disp); } } Address(const Address& other) : Operand(other) { } Address& operator=(const Address& other) { Operand::operator=(other); return *this; } }; class FieldAddress : public Address { public: FieldAddress(Register base, int32_t disp) : Address(base, disp - kHeapObjectTag) { } FieldAddress(Register base, Register index, ScaleFactor scale, int32_t disp) : Address(base, index, scale, disp - kHeapObjectTag) { } FieldAddress(const FieldAddress& other) : Address(other) { } FieldAddress& operator=(const FieldAddress& other) { Address::operator=(other); return *this; } }; class Label : public ValueObject { public: Label() : position_(0), unresolved_(0) { #ifdef DEBUG for (int i = 0; i < kMaxUnresolvedBranches; i++) { unresolved_near_positions_[i] = -1; } #endif // DEBUG } ~Label() { // Assert if label is being destroyed with unresolved branches pending. ASSERT(!IsLinked()); ASSERT(!HasNear()); } // Returns the position for bound labels. Cannot be used for unused or linked // labels. int Position() const { ASSERT(IsBound()); return -position_ - kWordSize; } int LinkPosition() const { ASSERT(IsLinked()); return position_ - kWordSize; } int NearPosition() { ASSERT(HasNear()); return unresolved_near_positions_[--unresolved_]; } bool IsBound() const { return position_ < 0; } bool IsUnused() const { return (position_ == 0) && (unresolved_ == 0); } bool IsLinked() const { return position_ > 0; } bool HasNear() const { return unresolved_ != 0; } private: void BindTo(int position) { ASSERT(!IsBound()); ASSERT(!HasNear()); position_ = -position - kWordSize; ASSERT(IsBound()); } void LinkTo(int position) { ASSERT(!IsBound()); position_ = position + kWordSize; ASSERT(IsLinked()); } void NearLinkTo(int position) { ASSERT(!IsBound()); ASSERT(unresolved_ < kMaxUnresolvedBranches); unresolved_near_positions_[unresolved_++] = position; } static const int kMaxUnresolvedBranches = 20; int position_; int unresolved_; int unresolved_near_positions_[kMaxUnresolvedBranches]; friend class Assembler; DISALLOW_COPY_AND_ASSIGN(Label); }; class CPUFeatures : public AllStatic { public: static void InitOnce(); // x64 always has at least SSE2. static bool sse2_supported() { return true; } static bool sse4_1_supported(); static bool double_truncate_round_supported() { return sse4_1_supported(); } private: static const uint64_t kSSE4_1BitMask = static_cast(1) << 51; static bool sse4_1_supported_; #ifdef DEBUG static bool initialized_; #endif }; class Assembler : public ValueObject { public: Assembler() : buffer_(), prologue_offset_(-1), comments_() { } ~Assembler() { } static const bool kNearJump = true; static const bool kFarJump = false; /* * Emit Machine Instructions. */ void call(Register reg); void call(const Address& address); void call(Label* label); void call(const ExternalLabel* label); static const intptr_t kCallExternalLabelSize = 13; void pushq(Register reg); void pushq(const Address& address); void pushq(const Immediate& imm); void popq(Register reg); void popq(const Address& address); void movl(Register dst, Register src); void movl(Register dst, const Immediate& imm); void movl(Register dst, const Address& src); void movl(const Address& dst, Register src); void movzxb(Register dst, Register src); void movzxb(Register dst, const Address& src); void movsxb(Register dst, Register src); void movsxb(Register dst, const Address& src); void movb(Register dst, const Address& src); void movb(const Address& dst, Register src); void movb(const Address& dst, const Immediate& imm); void movzxw(Register dst, Register src); void movzxw(Register dst, const Address& src); void movsxw(Register dst, Register src); void movsxw(Register dst, const Address& src); void movw(Register dst, const Address& src); void movw(const Address& dst, Register src); void movq(Register dst, const Immediate& imm); void movq(Register dst, Register src); void movq(Register dst, const Address& src); void movq(const Address& dst, Register src); void movq(const Address& dst, const Immediate& imm); void movsxl(Register dst, const Address& src); void leaq(Register dst, const Address& src); void movss(XmmRegister dst, const Address& src); void movss(const Address& dst, XmmRegister src); void movss(XmmRegister dst, XmmRegister src); void movd(XmmRegister dst, Register src); void movd(Register dst, XmmRegister src); void addss(XmmRegister dst, XmmRegister src); void subss(XmmRegister dst, XmmRegister src); void mulss(XmmRegister dst, XmmRegister src); void divss(XmmRegister dst, XmmRegister src); void movsd(XmmRegister dst, const Address& src); void movsd(const Address& dst, XmmRegister src); void movsd(XmmRegister dst, XmmRegister src); void movaps(XmmRegister dst, XmmRegister src); void movups(const Address& dst, XmmRegister src); void movups(XmmRegister dst, const Address& src); void addsd(XmmRegister dst, XmmRegister src); void subsd(XmmRegister dst, XmmRegister src); void mulsd(XmmRegister dst, XmmRegister src); void divsd(XmmRegister dst, XmmRegister src); void addps(XmmRegister dst, XmmRegister src); void subps(XmmRegister dst, XmmRegister src); void divps(XmmRegister dst, XmmRegister src); void mulps(XmmRegister dst, XmmRegister src); void minps(XmmRegister dst, XmmRegister src); void maxps(XmmRegister dst, XmmRegister src); void andps(XmmRegister dst, XmmRegister src); void andps(XmmRegister dst, const Address& src); void orps(XmmRegister dst, XmmRegister src); void notps(XmmRegister dst); void negateps(XmmRegister dst); void absps(XmmRegister dst); void zerowps(XmmRegister dst); void cmppseq(XmmRegister dst, XmmRegister src); void cmppsneq(XmmRegister dst, XmmRegister src); void cmppslt(XmmRegister dst, XmmRegister src); void cmppsle(XmmRegister dst, XmmRegister src); void cmppsnlt(XmmRegister dst, XmmRegister src); void cmppsnle(XmmRegister dst, XmmRegister src); void sqrtps(XmmRegister dst); void rsqrtps(XmmRegister dst); void reciprocalps(XmmRegister dst); void set1ps(XmmRegister dst, Register tmp, const Immediate& imm); void shufps(XmmRegister dst, XmmRegister src, const Immediate& mask); void comisd(XmmRegister a, XmmRegister b); void cvtsi2sd(XmmRegister a, Register b); void cvttsd2siq(Register dst, XmmRegister src); void cvtss2sd(XmmRegister dst, XmmRegister src); void cvtsd2ss(XmmRegister dst, XmmRegister src); void pxor(XmmRegister dst, XmmRegister src); enum RoundingMode { kRoundToNearest = 0x0, kRoundDown = 0x1, kRoundUp = 0x2, kRoundToZero = 0x3 }; void roundsd(XmmRegister dst, XmmRegister src, RoundingMode mode); void xchgl(Register dst, Register src); void xchgq(Register dst, Register src); void cmpl(Register reg, const Immediate& imm); void cmpl(Register reg0, Register reg1); void cmpl(Register reg, const Address& address); void cmpl(const Address& address, const Immediate& imm); void cmpq(Register reg, const Immediate& imm); void cmpq(const Address& address, Register reg); void cmpq(const Address& address, const Immediate& imm); void cmpq(Register reg0, Register reg1); void cmpq(Register reg, const Address& address); void testl(Register reg1, Register reg2); void testl(Register reg, const Immediate& imm); void testq(Register reg1, Register reg2); void testq(Register reg, const Immediate& imm); void andl(Register dst, Register src); void andl(Register dst, const Immediate& imm); void orl(Register dst, Register src); void orl(Register dst, const Immediate& imm); void xorl(Register dst, Register src); void andq(Register dst, Register src); void andq(Register dst, const Address& address); void andq(Register dst, const Immediate& imm); void orq(Register dst, Register src); void orq(Register dst, const Address& address); void orq(Register dst, const Immediate& imm); void xorq(Register dst, Register src); void xorq(Register dst, const Address& address); void xorq(const Address& dst, Register src); void xorq(Register dst, const Immediate& imm); void addl(Register dst, Register src); void addl(const Address& address, const Immediate& imm); void addq(Register dst, Register src); void addq(Register reg, const Immediate& imm); void addq(Register reg, const Address& address); void addq(const Address& address, const Immediate& imm); void addq(const Address& address, Register reg); void adcl(Register dst, Register src); void subl(Register dst, Register src); void cdq(); void cqo(); void idivl(Register reg); void idivq(Register reg); void imull(Register dst, Register src); void imull(Register reg, const Immediate& imm); void imulq(Register dst, Register src); void imulq(Register dst, const Address& address); void imulq(Register dst, const Immediate& imm); void subq(Register dst, Register src); void subq(Register reg, const Immediate& imm); void subq(Register reg, const Address& address); void shll(Register reg, const Immediate& imm); void shll(Register operand, Register shifter); void shrl(Register reg, const Immediate& imm); void shrl(Register operand, Register shifter); void sarl(Register reg, const Immediate& imm); void sarl(Register operand, Register shifter); void shlq(Register reg, const Immediate& imm); void shlq(Register operand, Register shifter); void shrq(Register reg, const Immediate& imm); void shrq(Register operand, Register shifter); void sarq(Register reg, const Immediate& imm); void sarq(Register operand, Register shifter); void incl(const Address& address); void decl(const Address& address); void incq(Register reg); void incq(const Address& address); void decq(Register reg); void decq(const Address& address); void negl(Register reg); void negq(Register reg); void notq(Register reg); void enter(const Immediate& imm); void leave(); void ret(); void movmskpd(Register dst, XmmRegister src); void movmskps(Register dst, XmmRegister src); void sqrtsd(XmmRegister dst, XmmRegister src); void xorpd(XmmRegister dst, const Address& src); void xorpd(XmmRegister dst, XmmRegister src); void xorps(XmmRegister dst, const Address& src); void xorps(XmmRegister dst, XmmRegister src); void andpd(XmmRegister dst, const Address& src); void fldl(const Address& src); void fstpl(const Address& dst); void fildl(const Address& src); void fincstp(); void ffree(intptr_t value); void fsin(); void fcos(); // 'size' indicates size in bytes and must be in the range 1..8. void nop(int size = 1); void int3(); void hlt(); void j(Condition condition, Label* label, bool near = kFarJump); void j(Condition condition, const ExternalLabel* label); void jmp(Register reg); void jmp(Label* label, bool near = kFarJump); void jmp(const ExternalLabel* label); void lock(); void cmpxchgl(const Address& address, Register reg); void lock_cmpxchgl(const Address& address, Register reg) { lock(); cmpxchgl(address, reg); } void cmpxchgq(const Address& address, Register reg); void lock_cmpxchgq(const Address& address, Register reg) { lock(); cmpxchgq(address, reg); } void cpuid(); // Issue memory to memory move through a TMP register. void MoveMemoryToMemory(const Address& dst, const Address& src) { movq(TMP, src); movq(dst, TMP); } void Exchange(Register reg, const Address& mem) { movq(TMP, mem); movq(mem, reg); movq(reg, TMP); } void Exchange(const Address& mem1, const Address& mem2) { movq(TMP, mem1); xorq(TMP, mem2); xorq(mem1, TMP); xorq(mem2, TMP); } /* * Macros for High-level operations and implemented on all architectures. */ void CompareRegisters(Register a, Register b); // Issues a move instruction if 'to' is not the same as 'from'. void MoveRegister(Register to, Register from); void PopRegister(Register r); void AddImmediate(Register reg, const Immediate& imm); void Drop(intptr_t stack_elements); void LoadObject(Register dst, const Object& object); void StoreObject(const Address& dst, const Object& obj); void PushObject(const Object& object); void CompareObject(Register reg, const Object& object); void LoadDoubleConstant(XmmRegister dst, double value); // Destroys value. void StoreIntoObject(Register object, // Object we are storing into. const Address& dest, // Where we are storing into. Register value); // Value we are storing. void StoreIntoObjectNoBarrier(Register object, const Address& dest, Register value); void DoubleNegate(XmmRegister d); void FloatNegate(XmmRegister f); void DoubleAbs(XmmRegister reg); void DoubleRound(XmmRegister dst, XmmRegister src, XmmRegister tmp); void LockCmpxchgl(const Address& address, Register reg) { lock(); cmpxchgl(address, reg); } void EnterFrame(intptr_t frame_space); void LeaveFrame(); void ReserveAlignedFrameSpace(intptr_t frame_space); // Create a frame for calling into runtime that preserves all volatile // registers. Frame's RSP is guaranteed to be correctly aligned and // frame_space bytes are reserved under it. void EnterCallRuntimeFrame(intptr_t frame_space); void LeaveCallRuntimeFrame(); void CallRuntime(const RuntimeEntry& entry); /* * Loading and comparing classes of objects. */ void LoadClassId(Register result, Register object); void LoadClassById(Register result, Register class_id); void LoadClass(Register result, Register object); void CompareClassId(Register object, intptr_t class_id); /* * Misc. functionality. */ void SmiTag(Register reg) { addq(reg, reg); } void SmiUntag(Register reg) { sarq(reg, Immediate(kSmiTagSize)); } int PreferredLoopAlignment() { return 16; } void Align(int alignment, int offset); void Bind(Label* label); void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3); const Code::Comments& GetCodeComments() const; int CodeSize() const { return buffer_.Size(); } int prologue_offset() const { return prologue_offset_; } const ZoneGrowableArray& GetPointerOffsets() const { return buffer_.pointer_offsets(); } void FinalizeInstructions(const MemoryRegion& region) { buffer_.FinalizeInstructions(region); } // Debugging and bringup support. void Stop(const char* message); void Unimplemented(const char* message); void Untested(const char* message); void Unreachable(const char* message); static void InitializeMemoryWithBreakpoints(uword data, int length); static const char* RegisterName(Register reg); static const char* FpuRegisterName(FpuRegister reg); private: AssemblerBuffer buffer_; int prologue_offset_; class CodeComment : public ZoneAllocated { public: CodeComment(intptr_t pc_offset, const String& comment) : pc_offset_(pc_offset), comment_(comment) { } intptr_t pc_offset() const { return pc_offset_; } const String& comment() const { return comment_; } private: intptr_t pc_offset_; const String& comment_; DISALLOW_COPY_AND_ASSIGN(CodeComment); }; GrowableArray comments_; inline void EmitUint8(uint8_t value); inline void EmitInt32(int32_t value); inline void EmitInt64(int64_t value); inline void EmitRegisterREX(Register reg, uint8_t rex); inline void EmitRegisterOperand(int rm, int reg); inline void EmitOperandREX(int rm, const Operand& operand, uint8_t rex); inline void EmitXmmRegisterOperand(int rm, XmmRegister reg); inline void EmitFixup(AssemblerFixup* fixup); inline void EmitOperandSizeOverride(); inline void EmitREX_RB(XmmRegister reg, XmmRegister base, uint8_t rex = REX_NONE); inline void EmitREX_RB(XmmRegister reg, const Operand& operand, uint8_t rex = REX_NONE); inline void EmitREX_RB(XmmRegister reg, Register base, uint8_t rex = REX_NONE); inline void EmitREX_RB(Register reg, XmmRegister base, uint8_t rex = REX_NONE); void EmitOperand(int rm, const Operand& operand); void EmitImmediate(const Immediate& imm); void EmitComplex(int rm, const Operand& operand, const Immediate& immediate); void EmitLabel(Label* label, int instruction_size); void EmitLabelLink(Label* label); void EmitNearLabelLink(Label* label); void EmitGenericShift(bool wide, int rm, Register reg, const Immediate& imm); void EmitGenericShift(bool wide, int rm, Register operand, Register shifter); void StoreIntoObjectFilter(Register object, Register value, Label* no_update); DISALLOW_ALLOCATION(); DISALLOW_COPY_AND_ASSIGN(Assembler); }; inline void Assembler::EmitUint8(uint8_t value) { buffer_.Emit(value); } inline void Assembler::EmitInt32(int32_t value) { buffer_.Emit(value); } inline void Assembler::EmitInt64(int64_t value) { buffer_.Emit(value); } inline void Assembler::EmitRegisterREX(Register reg, uint8_t rex) { ASSERT(reg != kNoRegister); rex |= (reg > 7 ? REX_B : REX_NONE); if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex); } inline void Assembler::EmitOperandREX(int rm, const Operand& operand, uint8_t rex) { rex |= (rm > 7 ? REX_R : REX_NONE) | operand.rex(); if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex); } inline void Assembler::EmitREX_RB(XmmRegister reg, XmmRegister base, uint8_t rex) { if (reg > 7) rex |= REX_R; if (base > 7) rex |= REX_B; if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex); } inline void Assembler::EmitREX_RB(XmmRegister reg, const Operand& operand, uint8_t rex) { if (reg > 7) rex |= REX_R; rex |= operand.rex(); if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex); } inline void Assembler::EmitREX_RB(XmmRegister reg, Register base, uint8_t rex) { if (reg > 7) rex |= REX_R; if (base > 7) rex |= REX_B; if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex); } inline void Assembler::EmitREX_RB(Register reg, XmmRegister base, uint8_t rex) { if (reg > 7) rex |= REX_R; if (base > 7) rex |= REX_B; if (rex != REX_NONE) EmitUint8(REX_PREFIX | rex); } inline void Assembler::EmitFixup(AssemblerFixup* fixup) { buffer_.EmitFixup(fixup); } inline void Assembler::EmitOperandSizeOverride() { EmitUint8(0x66); } } // namespace dart #endif // VM_ASSEMBLER_X64_H_