|  | // 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) { | 
|  | VIXL_ASSERT(!IsValid()); | 
|  | VIXL_ASSERT(IsNone()); | 
|  | } | 
|  |  | 
|  | CPURegister(unsigned code, unsigned size, RegisterType type) | 
|  | : code_(code), size_(size), type_(type) { | 
|  | VIXL_ASSERT(IsValidOrNone()); | 
|  | } | 
|  |  | 
|  | unsigned code() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return code_; | 
|  | } | 
|  |  | 
|  | RegisterType type() const { | 
|  | VIXL_ASSERT(IsValidOrNone()); | 
|  | return type_; | 
|  | } | 
|  |  | 
|  | RegList Bit() const { | 
|  | VIXL_ASSERT(code_ < (sizeof(RegList) * 8)); | 
|  | return IsValid() ? (static_cast<RegList>(1) << code_) : 0; | 
|  | } | 
|  |  | 
|  | unsigned size() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return size_; | 
|  | } | 
|  |  | 
|  | int SizeInBytes() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | VIXL_ASSERT(size() % 8 == 0); | 
|  | return size_ / 8; | 
|  | } | 
|  |  | 
|  | int SizeInBits() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return size_; | 
|  | } | 
|  |  | 
|  | bool Is32Bits() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return size_ == 32; | 
|  | } | 
|  |  | 
|  | bool Is64Bits() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return size_ == 64; | 
|  | } | 
|  |  | 
|  | bool IsValid() const { | 
|  | if (IsValidRegister() || IsValidFPRegister()) { | 
|  | VIXL_ASSERT(!IsNone()); | 
|  | return true; | 
|  | } else { | 
|  | VIXL_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. | 
|  | VIXL_ASSERT((type_ != kNoRegister) || (code_ == 0)); | 
|  | VIXL_ASSERT((type_ != kNoRegister) || (size_ == 0)); | 
|  |  | 
|  | return type_ == kNoRegister; | 
|  | } | 
|  |  | 
|  | bool Aliases(const CPURegister& other) const { | 
|  | VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); | 
|  | return (code_ == other.code_) && (type_ == other.type_); | 
|  | } | 
|  |  | 
|  | bool Is(const CPURegister& other) const { | 
|  | VIXL_ASSERT(IsValidOrNone() && other.IsValidOrNone()); | 
|  | return Aliases(other) && (size_ == other.size_); | 
|  | } | 
|  |  | 
|  | inline bool IsZero() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return IsRegister() && (code_ == kZeroRegCode); | 
|  | } | 
|  |  | 
|  | inline bool IsSP() const { | 
|  | VIXL_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()) { | 
|  | VIXL_ASSERT(IsValidRegister()); | 
|  | } | 
|  | explicit Register(unsigned code, unsigned size) | 
|  | : CPURegister(code, size, kRegister) {} | 
|  |  | 
|  | bool IsValid() const { | 
|  | VIXL_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()) { | 
|  | VIXL_ASSERT(IsValidFPRegister()); | 
|  | } | 
|  | inline FPRegister(unsigned code, unsigned size) | 
|  | : CPURegister(code, size, kFPRegister) {} | 
|  |  | 
|  | bool IsValid() const { | 
|  | VIXL_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()) { | 
|  | VIXL_ASSERT(AreSameSizeAndType(reg1, reg2, reg3, reg4)); | 
|  | VIXL_ASSERT(IsValid()); | 
|  | } | 
|  |  | 
|  | inline CPURegList(CPURegister::RegisterType type, unsigned size, RegList list) | 
|  | : list_(list), size_(size), type_(type) { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | } | 
|  |  | 
|  | inline CPURegList(CPURegister::RegisterType type, unsigned size, | 
|  | unsigned first_reg, unsigned last_reg) | 
|  | : size_(size), type_(type) { | 
|  | VIXL_ASSERT(((type == CPURegister::kRegister) && | 
|  | (last_reg < kNumberOfRegisters)) || | 
|  | ((type == CPURegister::kFPRegister) && | 
|  | (last_reg < kNumberOfFPRegisters))); | 
|  | VIXL_ASSERT(last_reg >= first_reg); | 
|  | list_ = (UINT64_C(1) << (last_reg + 1)) - 1; | 
|  | list_ &= ~((UINT64_C(1) << first_reg) - 1); | 
|  | VIXL_ASSERT(IsValid()); | 
|  | } | 
|  |  | 
|  | inline CPURegister::RegisterType type() const { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | VIXL_ASSERT(other.type() == type_); | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | VIXL_ASSERT(other.type() == type_); | 
|  | VIXL_ASSERT(other.RegisterSizeInBits() == size_); | 
|  | list_ &= ~other.list(); | 
|  | } | 
|  |  | 
|  | // Variants of Combine and Remove which take a single register. | 
|  | inline void Combine(const CPURegister& other) { | 
|  | VIXL_ASSERT(other.type() == type_); | 
|  | VIXL_ASSERT(other.size() == size_); | 
|  | Combine(other.code()); | 
|  | } | 
|  |  | 
|  | inline void Remove(const CPURegister& other) { | 
|  | VIXL_ASSERT(other.type() == type_); | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); | 
|  | list_ |= (UINT64_C(1) << code); | 
|  | } | 
|  |  | 
|  | inline void Remove(int code) { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | VIXL_ASSERT(CPURegister(code, size_, type_).IsValid()); | 
|  | list_ &= ~(UINT64_C(1) << code); | 
|  | } | 
|  |  | 
|  | inline RegList list() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return list_; | 
|  | } | 
|  |  | 
|  | inline void set_list(RegList new_list) { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | list_ = new_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 { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return list_ == 0; | 
|  | } | 
|  |  | 
|  | inline bool IncludesAliasOf(const CPURegister& other) const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return (type_ == other.type()) && ((other.Bit() & list_) != 0); | 
|  | } | 
|  |  | 
|  | inline bool IncludesAliasOf(int code) const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return ((code & list_) != 0); | 
|  | } | 
|  |  | 
|  | inline int Count() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return CountSetBits(list_, kRegListSizeInBits); | 
|  | } | 
|  |  | 
|  | inline unsigned RegisterSizeInBits() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return size_; | 
|  | } | 
|  |  | 
|  | inline unsigned RegisterSizeInBytes() const { | 
|  | int size_in_bits = RegisterSizeInBits(); | 
|  | VIXL_ASSERT((size_in_bits % 8) == 0); | 
|  | return size_in_bits / 8; | 
|  | } | 
|  |  | 
|  | inline unsigned TotalSizeInBytes() const { | 
|  | VIXL_ASSERT(IsValid()); | 
|  | return RegisterSizeInBytes() * Count(); | 
|  | } | 
|  |  | 
|  | 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; | 
|  | bool IsZero() 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 { | 
|  | VIXL_ASSERT(IsImmediate()); | 
|  | return immediate_; | 
|  | } | 
|  |  | 
|  | Register reg() const { | 
|  | VIXL_ASSERT(IsShiftedRegister() || IsExtendedRegister()); | 
|  | return reg_; | 
|  | } | 
|  |  | 
|  | Shift shift() const { | 
|  | VIXL_ASSERT(IsShiftedRegister()); | 
|  | return shift_; | 
|  | } | 
|  |  | 
|  | Extend extend() const { | 
|  | VIXL_ASSERT(IsExtendedRegister()); | 
|  | return extend_; | 
|  | } | 
|  |  | 
|  | unsigned shift_amount() const { | 
|  | VIXL_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. | 
|  | VIXL_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) { | 
|  | VIXL_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); | 
|  |  | 
|  | // Add and update status flags. | 
|  | void adds(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Compare negative. | 
|  | void cmn(const Register& rn, const Operand& operand); | 
|  |  | 
|  | // Subtract. | 
|  | void sub(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Subtract and update status flags. | 
|  | void subs(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Compare. | 
|  | void cmp(const Register& rn, const Operand& operand); | 
|  |  | 
|  | // Negate. | 
|  | void neg(const Register& rd, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Negate and update status flags. | 
|  | void negs(const Register& rd, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Add with carry bit. | 
|  | void adc(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Add with carry bit and update status flags. | 
|  | void adcs(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Subtract with carry bit. | 
|  | void sbc(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Subtract with carry bit and update status flags. | 
|  | void sbcs(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Negate with carry bit. | 
|  | void ngc(const Register& rd, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Negate with carry bit and update status flags. | 
|  | void ngcs(const Register& rd, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Logical instructions. | 
|  | // Bitwise and (A & B). | 
|  | void and_(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // Bitwise and (A & B) and update status flags. | 
|  | void ands(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // 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); | 
|  |  | 
|  | // Bit clear (A & ~B) and update status flags. | 
|  | void bics(const Register& rd, | 
|  | const Register& rn, | 
|  | const Operand& operand); | 
|  |  | 
|  | // 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) { | 
|  | VIXL_ASSERT(width >= 1); | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(width >= 1); | 
|  | VIXL_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) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(width >= 1); | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(width >= 1); | 
|  | VIXL_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(); | 
|  | VIXL_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) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(width >= 1); | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(width >= 1); | 
|  | VIXL_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 double precision floating point literal to FP register. | 
|  | void ldr(const FPRegister& ft, double imm); | 
|  |  | 
|  | // Load single precision floating point literal to FP register. | 
|  | void ldr(const FPRegister& ft, float 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); | 
|  |  | 
|  | // Data memory barrier. | 
|  | void dmb(BarrierDomain domain, BarrierType type); | 
|  |  | 
|  | // Data synchronization barrier. | 
|  | void dsb(BarrierDomain domain, BarrierType type); | 
|  |  | 
|  | // Instruction synchronization barrier. | 
|  | void isb(); | 
|  |  | 
|  | // Alias for system instructions. | 
|  | // No-op. | 
|  | void nop() { | 
|  | hint(NOP); | 
|  | } | 
|  |  | 
|  | // FP instructions. | 
|  | // Move double precision immediate to FP register. | 
|  | void fmov(const FPRegister& fd, double imm); | 
|  |  | 
|  | // Move single precision immediate to FP register. | 
|  | void fmov(const FPRegister& fd, float imm); | 
|  |  | 
|  | // Move FP register to register. | 
|  | void fmov(const Register& rd, const FPRegister& fn); | 
|  |  | 
|  | // Move register to FP register. | 
|  | void fmov(const FPRegister& fd, const Register& rn); | 
|  |  | 
|  | // Move FP register to FP register. | 
|  | void fmov(const FPRegister& fd, const 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 fused multiply and add. | 
|  | void fmadd(const FPRegister& fd, | 
|  | const FPRegister& fn, | 
|  | const FPRegister& fm, | 
|  | const FPRegister& fa); | 
|  |  | 
|  | // FP fused multiply and subtract. | 
|  | void fmsub(const FPRegister& fd, | 
|  | const FPRegister& fn, | 
|  | const FPRegister& fm, | 
|  | const FPRegister& fa); | 
|  |  | 
|  | // FP fused multiply, add and negate. | 
|  | void fnmadd(const FPRegister& fd, | 
|  | const FPRegister& fn, | 
|  | const FPRegister& fm, | 
|  | const FPRegister& fa); | 
|  |  | 
|  | // FP fused multiply, subtract and negate. | 
|  | void fnmsub(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 maximum number. | 
|  | void fmaxnm(const FPRegister& fd, const FPRegister& fn, const FPRegister& fm); | 
|  |  | 
|  | // FP minimum number. | 
|  | void fminnm(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 away). | 
|  | void frinta(const FPRegister& fd, const FPRegister& fn); | 
|  |  | 
|  | // FP round to integer (toward minus infinity). | 
|  | void frintm(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 signed integer (nearest with ties to away). | 
|  | void fcvtas(const Register& rd, const FPRegister& fn); | 
|  |  | 
|  | // Convert FP to unsigned integer (nearest with ties to away). | 
|  | void fcvtau(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 (round towards -infinity). | 
|  | void fcvtmu(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 (nearest with ties to even). | 
|  | void fcvtnu(const Register& rd, const FPRegister& fn); | 
|  |  | 
|  | // Convert FP to signed integer (round towards zero). | 
|  | void fcvtzs(const Register& rd, const FPRegister& fn); | 
|  |  | 
|  | // Convert FP to unsigned integer (round towards zero). | 
|  | void fcvtzu(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) { | 
|  | VIXL_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'}; | 
|  | VIXL_STATIC_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) { | 
|  | VIXL_ASSERT(rd.code() != kSPRegInternalCode); | 
|  | return rd.code() << Rd_offset; | 
|  | } | 
|  |  | 
|  | static Instr Rn(CPURegister rn) { | 
|  | VIXL_ASSERT(rn.code() != kSPRegInternalCode); | 
|  | return rn.code() << Rn_offset; | 
|  | } | 
|  |  | 
|  | static Instr Rm(CPURegister rm) { | 
|  | VIXL_ASSERT(rm.code() != kSPRegInternalCode); | 
|  | return rm.code() << Rm_offset; | 
|  | } | 
|  |  | 
|  | static Instr Ra(CPURegister ra) { | 
|  | VIXL_ASSERT(ra.code() != kSPRegInternalCode); | 
|  | return ra.code() << Ra_offset; | 
|  | } | 
|  |  | 
|  | static Instr Rt(CPURegister rt) { | 
|  | VIXL_ASSERT(rt.code() != kSPRegInternalCode); | 
|  | return rt.code() << Rt_offset; | 
|  | } | 
|  |  | 
|  | static Instr Rt2(CPURegister rt2) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(!rd.IsZero()); | 
|  | return (rd.code() & kRegCodeMask) << Rd_offset; | 
|  | } | 
|  |  | 
|  | static Instr RnSP(Register rn) { | 
|  | VIXL_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; | 
|  | } | 
|  | VIXL_UNREACHABLE(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static Instr Cond(Condition cond) { | 
|  | return cond << Condition_offset; | 
|  | } | 
|  |  | 
|  | // PC-relative address encoding. | 
|  | static Instr ImmPCRelAddress(int imm21) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(is_int26(imm26)); | 
|  | return truncate_to_int26(imm26) << ImmUncondBranch_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmCondBranch(int imm19) { | 
|  | VIXL_ASSERT(is_int19(imm19)); | 
|  | return truncate_to_int19(imm19) << ImmCondBranch_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmCmpBranch(int imm19) { | 
|  | VIXL_ASSERT(is_int19(imm19)); | 
|  | return truncate_to_int19(imm19) << ImmCmpBranch_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmTestBranch(int imm14) { | 
|  | VIXL_ASSERT(is_int14(imm14)); | 
|  | return truncate_to_int14(imm14) << ImmTestBranch_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmTestBranchBit(unsigned bit_pos) { | 
|  | VIXL_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) { | 
|  | VIXL_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) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(((reg_size == kXRegSize) && is_uint6(immr)) || | 
|  | ((reg_size == kWRegSize) && is_uint5(immr))); | 
|  | USE(reg_size); | 
|  | VIXL_ASSERT(is_uint6(immr)); | 
|  | return immr << ImmR_offset; | 
|  | } | 
|  |  | 
|  | static inline Instr ImmSetBits(unsigned imms, unsigned reg_size) { | 
|  | VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); | 
|  | VIXL_ASSERT(is_uint6(imms)); | 
|  | VIXL_ASSERT((reg_size == kXRegSize) || is_uint6(imms + 3)); | 
|  | USE(reg_size); | 
|  | return imms << ImmSetBits_offset; | 
|  | } | 
|  |  | 
|  | static inline Instr ImmRotate(unsigned immr, unsigned reg_size) { | 
|  | VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(is_int19(imm19)); | 
|  | return truncate_to_int19(imm19) << ImmLLiteral_offset; | 
|  | } | 
|  |  | 
|  | static inline Instr BitN(unsigned bitn, unsigned reg_size) { | 
|  | VIXL_ASSERT((reg_size == kWRegSize) || (reg_size == kXRegSize)); | 
|  | VIXL_ASSERT((reg_size == kXRegSize) || (bitn == 0)); | 
|  | USE(reg_size); | 
|  | return bitn << BitN_offset; | 
|  | } | 
|  |  | 
|  | static Instr ShiftDP(Shift shift) { | 
|  | VIXL_ASSERT(shift == LSL || shift == LSR || shift == ASR || shift == ROR); | 
|  | return shift << ShiftDP_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmDPShift(unsigned amount) { | 
|  | VIXL_ASSERT(is_uint6(amount)); | 
|  | return amount << ImmDPShift_offset; | 
|  | } | 
|  |  | 
|  | static Instr ExtendMode(Extend extend) { | 
|  | return extend << ExtendMode_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmExtendShift(unsigned left_shift) { | 
|  | VIXL_ASSERT(left_shift <= 4); | 
|  | return left_shift << ImmExtendShift_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmCondCmp(unsigned imm) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(is_uint12(imm12)); | 
|  | return imm12 << ImmLSUnsigned_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmLS(int imm9) { | 
|  | VIXL_ASSERT(is_int9(imm9)); | 
|  | return truncate_to_int9(imm9) << ImmLS_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmLSPair(int imm7, LSDataSize size) { | 
|  | VIXL_ASSERT(((imm7 >> size) << size) == imm7); | 
|  | int scaled_imm7 = imm7 >> size; | 
|  | VIXL_ASSERT(is_int7(scaled_imm7)); | 
|  | return truncate_to_int7(scaled_imm7) << ImmLSPair_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmShiftLS(unsigned shift_amount) { | 
|  | VIXL_ASSERT(is_uint1(shift_amount)); | 
|  | return shift_amount << ImmShiftLS_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmException(int imm16) { | 
|  | VIXL_ASSERT(is_uint16(imm16)); | 
|  | return imm16 << ImmException_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmSystemRegister(int imm15) { | 
|  | VIXL_ASSERT(is_uint15(imm15)); | 
|  | return imm15 << ImmSystemRegister_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmHint(int imm7) { | 
|  | VIXL_ASSERT(is_uint7(imm7)); | 
|  | return imm7 << ImmHint_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmBarrierDomain(int imm2) { | 
|  | VIXL_ASSERT(is_uint2(imm2)); | 
|  | return imm2 << ImmBarrierDomain_offset; | 
|  | } | 
|  |  | 
|  | static Instr ImmBarrierType(int imm2) { | 
|  | VIXL_ASSERT(is_uint2(imm2)); | 
|  | return imm2 << ImmBarrierType_offset; | 
|  | } | 
|  |  | 
|  | static LSDataSize CalcLSDataSize(LoadStoreOp op) { | 
|  | VIXL_ASSERT((SizeLS_offset + SizeLS_width) == (kInstructionSize * 8)); | 
|  | return static_cast<LSDataSize>(op >> SizeLS_offset); | 
|  | } | 
|  |  | 
|  | // Move immediates encoding. | 
|  | static Instr ImmMoveWide(uint64_t imm) { | 
|  | VIXL_ASSERT(is_uint16(imm)); | 
|  | return imm << ImmMoveWide_offset; | 
|  | } | 
|  |  | 
|  | static Instr ShiftMoveWide(int64_t shift) { | 
|  | VIXL_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) { | 
|  | VIXL_ASSERT(is_uint6(scale)); | 
|  | return scale << FPScale_offset; | 
|  | } | 
|  |  | 
|  | // Size of the code generated in bytes | 
|  | uint64_t SizeOfCodeGenerated() const { | 
|  | VIXL_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 { | 
|  | VIXL_ASSERT(label->IsBound()); | 
|  | VIXL_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? | 
|  | VIXL_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); | 
|  |  | 
|  | static bool IsImmFP32(float imm); | 
|  | static bool IsImmFP64(double imm); | 
|  |  | 
|  | // 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); | 
|  |  | 
|  | void RecordLiteral(int64_t imm, unsigned size); | 
|  |  | 
|  | // Emit the instruction at pc_. | 
|  | void Emit(Instr instruction) { | 
|  | VIXL_STATIC_ASSERT(sizeof(*pc_) == 1); | 
|  | VIXL_STATIC_ASSERT(sizeof(instruction) == kInstructionSize); | 
|  | VIXL_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) { | 
|  | VIXL_STATIC_ASSERT(sizeof(*pc_) == 1); | 
|  | VIXL_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() { | 
|  | VIXL_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_ |