diff options
Diffstat (limited to 'disas/libvixl/a64/assembler-a64.h')
-rw-r--r-- | disas/libvixl/a64/assembler-a64.h | 1784 |
1 files changed, 1784 insertions, 0 deletions
diff --git a/disas/libvixl/a64/assembler-a64.h b/disas/libvixl/a64/assembler-a64.h new file mode 100644 index 0000000..93b3011 --- /dev/null +++ b/disas/libvixl/a64/assembler-a64.h @@ -0,0 +1,1784 @@ +// Copyright 2013, ARM Limited +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of ARM Limited nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef VIXL_A64_ASSEMBLER_A64_H_ +#define VIXL_A64_ASSEMBLER_A64_H_ + +#include <list> + +#include "globals.h" +#include "utils.h" +#include "a64/instructions-a64.h" + +namespace vixl { + +typedef uint64_t RegList; +static const int kRegListSizeInBits = sizeof(RegList) * 8; + +// Registers. + +// Some CPURegister methods can return Register and FPRegister types, so we +// need to declare them in advance. +class Register; +class FPRegister; + + +class CPURegister { + public: + enum RegisterType { + // The kInvalid value is used to detect uninitialized static instances, + // which are always zero-initialized before any constructors are called. + kInvalid = 0, + kRegister, + kFPRegister, + kNoRegister + }; + + CPURegister() : code_(0), size_(0), type_(kNoRegister) { + ASSERT(!IsValid()); + ASSERT(IsNone()); + } + + CPURegister(unsigned code, unsigned size, RegisterType type) + : code_(code), size_(size), type_(type) { + ASSERT(IsValidOrNone()); + } + + unsigned code() const { + ASSERT(IsValid()); + return code_; + } + + RegisterType type() const { + ASSERT(IsValidOrNone()); + return type_; + } + + RegList Bit() const { + ASSERT(code_ < (sizeof(RegList) * 8)); + return IsValid() ? (static_cast<RegList>(1) << code_) : 0; + } + + unsigned size() const { + ASSERT(IsValid()); + return size_; + } + + int SizeInBytes() const { + ASSERT(IsValid()); + ASSERT(size() % 8 == 0); + return size_ / 8; + } + + int SizeInBits() const { + ASSERT(IsValid()); + return size_; + } + + bool Is32Bits() const { + ASSERT(IsValid()); + return size_ == 32; + } + + bool Is64Bits() const { + ASSERT(IsValid()); + return size_ == 64; + } + + bool IsValid() const { + if (IsValidRegister() || IsValidFPRegister()) { + ASSERT(!IsNone()); + return true; + } else { + ASSERT(IsNone()); + return false; + } + } + + bool IsValidRegister() const { + return IsRegister() && + ((size_ == kWRegSize) || (size_ == kXRegSize)) && + ((code_ < kNumberOfRegisters) || (code_ == kSPRegInternalCode)); + } + + bool IsValidFPRegister() const { + return IsFPRegister() && + ((size_ == kSRegSize) || (size_ == kDRegSize)) && + (code_ < kNumberOfFPRegisters); + } + + bool IsNone() const { + // kNoRegister types should always have size 0 and code 0. + ASSERT((type_ != kNoRegister) || (code_ == 0)); + ASSERT((type_ != kNoRegister) || (size_ == 0)); + + return type_ == kNoRegister; + } + + bool Is(const CPURegister& other) const { + ASSERT(IsValidOrNone() && other.IsValidOrNone()); + return (code_ == other.code_) && (size_ == other.size_) && + (type_ == other.type_); + } + + inline bool IsZero() const { + ASSERT(IsValid()); + return IsRegister() && (code_ == kZeroRegCode); + } + + inline bool IsSP() const { + ASSERT(IsValid()); + return IsRegister() && (code_ == kSPRegInternalCode); + } + + inline bool IsRegister() const { + return type_ == kRegister; + } + + inline bool IsFPRegister() const { + return type_ == kFPRegister; + } + + const Register& W() const; + const Register& X() const; + const FPRegister& S() const; + const FPRegister& D() const; + + inline bool IsSameSizeAndType(const CPURegister& other) const { + return (size_ == other.size_) && (type_ == other.type_); + } + + protected: + unsigned code_; + unsigned size_; + RegisterType type_; + + private: + bool IsValidOrNone() const { + return IsValid() || IsNone(); + } +}; + + +class Register : public CPURegister { + public: + explicit Register() : CPURegister() {} + inline explicit Register(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + ASSERT(IsValidRegister()); + } + explicit Register(unsigned code, unsigned size) + : CPURegister(code, size, kRegister) {} + + bool IsValid() const { + ASSERT(IsRegister() || IsNone()); + return IsValidRegister(); + } + + static const Register& WRegFromCode(unsigned code); + static const Register& XRegFromCode(unsigned code); + + // V8 compatibility. + static const int kNumRegisters = kNumberOfRegisters; + static const int kNumAllocatableRegisters = kNumberOfRegisters - 1; + + private: + static const Register wregisters[]; + static const Register xregisters[]; +}; + + +class FPRegister : public CPURegister { + public: + inline FPRegister() : CPURegister() {} + inline explicit FPRegister(const CPURegister& other) + : CPURegister(other.code(), other.size(), other.type()) { + ASSERT(IsValidFPRegister()); + } + inline FPRegister(unsigned code, unsigned size) + : CPURegister(code, size, kFPRegister) {} + + bool IsValid() const { + ASSERT(IsFPRegister() || IsNone()); + return IsValidFPRegister(); + } + + static const FPRegister& SRegFromCode(unsigned code); + static const FPRegister& DRegFromCode(unsigned code); + + // V8 compatibility. + static const int kNumRegisters = kNumberOfFPRegisters; + static const int kNumAllocatableRegisters = kNumberOfFPRegisters - 1; + + private: + static const FPRegister sregisters[]; + static const FPRegister dregisters[]; +}; + + +// No*Reg is used to indicate an unused argument, or an error case. Note that +// these all compare equal (using the Is() method). The Register and FPRegister +// variants are provided for convenience. +const Register NoReg; +const FPRegister NoFPReg; +const CPURegister NoCPUReg; + + +#define DEFINE_REGISTERS(N) \ +const Register w##N(N, kWRegSize); \ +const Register x##N(N, kXRegSize); +REGISTER_CODE_LIST(DEFINE_REGISTERS) +#undef DEFINE_REGISTERS +const Register wsp(kSPRegInternalCode, kWRegSize); +const Register sp(kSPRegInternalCode, kXRegSize); + + +#define DEFINE_FPREGISTERS(N) \ +const FPRegister s##N(N, kSRegSize); \ +const FPRegister d##N(N, kDRegSize); +REGISTER_CODE_LIST(DEFINE_FPREGISTERS) +#undef DEFINE_FPREGISTERS + + +// Registers aliases. +const Register ip0 = x16; +const Register ip1 = x17; +const Register lr = x30; +const Register xzr = x31; +const Register wzr = w31; + + +// AreAliased returns true if any of the named registers overlap. Arguments +// set to NoReg are ignored. The system stack pointer may be specified. +bool AreAliased(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoReg, + const CPURegister& reg4 = NoReg, + const CPURegister& reg5 = NoReg, + const CPURegister& reg6 = NoReg, + const CPURegister& reg7 = NoReg, + const CPURegister& reg8 = NoReg); + + +// AreSameSizeAndType returns true if all of the specified registers have the +// same size, and are of the same type. The system stack pointer may be +// specified. Arguments set to NoReg are ignored, as are any subsequent +// arguments. At least one argument (reg1) must be valid (not NoCPUReg). +bool AreSameSizeAndType(const CPURegister& reg1, + const CPURegister& reg2, + const CPURegister& reg3 = NoCPUReg, + const CPURegister& reg4 = NoCPUReg, + const CPURegister& reg5 = NoCPUReg, + const CPURegister& reg6 = NoCPUReg, + const CPURegister& reg7 = NoCPUReg, + const CPURegister& reg8 = NoCPUReg); + + +// Lists of registers. +class CPURegList { + public: + inline explicit CPURegList(CPURegister reg1, + CPURegister reg2 = NoCPUReg, + CPURegister reg3 = NoCPUReg, + CPURegister reg4 = NoCPUReg) + : list_(reg1.Bit() | reg2.Bit() | reg3.Bit() | reg4.Bit()), + size_(reg1.size()), type_(reg1.type()) { + ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); + ASSERT(IsValid()); + } + + inline CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) + : list_(list), size_(size), type_(type) { + ASSERT(IsValid()); + } + + inline CPURegList(CPURegister::RegisterType type, unsigned size, + unsigned first_reg, unsigned last_reg) + : size_(size), type_(type) { + ASSERT(((type == CPURegister::kRegister) && + (last_reg < kNumberOfRegisters)) || + ((type == CPURegister::kFPRegister) && + (last_reg < kNumberOfFPRegisters))); + ASSERT(last_reg >= first_reg); + list_ = (1UL << (last_reg + 1)) - 1; + list_ &= ~((1UL << first_reg) - 1); + ASSERT(IsValid()); + } + + inline CPURegister::RegisterType type() const { + ASSERT(IsValid()); + return type_; + } + + // Combine another CPURegList into this one. Registers that already exist in + // this list are left unchanged. The type and size of the registers in the + // 'other' list must match those in this list. + void Combine(const CPURegList& other) { + ASSERT(IsValid()); + ASSERT(other.type() == type_); + ASSERT(other.RegisterSizeInBits() == size_); + list_ |= other.list(); + } + + // Remove every register in the other CPURegList from this one. Registers that + // do not exist in this list are ignored. The type and size of the registers + // in the 'other' list must match those in this list. + void Remove(const CPURegList& other) { + ASSERT(IsValid()); + ASSERT(other.type() == type_); + ASSERT(other.RegisterSizeInBits() == size_); + list_ &= ~other.list(); + } + + // Variants of Combine and Remove which take a single register. + inline void Combine(const CPURegister& other) { + ASSERT(other.type() == type_); + ASSERT(other.size() == size_); + Combine(other.code()); + } + + inline void Remove(const CPURegister& other) { + ASSERT(other.type() == type_); + ASSERT(other.size() == size_); + Remove(other.code()); + } + + // Variants of Combine and Remove which take a single register by its code; + // the type and size of the register is inferred from this list. + inline void Combine(int code) { + ASSERT(IsValid()); + ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ |= (1UL << code); + } + + inline void Remove(int code) { + ASSERT(IsValid()); + ASSERT(CPURegister(code, size_, type_).IsValid()); + list_ &= ~(1UL << code); + } + + inline RegList list() const { + ASSERT(IsValid()); + return list_; + } + + // Remove all callee-saved registers from the list. This can be useful when + // preparing registers for an AAPCS64 function call, for example. + void RemoveCalleeSaved(); + + CPURegister PopLowestIndex(); + CPURegister PopHighestIndex(); + + // AAPCS64 callee-saved registers. + static CPURegList GetCalleeSaved(unsigned size = kXRegSize); + static CPURegList GetCalleeSavedFP(unsigned size = kDRegSize); + + // AAPCS64 caller-saved registers. Note that this includes lr. + static CPURegList GetCallerSaved(unsigned size = kXRegSize); + static CPURegList GetCallerSavedFP(unsigned size = kDRegSize); + + inline bool IsEmpty() const { + ASSERT(IsValid()); + return list_ == 0; + } + + inline bool IncludesAliasOf(const CPURegister& other) const { + ASSERT(IsValid()); + return (type_ == other.type()) && (other.Bit() & list_); + } + + inline int Count() const { + ASSERT(IsValid()); + return CountSetBits(list_, kRegListSizeInBits); + } + + inline unsigned RegisterSizeInBits() const { + ASSERT(IsValid()); + return size_; + } + + inline unsigned RegisterSizeInBytes() const { + int size_in_bits = RegisterSizeInBits(); + ASSERT((size_in_bits % 8) == 0); + return size_in_bits / 8; + } + + private: + RegList list_; + unsigned size_; + CPURegister::RegisterType type_; + + bool IsValid() const; +}; + + +// AAPCS64 callee-saved registers. +extern const CPURegList kCalleeSaved; +extern const CPURegList kCalleeSavedFP; + + +// AAPCS64 caller-saved registers. Note that this includes lr. +extern const CPURegList kCallerSaved; +extern const CPURegList kCallerSavedFP; + + +// Operand. +class Operand { + public: + // #<immediate> + // where <immediate> is int64_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(int64_t immediate); // NOLINT(runtime/explicit) + + // rm, {<shift> #<shift_amount>} + // where <shift> is one of {LSL, LSR, ASR, ROR}. + // <shift_amount> is uint6_t. + // This is allowed to be an implicit constructor because Operand is + // a wrapper class that doesn't normally perform any type conversion. + Operand(Register reg, + Shift shift = LSL, + unsigned shift_amount = 0); // NOLINT(runtime/explicit) + + // rm, {<extend> {#<shift_amount>}} + // where <extend> is one of {UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW, SXTX}. + // <shift_amount> is uint2_t. + explicit Operand(Register reg, Extend extend, unsigned shift_amount = 0); + + bool IsImmediate() const; + bool IsShiftedRegister() const; + bool IsExtendedRegister() const; + + // This returns an LSL shift (<= 4) operand as an equivalent extend operand, + // which helps in the encoding of instructions that use the stack pointer. + Operand ToExtendedRegister() const; + + int64_t immediate() const { + ASSERT(IsImmediate()); + return immediate_; + } + + Register reg() const { + ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return reg_; + } + + Shift shift() const { + ASSERT(IsShiftedRegister()); + return shift_; + } + + Extend extend() const { + ASSERT(IsExtendedRegister()); + return extend_; + } + + unsigned shift_amount() const { + ASSERT(IsShiftedRegister() || IsExtendedRegister()); + return shift_amount_; + } + + private: + int64_t immediate_; + Register reg_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +// MemOperand represents the addressing mode of a load or store instruction. +class MemOperand { + public: + explicit MemOperand(Register base, + ptrdiff_t offset = 0, + AddrMode addrmode = Offset); + explicit MemOperand(Register base, + Register regoffset, + Shift shift = LSL, + unsigned shift_amount = 0); + explicit MemOperand(Register base, + Register regoffset, + Extend extend, + unsigned shift_amount = 0); + explicit MemOperand(Register base, + const Operand& offset, + AddrMode addrmode = Offset); + + const Register& base() const { return base_; } + const Register& regoffset() const { return regoffset_; } + ptrdiff_t offset() const { return offset_; } + AddrMode addrmode() const { return addrmode_; } + Shift shift() const { return shift_; } + Extend extend() const { return extend_; } + unsigned shift_amount() const { return shift_amount_; } + bool IsImmediateOffset() const; + bool IsRegisterOffset() const; + bool IsPreIndex() const; + bool IsPostIndex() const; + + private: + Register base_; + Register regoffset_; + ptrdiff_t offset_; + AddrMode addrmode_; + Shift shift_; + Extend extend_; + unsigned shift_amount_; +}; + + +class Label { + public: + Label() : is_bound_(false), link_(NULL), target_(NULL) {} + ~Label() { + // If the label has been linked to, it needs to be bound to a target. + ASSERT(!IsLinked() || IsBound()); + } + + inline Instruction* link() const { return link_; } + inline Instruction* target() const { return target_; } + + inline bool IsBound() const { return is_bound_; } + inline bool IsLinked() const { return link_ != NULL; } + + inline void set_link(Instruction* new_link) { link_ = new_link; } + + static const int kEndOfChain = 0; + + private: + // Indicates if the label has been bound, ie its location is fixed. + bool is_bound_; + // Branches instructions branching to this label form a chained list, with + // their offset indicating where the next instruction is located. + // link_ points to the latest branch instruction generated branching to this + // branch. + // If link_ is not NULL, the label has been linked to. + Instruction* link_; + // The label location. + Instruction* target_; + + friend class Assembler; +}; + + +// TODO: Obtain better values for these, based on real-world data. +const int kLiteralPoolCheckInterval = 4 * KBytes; +const int kRecommendedLiteralPoolRange = 2 * kLiteralPoolCheckInterval; + + +// Control whether a branch over the literal pool should also be emitted. This +// is needed if the literal pool has to be emitted in the middle of the JITted +// code. +enum LiteralPoolEmitOption { + JumpRequired, + NoJumpRequired +}; + + +// Literal pool entry. +class Literal { + public: + Literal(Instruction* pc, uint64_t imm, unsigned size) + : pc_(pc), value_(imm), size_(size) {} + + private: + Instruction* pc_; + int64_t value_; + unsigned size_; + + friend class Assembler; +}; + + +// Assembler. +class Assembler { + public: + Assembler(byte* buffer, unsigned buffer_size); + + // The destructor asserts that one of the following is true: + // * The Assembler object has not been used. + // * Nothing has been emitted since the last Reset() call. + // * Nothing has been emitted since the last FinalizeCode() call. + ~Assembler(); + + // System functions. + + // Start generating code from the beginning of the buffer, discarding any code + // and data that has already been emitted into the buffer. + // + // In order to avoid any accidental transfer of state, Reset ASSERTs that the + // constant pool is not blocked. + void Reset(); + + // Finalize a code buffer of generated instructions. This function must be + // called before executing or copying code from the buffer. + void FinalizeCode(); + + // Label. + // Bind a label to the current PC. + void bind(Label* label); + int UpdateAndGetByteOffsetTo(Label* label); + inline int UpdateAndGetInstructionOffsetTo(Label* label) { + ASSERT(Label::kEndOfChain == 0); + return UpdateAndGetByteOffsetTo(label) >> kInstructionSizeLog2; + } + + + // Instruction set functions. + + // Branch / Jump instructions. + // Branch to register. + void br(const Register& xn); + + // Branch with link to register. + void blr(const Register& xn); + + // Branch to register with return hint. + void ret(const Register& xn = lr); + + // Unconditional branch to label. + void b(Label* label); + + // Conditional branch to label. + void b(Label* label, Condition cond); + + // Unconditional branch to PC offset. + void b(int imm26); + + // Conditional branch to PC offset. + void b(int imm19, Condition cond); + + // Branch with link to label. + void bl(Label* label); + + // Branch with link to PC offset. + void bl(int imm26); + + // Compare and branch to label if zero. + void cbz(const Register& rt, Label* label); + + // Compare and branch to PC offset if zero. + void cbz(const Register& rt, int imm19); + + // Compare and branch to label if not zero. + void cbnz(const Register& rt, Label* label); + + // Compare and branch to PC offset if not zero. + void cbnz(const Register& rt, int imm19); + + // Test bit and branch to label if zero. + void tbz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if zero. + void tbz(const Register& rt, unsigned bit_pos, int imm14); + + // Test bit and branch to label if not zero. + void tbnz(const Register& rt, unsigned bit_pos, Label* label); + + // Test bit and branch to PC offset if not zero. + void tbnz(const Register& rt, unsigned bit_pos, int imm14); + + // Address calculation instructions. + // Calculate a PC-relative address. Unlike for branches the offset in adr is + // unscaled (i.e. the result can be unaligned). + + // Calculate the address of a label. + void adr(const Register& rd, Label* label); + + // Calculate the address of a PC offset. + void adr(const Register& rd, int imm21); + + // Data Processing instructions. + // Add. + void add(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Compare negative. + void cmn(const Register& rn, const Operand& operand); + + // Subtract. + void sub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Compare. + void cmp(const Register& rn, const Operand& operand); + + // Negate. + void neg(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Add with carry bit. + void adc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Subtract with carry bit. + void sbc(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Negate with carry bit. + void ngc(const Register& rd, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Logical instructions. + // Bitwise and (A & B). + void and_(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Bit test and set flags. + void tst(const Register& rn, const Operand& operand); + + // Bit clear (A & ~B). + void bic(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S = LeaveFlags); + + // Bitwise or (A | B). + void orr(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise nor (A | ~B). + void orn(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise eor/xor (A ^ B). + void eor(const Register& rd, const Register& rn, const Operand& operand); + + // Bitwise enor/xnor (A ^ ~B). + void eon(const Register& rd, const Register& rn, const Operand& operand); + + // Logical shift left by variable. + void lslv(const Register& rd, const Register& rn, const Register& rm); + + // Logical shift right by variable. + void lsrv(const Register& rd, const Register& rn, const Register& rm); + + // Arithmetic shift right by variable. + void asrv(const Register& rd, const Register& rn, const Register& rm); + + // Rotate right by variable. + void rorv(const Register& rd, const Register& rn, const Register& rm); + + // Bitfield instructions. + // Bitfield move. + void bfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Signed bitfield move. + void sbfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Unsigned bitfield move. + void ubfm(const Register& rd, + const Register& rn, + unsigned immr, + unsigned imms); + + // Bfm aliases. + // Bitfield insert. + inline void bfi(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Bitfield extract and insert low. + inline void bfxil(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + bfm(rd, rn, lsb, lsb + width - 1); + } + + // Sbfm aliases. + // Arithmetic shift right. + inline void asr(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(shift < rd.size()); + sbfm(rd, rn, shift, rd.size() - 1); + } + + // Signed bitfield insert with zero at right. + inline void sbfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Signed bitfield extract. + inline void sbfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + sbfm(rd, rn, lsb, lsb + width - 1); + } + + // Signed extend byte. + inline void sxtb(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 7); + } + + // Signed extend halfword. + inline void sxth(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 15); + } + + // Signed extend word. + inline void sxtw(const Register& rd, const Register& rn) { + sbfm(rd, rn, 0, 31); + } + + // Ubfm aliases. + // Logical shift left. + inline void lsl(const Register& rd, const Register& rn, unsigned shift) { + unsigned reg_size = rd.size(); + ASSERT(shift < reg_size); + ubfm(rd, rn, (reg_size - shift) % reg_size, reg_size - shift - 1); + } + + // Logical shift right. + inline void lsr(const Register& rd, const Register& rn, unsigned shift) { + ASSERT(shift < rd.size()); + ubfm(rd, rn, shift, rd.size() - 1); + } + + // Unsigned bitfield insert with zero at right. + inline void ubfiz(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, (rd.size() - lsb) & (rd.size() - 1), width - 1); + } + + // Unsigned bitfield extract. + inline void ubfx(const Register& rd, + const Register& rn, + unsigned lsb, + unsigned width) { + ASSERT(width >= 1); + ASSERT(lsb + width <= rn.size()); + ubfm(rd, rn, lsb, lsb + width - 1); + } + + // Unsigned extend byte. + inline void uxtb(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 7); + } + + // Unsigned extend halfword. + inline void uxth(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 15); + } + + // Unsigned extend word. + inline void uxtw(const Register& rd, const Register& rn) { + ubfm(rd, rn, 0, 31); + } + + // Extract. + void extr(const Register& rd, + const Register& rn, + const Register& rm, + unsigned lsb); + + // Conditional select: rd = cond ? rn : rm. + void csel(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select increment: rd = cond ? rn : rm + 1. + void csinc(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select inversion: rd = cond ? rn : ~rm. + void csinv(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional select negation: rd = cond ? rn : -rm. + void csneg(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond); + + // Conditional set: rd = cond ? 1 : 0. + void cset(const Register& rd, Condition cond); + + // Conditional set mask: rd = cond ? -1 : 0. + void csetm(const Register& rd, Condition cond); + + // Conditional increment: rd = cond ? rn + 1 : rn. + void cinc(const Register& rd, const Register& rn, Condition cond); + + // Conditional invert: rd = cond ? ~rn : rn. + void cinv(const Register& rd, const Register& rn, Condition cond); + + // Conditional negate: rd = cond ? -rn : rn. + void cneg(const Register& rd, const Register& rn, Condition cond); + + // Rotate right. + inline void ror(const Register& rd, const Register& rs, unsigned shift) { + extr(rd, rs, rs, shift); + } + + // Conditional comparison. + // Conditional compare negative. + void ccmn(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Conditional compare. + void ccmp(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond); + + // Multiply. + void mul(const Register& rd, const Register& rn, const Register& rm); + + // Negated multiply. + void mneg(const Register& rd, const Register& rn, const Register& rm); + + // Signed long multiply: 32 x 32 -> 64-bit. + void smull(const Register& rd, const Register& rn, const Register& rm); + + // Signed multiply high: 64 x 64 -> 64-bit <127:64>. + void smulh(const Register& xd, const Register& xn, const Register& xm); + + // Multiply and accumulate. + void madd(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Multiply and subtract. + void msub(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void smaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and accumulate: 32 x 32 + 64 -> 64-bit. + void umaddl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void smsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Unsigned long multiply and subtract: 64 - (32 x 32) -> 64-bit. + void umsubl(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra); + + // Signed integer divide. + void sdiv(const Register& rd, const Register& rn, const Register& rm); + + // Unsigned integer divide. + void udiv(const Register& rd, const Register& rn, const Register& rm); + + // Bit reverse. + void rbit(const Register& rd, const Register& rn); + + // Reverse bytes in 16-bit half words. + void rev16(const Register& rd, const Register& rn); + + // Reverse bytes in 32-bit words. + void rev32(const Register& rd, const Register& rn); + + // Reverse bytes. + void rev(const Register& rd, const Register& rn); + + // Count leading zeroes. + void clz(const Register& rd, const Register& rn); + + // Count leading sign bits. + void cls(const Register& rd, const Register& rn); + + // Memory instructions. + // Load integer or FP register. + void ldr(const CPURegister& rt, const MemOperand& src); + + // Store integer or FP register. + void str(const CPURegister& rt, const MemOperand& dst); + + // Load word with sign extension. + void ldrsw(const Register& rt, const MemOperand& src); + + // Load byte. + void ldrb(const Register& rt, const MemOperand& src); + + // Store byte. + void strb(const Register& rt, const MemOperand& dst); + + // Load byte with sign extension. + void ldrsb(const Register& rt, const MemOperand& src); + + // Load half-word. + void ldrh(const Register& rt, const MemOperand& src); + + // Store half-word. + void strh(const Register& rt, const MemOperand& dst); + + // Load half-word with sign extension. + void ldrsh(const Register& rt, const MemOperand& src); + + // Load integer or FP register pair. + void ldp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair. + void stp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load word pair with sign extension. + void ldpsw(const Register& rt, const Register& rt2, const MemOperand& src); + + // Load integer or FP register pair, non-temporal. + void ldnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& src); + + // Store integer or FP register pair, non-temporal. + void stnp(const CPURegister& rt, const CPURegister& rt2, + const MemOperand& dst); + + // Load literal to register. + void ldr(const Register& rt, uint64_t imm); + + // Load literal to FP register. + void ldr(const FPRegister& ft, double imm); + + // Move instructions. The default shift of -1 indicates that the move + // instruction will calculate an appropriate 16-bit immediate and left shift + // that is equal to the 64-bit immediate argument. If an explicit left shift + // is specified (0, 16, 32 or 48), the immediate must be a 16-bit value. + // + // For movk, an explicit shift can be used to indicate which half word should + // be overwritten, eg. movk(x0, 0, 0) will overwrite the least-significant + // half word with zero, whereas movk(x0, 0, 48) will overwrite the + // most-significant. + + // Move immediate and keep. + void movk(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVK); + } + + // Move inverted immediate. + void movn(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVN); + } + + // Move immediate. + void movz(const Register& rd, uint64_t imm, int shift = -1) { + MoveWide(rd, imm, shift, MOVZ); + } + + // Misc instructions. + // Monitor debug-mode breakpoint. + void brk(int code); + + // Halting debug-mode breakpoint. + void hlt(int code); + + // Move register to register. + void mov(const Register& rd, const Register& rn); + + // Move inverted operand to register. + void mvn(const Register& rd, const Operand& operand); + + // System instructions. + // Move to register from system register. + void mrs(const Register& rt, SystemRegister sysreg); + + // Move from register to system register. + void msr(SystemRegister sysreg, const Register& rt); + + // System hint. + void hint(SystemHint code); + + // Alias for system instructions. + // No-op. + void nop() { + hint(NOP); + } + + // FP instructions. + // Move immediate to FP register. + void fmov(FPRegister fd, double imm); + + // Move FP register to register. + void fmov(Register rd, FPRegister fn); + + // Move register to FP register. + void fmov(FPRegister fd, Register rn); + + // Move FP register to FP register. + void fmov(FPRegister fd, FPRegister fn); + + // FP add. + void fadd(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP subtract. + void fsub(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP multiply. + void fmul(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP multiply and subtract. + void fmsub(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa); + + // FP divide. + void fdiv(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP maximum. + void fmax(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP minimum. + void fmin(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); + + // FP absolute. + void fabs(const FPRegister& fd, const FPRegister& fn); + + // FP negate. + void fneg(const FPRegister& fd, const FPRegister& fn); + + // FP square root. + void fsqrt(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (nearest with ties to even). + void frintn(const FPRegister& fd, const FPRegister& fn); + + // FP round to integer (towards zero). + void frintz(const FPRegister& fd, const FPRegister& fn); + + // FP compare registers. + void fcmp(const FPRegister& fn, const FPRegister& fm); + + // FP compare immediate. + void fcmp(const FPRegister& fn, double value); + + // FP conditional compare. + void fccmp(const FPRegister& fn, + const FPRegister& fm, + StatusFlags nzcv, + Condition cond); + + // FP conditional select. + void fcsel(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + Condition cond); + + // Common FP Convert function. + void FPConvertToInt(const Register& rd, + const FPRegister& fn, + FPIntegerConvertOp op); + + // FP convert between single and double precision. + void fcvt(const FPRegister& fd, const FPRegister& fn); + + // Convert FP to unsigned integer (round towards -infinity). + void fcvtmu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (round towards -infinity). + void fcvtms(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (nearest with ties to even). + void fcvtnu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (nearest with ties to even). + void fcvtns(const Register& rd, const FPRegister& fn); + + // Convert FP to unsigned integer (round towards zero). + void fcvtzu(const Register& rd, const FPRegister& fn); + + // Convert FP to signed integer (round towards zero). + void fcvtzs(const Register& rd, const FPRegister& fn); + + // Convert signed integer or fixed point to FP. + void scvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); + + // Convert unsigned integer or fixed point to FP. + void ucvtf(const FPRegister& fd, const Register& rn, unsigned fbits = 0); + + // Emit generic instructions. + // Emit raw instructions into the instruction stream. + inline void dci(Instr raw_inst) { Emit(raw_inst); } + + // Emit 32 bits of data into the instruction stream. + inline void dc32(uint32_t data) { EmitData(&data, sizeof(data)); } + + // Emit 64 bits of data into the instruction stream. + inline void dc64(uint64_t data) { EmitData(&data, sizeof(data)); } + + // Copy a string into the instruction stream, including the terminating NULL + // character. The instruction pointer (pc_) is then aligned correctly for + // subsequent instructions. + void EmitStringData(const char * string) { + ASSERT(string != NULL); + + size_t len = strlen(string) + 1; + EmitData(string, len); + + // Pad with NULL characters until pc_ is aligned. + const char pad[] = {'\0', '\0', '\0', '\0'}; + ASSERT(sizeof(pad) == kInstructionSize); + Instruction* next_pc = AlignUp(pc_, kInstructionSize); + EmitData(&pad, next_pc - pc_); + } + + // Code generation helpers. + + // Register encoding. + static Instr Rd(CPURegister rd) { + ASSERT(rd.code() != kSPRegInternalCode); + return rd.code() << Rd_offset; + } + + static Instr Rn(CPURegister rn) { + ASSERT(rn.code() != kSPRegInternalCode); + return rn.code() << Rn_offset; + } + + static Instr Rm(CPURegister rm) { + ASSERT(rm.code() != kSPRegInternalCode); + return rm.code() << Rm_offset; + } + + static Instr Ra(CPURegister ra) { + ASSERT(ra.code() != kSPRegInternalCode); + return ra.code() << Ra_offset; + } + + static Instr Rt(CPURegister rt) { + ASSERT(rt.code() != kSPRegInternalCode); + return rt.code() << Rt_offset; + } + + static Instr Rt2(CPURegister rt2) { + ASSERT(rt2.code() != kSPRegInternalCode); + return rt2.code() << Rt2_offset; + } + + // These encoding functions allow the stack pointer to be encoded, and + // disallow the zero register. + static Instr RdSP(Register rd) { + ASSERT(!rd.IsZero()); + return (rd.code() & kRegCodeMask) << Rd_offset; + } + + static Instr RnSP(Register rn) { + ASSERT(!rn.IsZero()); + return (rn.code() & kRegCodeMask) << Rn_offset; + } + + // Flags encoding. + static Instr Flags(FlagsUpdate S) { + if (S == SetFlags) { + return 1 << FlagsUpdate_offset; + } else if (S == LeaveFlags) { + return 0 << FlagsUpdate_offset; + } + UNREACHABLE(); + return 0; + } + + static Instr Cond(Condition cond) { + return cond << Condition_offset; + } + + // PC-relative address encoding. + static Instr ImmPCRelAddress(int imm21) { + ASSERT(is_int21(imm21)); + Instr imm = static_cast<Instr>(truncate_to_int21(imm21)); + Instr immhi = (imm >> ImmPCRelLo_width) << ImmPCRelHi_offset; + Instr immlo = imm << ImmPCRelLo_offset; + return (immhi & ImmPCRelHi_mask) | (immlo & ImmPCRelLo_mask); + } + + // Branch encoding. + static Instr ImmUncondBranch(int imm26) { + ASSERT(is_int26(imm26)); + return truncate_to_int26(imm26) << ImmUncondBranch_offset; + } + + static Instr ImmCondBranch(int imm19) { + ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCondBranch_offset; + } + + static Instr ImmCmpBranch(int imm19) { + ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmCmpBranch_offset; + } + + static Instr ImmTestBranch(int imm14) { + ASSERT(is_int14(imm14)); + return truncate_to_int14(imm14) << ImmTestBranch_offset; + } + + static Instr ImmTestBranchBit(unsigned bit_pos) { + ASSERT(is_uint6(bit_pos)); + // Subtract five from the shift offset, as we need bit 5 from bit_pos. + unsigned b5 = bit_pos << (ImmTestBranchBit5_offset - 5); + unsigned b40 = bit_pos << ImmTestBranchBit40_offset; + b5 &= ImmTestBranchBit5_mask; + b40 &= ImmTestBranchBit40_mask; + return b5 | b40; + } + + // Data Processing encoding. + static Instr SF(Register rd) { + return rd.Is64Bits() ? SixtyFourBits : ThirtyTwoBits; + } + + static Instr ImmAddSub(int64_t imm) { + ASSERT(IsImmAddSub(imm)); + if (is_uint12(imm)) { // No shift required. + return imm << ImmAddSub_offset; + } else { + return ((imm >> 12) << ImmAddSub_offset) | (1 << ShiftAddSub_offset); + } + } + + static inline Instr ImmS(unsigned imms, unsigned reg_size) { + ASSERT(((reg_size == kXRegSize) && is_uint6(imms)) || + ((reg_size == kWRegSize) && is_uint5(imms))); + USE(reg_size); + return imms << ImmS_offset; + } + + static inline Instr ImmR(unsigned immr, unsigned reg_size) { + ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + ASSERT(is_uint6(immr)); + return immr << ImmR_offset; + } + + static inline Instr ImmSetBits(unsigned imms, unsigned reg_size) { + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + ASSERT(is_uint6(imms)); + ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); + USE(reg_size); + return imms << ImmSetBits_offset; + } + + static inline Instr ImmRotate(unsigned immr, unsigned reg_size) { + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || + ((reg_size == kWRegSize) && is_uint5(immr))); + USE(reg_size); + return immr << ImmRotate_offset; + } + + static inline Instr ImmLLiteral(int imm19) { + ASSERT(is_int19(imm19)); + return truncate_to_int19(imm19) << ImmLLiteral_offset; + } + + static inline Instr BitN(unsigned bitn, unsigned reg_size) { + ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); + ASSERT((reg_size == kXRegSize) || (bitn == 0)); + USE(reg_size); + return bitn << BitN_offset; + } + + static Instr ShiftDP(Shift shift) { + ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); + return shift << ShiftDP_offset; + } + + static Instr ImmDPShift(unsigned amount) { + ASSERT(is_uint6(amount)); + return amount << ImmDPShift_offset; + } + + static Instr ExtendMode(Extend extend) { + return extend << ExtendMode_offset; + } + + static Instr ImmExtendShift(unsigned left_shift) { + ASSERT(left_shift <= 4); + return left_shift << ImmExtendShift_offset; + } + + static Instr ImmCondCmp(unsigned imm) { + ASSERT(is_uint5(imm)); + return imm << ImmCondCmp_offset; + } + + static Instr Nzcv(StatusFlags nzcv) { + return ((nzcv >> Flags_offset) & 0xf) << Nzcv_offset; + } + + // MemOperand offset encoding. + static Instr ImmLSUnsigned(int imm12) { + ASSERT(is_uint12(imm12)); + return imm12 << ImmLSUnsigned_offset; + } + + static Instr ImmLS(int imm9) { + ASSERT(is_int9(imm9)); + return truncate_to_int9(imm9) << ImmLS_offset; + } + + static Instr ImmLSPair(int imm7, LSDataSize size) { + ASSERT(((imm7 >> size) << size) == imm7); + int scaled_imm7 = imm7 >> size; + ASSERT(is_int7(scaled_imm7)); + return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; + } + + static Instr ImmShiftLS(unsigned shift_amount) { + ASSERT(is_uint1(shift_amount)); + return shift_amount << ImmShiftLS_offset; + } + + static Instr ImmException(int imm16) { + ASSERT(is_uint16(imm16)); + return imm16 << ImmException_offset; + } + + static Instr ImmSystemRegister(int imm15) { + ASSERT(is_uint15(imm15)); + return imm15 << ImmSystemRegister_offset; + } + + static Instr ImmHint(int imm7) { + ASSERT(is_uint7(imm7)); + return imm7 << ImmHint_offset; + } + + static LSDataSize CalcLSDataSize(LoadStoreOp op) { + ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8)); + return static_cast<LSDataSize>(op >> SizeLS_offset); + } + + // Move immediates encoding. + static Instr ImmMoveWide(uint64_t imm) { + ASSERT(is_uint16(imm)); + return imm << ImmMoveWide_offset; + } + + static Instr ShiftMoveWide(int64_t shift) { + ASSERT(is_uint2(shift)); + return shift << ShiftMoveWide_offset; + } + + // FP Immediates. + static Instr ImmFP32(float imm); + static Instr ImmFP64(double imm); + + // FP register type. + static Instr FPType(FPRegister fd) { + return fd.Is64Bits() ? FP64 : FP32; + } + + static Instr FPScale(unsigned scale) { + ASSERT(is_uint6(scale)); + return scale << FPScale_offset; + } + + // Size of the code generated in bytes + uint64_t SizeOfCodeGenerated() const { + ASSERT((pc_ >= buffer_) && (pc_ < (buffer_ + buffer_size_))); + return pc_ - buffer_; + } + + // Size of the code generated since label to the current position. + uint64_t SizeOfCodeGeneratedSince(Label* label) const { + ASSERT(label->IsBound()); + ASSERT((pc_ >= label->target()) && (pc_ < (buffer_ + buffer_size_))); + return pc_ - label->target(); + } + + + inline void BlockLiteralPool() { + literal_pool_monitor_++; + } + + inline void ReleaseLiteralPool() { + if (--literal_pool_monitor_ == 0) { + // Has the literal pool been blocked for too long? + ASSERT(literals_.empty() || + (pc_ < (literals_.back()->pc_ + kMaxLoadLiteralRange))); + } + } + + inline bool IsLiteralPoolBlocked() { + return literal_pool_monitor_ != 0; + } + + void CheckLiteralPool(LiteralPoolEmitOption option = JumpRequired); + void EmitLiteralPool(LiteralPoolEmitOption option = NoJumpRequired); + size_t LiteralPoolSize(); + + protected: + inline const Register& AppropriateZeroRegFor(const CPURegister& reg) const { + return reg.Is64Bits() ? xzr : wzr; + } + + + void LoadStore(const CPURegister& rt, + const MemOperand& addr, + LoadStoreOp op); + static bool IsImmLSUnscaled(ptrdiff_t offset); + static bool IsImmLSScaled(ptrdiff_t offset, LSDataSize size); + + void Logical(const Register& rd, + const Register& rn, + const Operand& operand, + LogicalOp op); + void LogicalImmediate(const Register& rd, + const Register& rn, + unsigned n, + unsigned imm_s, + unsigned imm_r, + LogicalOp op); + static bool IsImmLogical(uint64_t value, + unsigned width, + unsigned* n, + unsigned* imm_s, + unsigned* imm_r); + + void ConditionalCompare(const Register& rn, + const Operand& operand, + StatusFlags nzcv, + Condition cond, + ConditionalCompareOp op); + static bool IsImmConditionalCompare(int64_t immediate); + + void AddSubWithCarry(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubWithCarryOp op); + + // Functions for emulating operands not directly supported by the instruction + // set. + void EmitShift(const Register& rd, + const Register& rn, + Shift shift, + unsigned amount); + void EmitExtendShift(const Register& rd, + const Register& rn, + Extend extend, + unsigned left_shift); + + void AddSub(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + AddSubOp op); + static bool IsImmAddSub(int64_t immediate); + + // Find an appropriate LoadStoreOp or LoadStorePairOp for the specified + // registers. Only simple loads are supported; sign- and zero-extension (such + // as in LDPSW_x or LDRB_w) are not supported. + static LoadStoreOp LoadOpFor(const CPURegister& rt); + static LoadStorePairOp LoadPairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStoreOp StoreOpFor(const CPURegister& rt); + static LoadStorePairOp StorePairOpFor(const CPURegister& rt, + const CPURegister& rt2); + static LoadStorePairNonTemporalOp LoadPairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + static LoadStorePairNonTemporalOp StorePairNonTemporalOpFor( + const CPURegister& rt, const CPURegister& rt2); + + + private: + // Instruction helpers. + void MoveWide(const Register& rd, + uint64_t imm, + int shift, + MoveWideImmediateOp mov_op); + void DataProcShiftedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void DataProcExtendedRegister(const Register& rd, + const Register& rn, + const Operand& operand, + FlagsUpdate S, + Instr op); + void LoadStorePair(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairOp op); + void LoadStorePairNonTemporal(const CPURegister& rt, + const CPURegister& rt2, + const MemOperand& addr, + LoadStorePairNonTemporalOp op); + void LoadLiteral(const CPURegister& rt, uint64_t imm, LoadLiteralOp op); + void ConditionalSelect(const Register& rd, + const Register& rn, + const Register& rm, + Condition cond, + ConditionalSelectOp op); + void DataProcessing1Source(const Register& rd, + const Register& rn, + DataProcessing1SourceOp op); + void DataProcessing3Source(const Register& rd, + const Register& rn, + const Register& rm, + const Register& ra, + DataProcessing3SourceOp op); + void FPDataProcessing1Source(const FPRegister& fd, + const FPRegister& fn, + FPDataProcessing1SourceOp op); + void FPDataProcessing2Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + FPDataProcessing2SourceOp op); + void FPDataProcessing3Source(const FPRegister& fd, + const FPRegister& fn, + const FPRegister& fm, + const FPRegister& fa, + FPDataProcessing3SourceOp op); + + // Encoding helpers. + static bool IsImmFP32(float imm); + static bool IsImmFP64(double imm); + + void RecordLiteral(int64_t imm, unsigned size); + + // Emit the instruction at pc_. + void Emit(Instr instruction) { + ASSERT(sizeof(*pc_) == 1); + ASSERT(sizeof(instruction) == kInstructionSize); + ASSERT((pc_ + sizeof(instruction)) <= (buffer_ + buffer_size_)); + +#ifdef DEBUG + finalized_ = false; +#endif + + memcpy(pc_, &instruction, sizeof(instruction)); + pc_ += sizeof(instruction); + CheckBufferSpace(); + } + + // Emit data inline in the instruction stream. + void EmitData(void const * data, unsigned size) { + ASSERT(sizeof(*pc_) == 1); + ASSERT((pc_ + size) <= (buffer_ + buffer_size_)); + +#ifdef DEBUG + finalized_ = false; +#endif + + // TODO: Record this 'instruction' as data, so that it can be disassembled + // correctly. + memcpy(pc_, data, size); + pc_ += size; + CheckBufferSpace(); + } + + inline void CheckBufferSpace() { + ASSERT(pc_ < (buffer_ + buffer_size_)); + if (pc_ > next_literal_pool_check_) { + CheckLiteralPool(); + } + } + + // The buffer into which code and relocation info are generated. + Instruction* buffer_; + // Buffer size, in bytes. + unsigned buffer_size_; + Instruction* pc_; + std::list<Literal*> literals_; + Instruction* next_literal_pool_check_; + unsigned literal_pool_monitor_; + + friend class BlockLiteralPoolScope; + +#ifdef DEBUG + bool finalized_; +#endif +}; + +class BlockLiteralPoolScope { + public: + explicit BlockLiteralPoolScope(Assembler* assm) : assm_(assm) { + assm_->BlockLiteralPool(); + } + + ~BlockLiteralPoolScope() { + assm_->ReleaseLiteralPool(); + } + + private: + Assembler* assm_; +}; +} // namespace vixl + +#endif // VIXL_A64_ASSEMBLER_A64_H_ |