| // 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_IA32_H_ |
| #define VM_ASSEMBLER_IA32_H_ |
| |
| #ifndef VM_ASSEMBLER_H_ |
| #error Do not include assembler_ia32.h directly; use assembler.h instead. |
| #endif |
| |
| #include "platform/assert.h" |
| #include "platform/utils.h" |
| #include "vm/constants_ia32.h" |
| |
| namespace dart { |
| |
| // Forward declarations. |
| class RuntimeEntry; |
| class StubEntry; |
| |
| class Immediate : public ValueObject { |
| public: |
| explicit Immediate(int32_t value) : value_(value) { } |
| |
| Immediate(const Immediate& other) : ValueObject(), value_(other.value_) { } |
| |
| int32_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_); } |
| |
| private: |
| const int32_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 mod() const { |
| return (encoding_at(0) >> 6) & 3; |
| } |
| |
| Register rm() const { |
| return static_cast<Register>(encoding_at(0) & 7); |
| } |
| |
| ScaleFactor scale() const { |
| return static_cast<ScaleFactor>((encoding_at(1) >> 6) & 3); |
| } |
| |
| Register index() const { |
| return static_cast<Register>((encoding_at(1) >> 3) & 7); |
| } |
| |
| Register base() const { |
| return static_cast<Register>(encoding_at(1) & 7); |
| } |
| |
| int8_t disp8() const { |
| ASSERT(length_ >= 2); |
| return static_cast<int8_t>(encoding_[length_ - 1]); |
| } |
| |
| int32_t disp32() const { |
| ASSERT(length_ >= 5); |
| return bit_copy<int32_t>(encoding_[length_ - 4]); |
| } |
| |
| Operand(const Operand& other) : ValueObject(), length_(other.length_) { |
| memmove(&encoding_[0], &other.encoding_[0], other.length_); |
| } |
| |
| Operand& operator=(const Operand& other) { |
| length_ = other.length_; |
| memmove(&encoding_[0], &other.encoding_[0], other.length_); |
| return *this; |
| } |
| |
| bool Equals(const Operand& other) const { |
| if (length_ != other.length_) return false; |
| for (uint8_t i = 0; i < length_; i++) { |
| if (encoding_[i] != other.encoding_[i]) return false; |
| } |
| return true; |
| } |
| |
| protected: |
| Operand() : length_(0) { } // Needed by subclass Address. |
| |
| void SetModRM(int mod, Register rm) { |
| ASSERT((mod & ~3) == 0); |
| encoding_[0] = (mod << 6) | rm; |
| length_ = 1; |
| } |
| |
| void SetSIB(ScaleFactor scale, Register index, Register base) { |
| ASSERT(length_ == 1); |
| ASSERT((scale & ~3) == 0); |
| encoding_[1] = (scale << 6) | (index << 3) | base; |
| length_ = 2; |
| } |
| |
| void SetDisp8(int8_t disp) { |
| ASSERT(length_ == 1 || length_ == 2); |
| encoding_[length_++] = static_cast<uint8_t>(disp); |
| } |
| |
| void SetDisp32(int32_t disp) { |
| ASSERT(length_ == 1 || length_ == 2); |
| intptr_t disp_size = sizeof(disp); |
| memmove(&encoding_[length_], &disp, disp_size); |
| length_ += disp_size; |
| } |
| |
| private: |
| uint8_t length_; |
| uint8_t encoding_[6]; |
| uint8_t padding_; |
| |
| explicit Operand(Register reg) { SetModRM(3, reg); } |
| |
| // Get the operand encoding byte at the given index. |
| uint8_t encoding_at(intptr_t 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 ((encoding_[0] & 0xF8) == 0xC0) // Addressing mode is register only. |
| && ((encoding_[0] & 0x07) == reg); // Register codes match. |
| } |
| |
| friend class Assembler; |
| }; |
| |
| |
| class Address : public Operand { |
| public: |
| Address(Register base, int32_t disp) { |
| if (disp == 0 && base != EBP) { |
| SetModRM(0, base); |
| if (base == ESP) SetSIB(TIMES_1, ESP, base); |
| } else if (Utils::IsInt(8, disp)) { |
| SetModRM(1, base); |
| if (base == ESP) SetSIB(TIMES_1, ESP, base); |
| SetDisp8(disp); |
| } else { |
| SetModRM(2, base); |
| if (base == ESP) SetSIB(TIMES_1, ESP, base); |
| SetDisp32(disp); |
| } |
| } |
| |
| Address(Register index, ScaleFactor scale, int32_t disp) { |
| ASSERT(index != ESP); // Illegal addressing mode. |
| SetModRM(0, ESP); |
| SetSIB(scale, index, EBP); |
| SetDisp32(disp); |
| } |
| |
| // This addressing mode does not exist. |
| Address(Register index, ScaleFactor scale, Register r); |
| |
| Address(Register base, Register index, ScaleFactor scale, int32_t disp) { |
| ASSERT(index != ESP); // Illegal addressing mode. |
| if (disp == 0 && base != EBP) { |
| SetModRM(0, ESP); |
| SetSIB(scale, index, base); |
| } else if (Utils::IsInt(8, disp)) { |
| SetModRM(1, ESP); |
| SetSIB(scale, index, base); |
| SetDisp8(disp); |
| } else { |
| SetModRM(2, ESP); |
| SetSIB(scale, index, base); |
| SetDisp32(disp); |
| } |
| } |
| |
| // This addressing mode does not exist. |
| Address(Register base, Register index, ScaleFactor scale, Register r); |
| |
| Address(const Address& other) : Operand(other) { } |
| |
| Address& operator=(const Address& other) { |
| Operand::operator=(other); |
| return *this; |
| } |
| |
| static Address Absolute(const uword addr) { |
| Address result; |
| result.SetModRM(0, EBP); |
| result.SetDisp32(addr); |
| return result; |
| } |
| |
| private: |
| Address() { } // Needed by Address::Absolute. |
| }; |
| |
| |
| class FieldAddress : public Address { |
| public: |
| FieldAddress(Register base, int32_t disp) |
| : Address(base, disp - kHeapObjectTag) { } |
| |
| // This addressing mode does not exist. |
| FieldAddress(Register base, Register r); |
| |
| FieldAddress(Register base, Register index, ScaleFactor scale, int32_t disp) |
| : Address(base, index, scale, disp - kHeapObjectTag) { } |
| |
| // This addressing mode does not exist. |
| FieldAddress(Register base, Register index, ScaleFactor scale, Register r); |
| |
| 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. |
| intptr_t Position() const { |
| ASSERT(IsBound()); |
| return -position_ - kWordSize; |
| } |
| |
| intptr_t LinkPosition() const { |
| ASSERT(IsLinked()); |
| return position_ - kWordSize; |
| } |
| |
| intptr_t 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(intptr_t position) { |
| ASSERT(!IsBound()); |
| ASSERT(!HasNear()); |
| position_ = -position - kWordSize; |
| ASSERT(IsBound()); |
| } |
| |
| void LinkTo(intptr_t position) { |
| ASSERT(!IsBound()); |
| position_ = position + kWordSize; |
| ASSERT(IsLinked()); |
| } |
| |
| void NearLinkTo(intptr_t position) { |
| ASSERT(!IsBound()); |
| ASSERT(unresolved_ < kMaxUnresolvedBranches); |
| unresolved_near_positions_[unresolved_++] = position; |
| } |
| |
| static const int kMaxUnresolvedBranches = 20; |
| |
| intptr_t position_; |
| intptr_t unresolved_; |
| intptr_t unresolved_near_positions_[kMaxUnresolvedBranches]; |
| |
| friend class Assembler; |
| DISALLOW_COPY_AND_ASSIGN(Label); |
| }; |
| |
| |
| class Assembler : public ValueObject { |
| public: |
| explicit Assembler(bool use_far_branches = false) |
| : buffer_(), |
| prologue_offset_(-1), |
| jit_cookie_(0), |
| comments_(), |
| code_(Code::ZoneHandle()) { |
| // This mode is only needed and implemented for MIPS and ARM. |
| ASSERT(!use_far_branches); |
| } |
| ~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 = 5; |
| |
| void pushl(Register reg); |
| void pushl(const Address& address); |
| void pushl(const Immediate& imm); |
| |
| void popl(Register reg); |
| void popl(const Address& address); |
| |
| void pushal(); |
| void popal(); |
| |
| void setcc(Condition condition, ByteRegister dst); |
| |
| void movl(Register dst, const Immediate& src); |
| void movl(Register dst, Register src); |
| |
| void movl(Register dst, const Address& src); |
| void movl(const Address& dst, Register src); |
| void movl(const Address& dst, const Immediate& imm); |
| |
| void movzxb(Register dst, ByteRegister src); |
| void movzxb(Register dst, const Address& src); |
| void movsxb(Register dst, ByteRegister src); |
| void movsxb(Register dst, const Address& src); |
| void movb(Register dst, const Address& src); |
| void movb(const Address& dst, ByteRegister 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 movw(const Address& dst, const Immediate& imm); |
| |
| void leal(Register dst, const Address& src); |
| |
| void cmovno(Register dst, Register src); |
| void cmove(Register dst, Register src); |
| void cmovne(Register dst, Register src); |
| void cmovs(Register dst, Register src); |
| void cmovns(Register dst, Register src); |
| |
| void cmovgel(Register dst, Register src); |
| void cmovlessl(Register dst, Register src); |
| |
| void rep_movsb(); |
| |
| 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 movq(const Address& dst, XmmRegister src); |
| void movq(XmmRegister dst, const Address& src); |
| |
| void addss(XmmRegister dst, XmmRegister src); |
| void addss(XmmRegister dst, const Address& src); |
| void subss(XmmRegister dst, XmmRegister src); |
| void subss(XmmRegister dst, const Address& src); |
| void mulss(XmmRegister dst, XmmRegister src); |
| void mulss(XmmRegister dst, const Address& src); |
| void divss(XmmRegister dst, XmmRegister src); |
| void divss(XmmRegister dst, const Address& 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(XmmRegister dst, const Address& src); |
| void movups(const Address& dst, XmmRegister src); |
| |
| void addsd(XmmRegister dst, XmmRegister src); |
| void addsd(XmmRegister dst, const Address& src); |
| void subsd(XmmRegister dst, XmmRegister src); |
| void subsd(XmmRegister dst, const Address& src); |
| void mulsd(XmmRegister dst, XmmRegister src); |
| void mulsd(XmmRegister dst, const Address& src); |
| void divsd(XmmRegister dst, XmmRegister src); |
| void divsd(XmmRegister dst, const Address& src); |
| |
| void addpl(XmmRegister dst, XmmRegister src); |
| void subpl(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 movhlps(XmmRegister dst, XmmRegister src); |
| void movlhps(XmmRegister dst, XmmRegister src); |
| void unpcklps(XmmRegister dst, XmmRegister src); |
| void unpckhps(XmmRegister dst, XmmRegister src); |
| void unpcklpd(XmmRegister dst, XmmRegister src); |
| void unpckhpd(XmmRegister dst, XmmRegister src); |
| |
| void set1ps(XmmRegister dst, Register tmp, const Immediate& imm); |
| void shufps(XmmRegister dst, XmmRegister src, const Immediate& mask); |
| |
| void addpd(XmmRegister dst, XmmRegister src); |
| void negatepd(XmmRegister dst); |
| void subpd(XmmRegister dst, XmmRegister src); |
| void mulpd(XmmRegister dst, XmmRegister src); |
| void divpd(XmmRegister dst, XmmRegister src); |
| void abspd(XmmRegister dst); |
| void minpd(XmmRegister dst, XmmRegister src); |
| void maxpd(XmmRegister dst, XmmRegister src); |
| void sqrtpd(XmmRegister dst); |
| void cvtps2pd(XmmRegister dst, XmmRegister src); |
| void cvtpd2ps(XmmRegister dst, XmmRegister src); |
| void shufpd(XmmRegister dst, XmmRegister src, const Immediate& mask); |
| |
| void cvtsi2ss(XmmRegister dst, Register src); |
| void cvtsi2sd(XmmRegister dst, Register src); |
| |
| void cvtss2si(Register dst, XmmRegister src); |
| void cvtss2sd(XmmRegister dst, XmmRegister src); |
| |
| void cvtsd2si(Register dst, XmmRegister src); |
| void cvtsd2ss(XmmRegister dst, XmmRegister src); |
| |
| void cvttss2si(Register dst, XmmRegister src); |
| void cvttsd2si(Register dst, XmmRegister src); |
| |
| void cvtdq2pd(XmmRegister dst, XmmRegister src); |
| |
| void comiss(XmmRegister a, XmmRegister b); |
| void comisd(XmmRegister a, XmmRegister b); |
| |
| void movmskpd(Register dst, XmmRegister src); |
| void movmskps(Register dst, XmmRegister src); |
| |
| void sqrtsd(XmmRegister dst, XmmRegister src); |
| void sqrtss(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 andpd(XmmRegister dst, XmmRegister src); |
| |
| void orpd(XmmRegister dst, XmmRegister src); |
| |
| void pextrd(Register dst, XmmRegister src, const Immediate& imm); |
| void pmovsxdq(XmmRegister dst, XmmRegister src); |
| void pcmpeqq(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 flds(const Address& src); |
| void fstps(const Address& dst); |
| |
| void fldl(const Address& src); |
| void fstpl(const Address& dst); |
| |
| void fnstcw(const Address& dst); |
| void fldcw(const Address& src); |
| |
| void fistpl(const Address& dst); |
| void fistps(const Address& dst); |
| void fildl(const Address& src); |
| void filds(const Address& src); |
| |
| void fincstp(); |
| void ffree(intptr_t value); |
| |
| void fsin(); |
| void fcos(); |
| void fsincos(); |
| void fptan(); |
| |
| void xchgl(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, Register reg); |
| void cmpl(const Address& address, const Immediate& imm); |
| void cmpw(Register reg, const Address& address); |
| void cmpw(const Address& address, const Immediate& imm); |
| void cmpb(const Address& address, const Immediate& imm); |
| |
| void testl(Register reg1, Register reg2); |
| void testl(Register reg, const Immediate& imm); |
| void testb(const Address& address, const Immediate& imm); |
| |
| void andl(Register dst, const Immediate& imm); |
| void andl(Register dst, Register src); |
| void andl(Register dst, const Address& address); |
| |
| void orl(Register dst, const Immediate& imm); |
| void orl(Register dst, Register src); |
| void orl(Register dst, const Address& address); |
| void orl(const Address& address, Register dst); |
| |
| void xorl(Register dst, const Immediate& imm); |
| void xorl(Register dst, Register src); |
| void xorl(Register dst, const Address& address); |
| |
| void addl(Register dst, Register src); |
| void addl(Register reg, const Immediate& imm); |
| void addl(Register reg, const Address& address); |
| |
| void addl(const Address& address, Register reg); |
| void addl(const Address& address, const Immediate& imm); |
| |
| void adcl(Register dst, Register src); |
| void adcl(Register reg, const Immediate& imm); |
| void adcl(Register dst, const Address& address); |
| void adcl(const Address& dst, Register src); |
| |
| void subl(Register dst, Register src); |
| void subl(Register reg, const Immediate& imm); |
| void subl(Register reg, const Address& address); |
| void subl(const Address& address, Register reg); |
| |
| void cdq(); |
| |
| void idivl(Register reg); |
| |
| void divl(Register reg); |
| |
| void imull(Register dst, Register src); |
| void imull(Register reg, const Immediate& imm); |
| void imull(Register reg, const Address& address); |
| |
| void imull(Register reg); |
| void imull(const Address& address); |
| |
| void mull(Register reg); |
| void mull(const Address& address); |
| |
| void sbbl(Register dst, Register src); |
| void sbbl(Register reg, const Immediate& imm); |
| void sbbl(Register reg, const Address& address); |
| void sbbl(const Address& address, Register reg); |
| |
| void incl(Register reg); |
| void incl(const Address& address); |
| |
| void decl(Register reg); |
| void decl(const Address& address); |
| |
| void shll(Register reg, const Immediate& imm); |
| void shll(Register operand, Register shifter); |
| void shll(const Address& 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 sarl(const Address& address, Register shifter); |
| void shldl(Register dst, Register src, Register shifter); |
| void shldl(Register dst, Register src, const Immediate& imm); |
| void shldl(const Address& operand, Register src, Register shifter); |
| void shrdl(Register dst, Register src, Register shifter); |
| void shrdl(Register dst, Register src, const Immediate& imm); |
| void shrdl(const Address& dst, Register src, Register shifter); |
| |
| void negl(Register reg); |
| void notl(Register reg); |
| |
| void bsrl(Register dst, Register src); |
| |
| void bt(Register base, Register offset); |
| |
| void enter(const Immediate& imm); |
| void leave(); |
| |
| void ret(); |
| void ret(const Immediate& imm); |
| |
| // 'size' indicates size in bytes and must be in the range 1..8. |
| void nop(int size = 1); |
| void int3(); |
| void hlt(); |
| |
| static uword GetBreakInstructionFiller() { |
| return 0xCCCCCCCC; |
| } |
| |
| // Note: verified_mem mode forces far jumps. |
| void j(Condition condition, Label* label, bool near = kFarJump); |
| void j(Condition condition, const ExternalLabel* label); |
| |
| void jmp(Register reg); |
| // Note: verified_mem mode forces far jumps. |
| void jmp(Label* label, bool near = kFarJump); |
| void jmp(const ExternalLabel* label); |
| |
| void lock(); |
| void cmpxchgl(const Address& address, Register reg); |
| |
| void cpuid(); |
| |
| /* |
| * 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 SubImmediate(Register reg, const Immediate& imm); |
| |
| void Drop(intptr_t stack_elements); |
| |
| void LoadIsolate(Register dst); |
| |
| void LoadObject(Register dst, const Object& object); |
| |
| // If 'object' is a large Smi, xor it with a per-assembler cookie value to |
| // prevent user-controlled immediates from appearing in the code stream. |
| void LoadObjectSafely(Register dst, const Object& object); |
| |
| void PushObject(const Object& object); |
| void CompareObject(Register reg, const Object& object); |
| void LoadDoubleConstant(XmmRegister dst, double value); |
| |
| // When storing into a heap object field, knowledge of the previous content |
| // is expressed through these constants. |
| enum FieldContent { |
| kEmptyOrSmiOrNull, // Empty = garbage/zapped in release/debug mode. |
| kHeapObjectOrSmi, |
| kOnlySmi, |
| }; |
| |
| void StoreIntoObject(Register object, // Object we are storing into. |
| const Address& dest, // Where we are storing into. |
| Register value, // Value we are storing. |
| bool can_value_be_smi = true); |
| |
| void StoreIntoObjectNoBarrier(Register object, |
| const Address& dest, |
| Register value, |
| FieldContent old_content = kHeapObjectOrSmi); |
| void InitializeFieldNoBarrier(Register object, |
| const Address& dest, |
| Register value) { |
| return StoreIntoObjectNoBarrier(object, dest, value, kEmptyOrSmiOrNull); |
| } |
| void StoreIntoObjectNoBarrier(Register object, |
| const Address& dest, |
| const Object& value, |
| FieldContent old_content = kHeapObjectOrSmi); |
| void InitializeFieldNoBarrier(Register object, |
| const Address& dest, |
| const Object& value) { |
| return StoreIntoObjectNoBarrier(object, dest, value, kEmptyOrSmiOrNull); |
| } |
| |
| // Stores a Smi value into a heap object field that always contains a Smi. |
| void StoreIntoSmiField(const Address& dest, Register value); |
| void ZeroInitSmiField(const Address& dest); |
| // Increments a Smi field. Leaves flags in same state as an 'addl'. |
| void IncrementSmiField(const Address& dest, int32_t increment); |
| |
| void DoubleNegate(XmmRegister d); |
| void FloatNegate(XmmRegister f); |
| |
| void DoubleAbs(XmmRegister reg); |
| |
| 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, intptr_t argument_count); |
| |
| void Call(const StubEntry& stub_entry); |
| |
| void Jmp(const StubEntry& stub_entry); |
| void J(Condition condition, const StubEntry& stub_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, Register scratch); |
| |
| void CompareClassId(Register object, intptr_t class_id, Register scratch); |
| |
| void LoadClassIdMayBeSmi(Register result, Register object); |
| void LoadTaggedClassIdMayBeSmi(Register result, Register object); |
| |
| void SmiUntagOrCheckClass(Register object, |
| intptr_t class_id, |
| Register scratch, |
| Label* is_smi); |
| |
| void ComputeRange(Register result, |
| Register value, |
| Register lo_temp, |
| Register hi_temp, |
| Label* miss); |
| |
| void UpdateRangeFeedback(Register value, |
| intptr_t index, |
| Register ic_data, |
| Register scratch1, |
| Register scratch2, |
| Register scratch3, |
| Label* miss); |
| |
| static Address ElementAddressForIntIndex(bool is_external, |
| intptr_t cid, |
| intptr_t index_scale, |
| Register array, |
| intptr_t index); |
| |
| static Address ElementAddressForRegIndex(bool is_external, |
| intptr_t cid, |
| intptr_t index_scale, |
| Register array, |
| Register index); |
| |
| static Address VMTagAddress() { |
| return Address(THR, Thread::vm_tag_offset()); |
| } |
| |
| /* |
| * Misc. functionality |
| */ |
| void SmiTag(Register reg) { |
| addl(reg, reg); |
| } |
| |
| void SmiUntag(Register reg) { |
| sarl(reg, Immediate(kSmiTagSize)); |
| } |
| |
| intptr_t PreferredLoopAlignment() { return 16; } |
| void Align(intptr_t alignment, intptr_t offset); |
| void Bind(Label* label); |
| void Jump(Label* label) { jmp(label); } |
| |
| // Address of code at offset. |
| uword CodeAddress(intptr_t offset) { |
| return buffer_.Address(offset); |
| } |
| |
| intptr_t CodeSize() const { return buffer_.Size(); } |
| intptr_t prologue_offset() const { return prologue_offset_; } |
| |
| // Count the fixups that produce a pointer offset, without processing |
| // the fixups. |
| intptr_t CountPointerOffsets() const { |
| return buffer_.CountPointerOffsets(); |
| } |
| const ZoneGrowableArray<intptr_t>& GetPointerOffsets() const { |
| return buffer_.pointer_offsets(); |
| } |
| |
| ObjectPoolWrapper& object_pool_wrapper() { return object_pool_wrapper_; } |
| |
| RawObjectPool* MakeObjectPool() { |
| return object_pool_wrapper_.MakeObjectPool(); |
| } |
| |
| void FinalizeInstructions(const MemoryRegion& region) { |
| buffer_.FinalizeInstructions(region); |
| } |
| |
| // Set up a Dart frame on entry with a frame pointer and PC information to |
| // enable easy access to the RawInstruction object of code corresponding |
| // to this frame. |
| // The dart frame layout is as follows: |
| // .... |
| // ret PC |
| // saved EBP <=== EBP |
| // pc (used to derive the RawInstruction Object of the dart code) |
| // locals space <=== ESP |
| // ..... |
| // This code sets this up with the sequence: |
| // pushl ebp |
| // movl ebp, esp |
| // call L |
| // L: <code to adjust saved pc if there is any intrinsification code> |
| // ..... |
| void EnterDartFrame(intptr_t frame_size); |
| |
| // Set up a Dart frame for a function compiled for on-stack replacement. |
| // The frame layout is a normal Dart frame, but the frame is partially set |
| // up on entry (it is the frame of the unoptimized code). |
| void EnterOsrFrame(intptr_t extra_size); |
| |
| // Set up a stub frame so that the stack traversal code can easily identify |
| // a stub frame. |
| // The stub frame layout is as follows: |
| // .... |
| // ret PC |
| // saved EBP |
| // 0 (used to indicate frame is a stub frame) |
| // ..... |
| // This code sets this up with the sequence: |
| // pushl ebp |
| // movl ebp, esp |
| // pushl immediate(0) |
| // ..... |
| void EnterStubFrame(); |
| static const intptr_t kEnterStubFramePushedWords = 2; |
| |
| // Instruction pattern from entrypoint is used in dart frame prologs |
| // to set up the frame and save a PC which can be used to figure out the |
| // RawInstruction object corresponding to the code running in the frame. |
| // entrypoint: |
| // pushl ebp (size is 1 byte) |
| // movl ebp, esp (size is 2 bytes) |
| // call L (size is 5 bytes) |
| // L: |
| static const intptr_t kEntryPointToPcMarkerOffset = 8; |
| static intptr_t EntryPointToPcMarkerOffset() { |
| return kEntryPointToPcMarkerOffset; |
| } |
| |
| // If allocation tracing for |cid| is enabled, will jump to |trace| label, |
| // which will allocate in the runtime where tracing occurs. |
| void MaybeTraceAllocation(intptr_t cid, |
| Register temp_reg, |
| Label* trace, |
| bool near_jump, |
| bool inline_isolate = true); |
| |
| void UpdateAllocationStats(intptr_t cid, |
| Register temp_reg, |
| Heap::Space space, |
| bool inline_isolate = true); |
| |
| void UpdateAllocationStatsWithSize(intptr_t cid, |
| Register size_reg, |
| Register temp_reg, |
| Heap::Space space, |
| bool inline_isolate = true); |
| void UpdateAllocationStatsWithSize(intptr_t cid, |
| intptr_t instance_size, |
| Register temp_reg, |
| Heap::Space space, |
| bool inline_isolate = true); |
| |
| // Inlined allocation of an instance of class 'cls', code has no runtime |
| // calls. Jump to 'failure' if the instance cannot be allocated here. |
| // Allocated instance is returned in 'instance_reg'. |
| // Only the tags field of the object is initialized. |
| void TryAllocate(const Class& cls, |
| Label* failure, |
| bool near_jump, |
| Register instance_reg, |
| Register temp_reg); |
| |
| void TryAllocateArray(intptr_t cid, |
| intptr_t instance_size, |
| Label* failure, |
| bool near_jump, |
| Register instance, |
| Register end_address, |
| Register temp); |
| |
| // 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, intptr_t length); |
| |
| void Comment(const char* format, ...) PRINTF_ATTRIBUTE(2, 3); |
| static bool EmittingComments(); |
| |
| const Code::Comments& GetCodeComments() const; |
| |
| static const char* RegisterName(Register reg); |
| static const char* FpuRegisterName(FpuRegister reg); |
| |
| // Smis that do not fit into 17 bits (16 bits of payload) are unsafe. |
| static bool IsSafeSmi(const Object& object) { |
| if (!object.IsSmi()) { |
| return false; |
| } |
| |
| if (Utils::IsInt(17, reinterpret_cast<intptr_t>(object.raw()))) { |
| return true; |
| } |
| |
| // Single bit smis (powers of two) and corresponding masks are safe. |
| const intptr_t value = Smi::Cast(object).Value(); |
| if (Utils::IsPowerOfTwo(value) || Utils::IsPowerOfTwo(value + 1)) { |
| return true; |
| } |
| |
| return false; |
| } |
| static bool IsSafe(const Object& object) { |
| return !object.IsSmi() || IsSafeSmi(object); |
| } |
| |
| void set_code_object(const Code& code) { |
| code_ ^= code.raw(); |
| } |
| |
| void PushCodeObject(); |
| |
| private: |
| 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); |
| }; |
| |
| |
| inline void EmitUint8(uint8_t value); |
| inline void EmitInt32(int32_t value); |
| inline void EmitRegisterOperand(int rm, int reg); |
| inline void EmitXmmRegisterOperand(int rm, XmmRegister reg); |
| inline void EmitFixup(AssemblerFixup* fixup); |
| inline void EmitOperandSizeOverride(); |
| |
| 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, intptr_t instruction_size); |
| void EmitLabelLink(Label* label); |
| void EmitNearLabelLink(Label* label); |
| |
| void EmitGenericShift(int rm, Register reg, const Immediate& imm); |
| void EmitGenericShift(int rm, const Operand& operand, Register shifter); |
| |
| void StoreIntoObjectFilter(Register object, Register value, Label* no_update); |
| |
| // Shorter filtering sequence that assumes that value is not a smi. |
| void StoreIntoObjectFilterNoSmi(Register object, |
| Register value, |
| Label* no_update); |
| #if defined(DEBUG) |
| void VerifyUninitialized(const Address& address); |
| void VerifyObjectOrSmi(const Address& address); |
| void VerifySmi(const Address& address, const char* stop_msg = "Expected Smi"); |
| #endif // DEBUG |
| // Like VerifiedMemory::Verify(address, kWordSize) and ::Write, but also, |
| // in DEBUG mode, verifies that 'address' has content of type 'old_content'. |
| void VerifyHeapWord(const Address& address, FieldContent old_content); |
| void VerifiedWrite(const Address& dest, |
| Register value, |
| FieldContent old_content); |
| void UnverifiedStoreOldObject(const Address& dest, const Object& value); |
| |
| int32_t jit_cookie(); |
| |
| AssemblerBuffer buffer_; |
| ObjectPoolWrapper object_pool_wrapper_; |
| intptr_t prologue_offset_; |
| int32_t jit_cookie_; |
| GrowableArray<CodeComment*> comments_; |
| Code& code_; |
| |
| DISALLOW_ALLOCATION(); |
| DISALLOW_COPY_AND_ASSIGN(Assembler); |
| }; |
| |
| |
| inline void Assembler::EmitUint8(uint8_t value) { |
| buffer_.Emit<uint8_t>(value); |
| } |
| |
| |
| inline void Assembler::EmitInt32(int32_t value) { |
| buffer_.Emit<int32_t>(value); |
| } |
| |
| |
| inline void Assembler::EmitRegisterOperand(int rm, int reg) { |
| ASSERT(rm >= 0 && rm < 8); |
| buffer_.Emit<uint8_t>(0xC0 + (rm << 3) + reg); |
| } |
| |
| |
| inline void Assembler::EmitXmmRegisterOperand(int rm, XmmRegister reg) { |
| EmitRegisterOperand(rm, static_cast<Register>(reg)); |
| } |
| |
| |
| inline void Assembler::EmitFixup(AssemblerFixup* fixup) { |
| buffer_.EmitFixup(fixup); |
| } |
| |
| |
| inline void Assembler::EmitOperandSizeOverride() { |
| EmitUint8(0x66); |
| } |
| |
| } // namespace dart |
| |
| #endif // VM_ASSEMBLER_IA32_H_ |