| // 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_ |