// Copyright 2015, VIXL authors // 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_AARCH32_OPERANDS_AARCH32_H_ #define VIXL_AARCH32_OPERANDS_AARCH32_H_ #include "aarch32/instructions-aarch32.h" namespace vixl { namespace aarch32 { // Operand represents generic set of arguments to pass to an instruction. // // Usage: , // // where is the instruction to use (e.g., Mov(), Rsb(), etc.) // is the destination register // is the rest of the arguments to the instruction // // can be one of: // // # - an unsigned 32-bit immediate value // , <#amount> - immediate shifted register // , - register shifted register // class Operand { public: // { # } // where is uint32_t. // This is allowed to be an implicit constructor because Operand is // a wrapper class that doesn't normally perform any type conversion. Operand(uint32_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoReg), shift_(LSL), amount_(0), rs_(NoReg) {} Operand(int32_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoReg), shift_(LSL), amount_(0), rs_(NoReg) {} // rm // where rm is the base register // This is allowed to be an implicit constructor because Operand is // a wrapper class that doesn't normally perform any type conversion. Operand(Register rm) // NOLINT(runtime/explicit) : imm_(0), rm_(rm), shift_(LSL), amount_(0), rs_(NoReg) { VIXL_ASSERT(rm_.IsValid()); } // rm, // where rm is the base register, and // is RRX Operand(Register rm, Shift shift) : imm_(0), rm_(rm), shift_(shift), amount_(0), rs_(NoReg) { VIXL_ASSERT(rm_.IsValid()); VIXL_ASSERT(shift_.IsRRX()); } // rm, # // where rm is the base register, and // is one of {LSL, LSR, ASR, ROR}, and // is uint6_t. Operand(Register rm, Shift shift, uint32_t amount) : imm_(0), rm_(rm), shift_(shift), amount_(amount), rs_(NoReg) { VIXL_ASSERT(rm_.IsValid()); VIXL_ASSERT(!shift_.IsRRX()); #ifdef VIXL_DEBUG switch (shift_.GetType()) { case LSL: VIXL_ASSERT(amount_ <= 31); break; case ROR: VIXL_ASSERT(amount_ <= 31); break; case LSR: case ASR: VIXL_ASSERT(amount_ <= 32); break; case RRX: default: VIXL_UNREACHABLE(); break; } #endif } // rm, rs // where rm is the base register, and // is one of {LSL, LSR, ASR, ROR}, and // rs is the shifted register Operand(Register rm, Shift shift, Register rs) : imm_(0), rm_(rm), shift_(shift), amount_(0), rs_(rs) { VIXL_ASSERT(rm_.IsValid() && rs_.IsValid()); VIXL_ASSERT(!shift_.IsRRX()); } // Factory methods creating operands from any integral or pointer type. The // source must fit into 32 bits. template static Operand From(T immediate) { #if __cplusplus >= 201103L VIXL_STATIC_ASSERT_MESSAGE(std::is_integral::value, "An integral type is required to build an " "immediate operand."); #endif // Allow both a signed or unsigned 32 bit integer to be passed, but store it // as a uint32_t. The signedness information will be lost. We have to add a // static_cast to make sure the compiler does not complain about implicit 64 // to 32 narrowing. It's perfectly acceptable for the user to pass a 64-bit // value, as long as it can be encoded in 32 bits. VIXL_ASSERT(IsInt32(immediate) || IsUint32(immediate)); return Operand(static_cast(immediate)); } template static Operand From(T* address) { uintptr_t address_as_integral = reinterpret_cast(address); VIXL_ASSERT(IsUint32(address_as_integral)); return Operand(static_cast(address_as_integral)); } bool IsImmediate() const { return !rm_.IsValid(); } bool IsPlainRegister() const { return rm_.IsValid() && !shift_.IsRRX() && !rs_.IsValid() && (amount_ == 0); } bool IsImmediateShiftedRegister() const { return rm_.IsValid() && !rs_.IsValid(); } bool IsRegisterShiftedRegister() const { return rm_.IsValid() && rs_.IsValid(); } uint32_t GetImmediate() const { VIXL_ASSERT(IsImmediate()); return imm_; } int32_t GetSignedImmediate() const { VIXL_ASSERT(IsImmediate()); int32_t result; memcpy(&result, &imm_, sizeof(result)); return result; } Register GetBaseRegister() const { VIXL_ASSERT(IsImmediateShiftedRegister() || IsRegisterShiftedRegister()); return rm_; } Shift GetShift() const { VIXL_ASSERT(IsImmediateShiftedRegister() || IsRegisterShiftedRegister()); return shift_; } uint32_t GetShiftAmount() const { VIXL_ASSERT(IsImmediateShiftedRegister()); return amount_; } Register GetShiftRegister() const { VIXL_ASSERT(IsRegisterShiftedRegister()); return rs_; } uint32_t GetTypeEncodingValue() const { return shift_.IsRRX() ? kRRXEncodedValue : shift_.GetValue(); } private: // Forbid implicitely creating operands around types that cannot be encoded // into a uint32_t without loss. #if __cplusplus >= 201103L Operand(int64_t) = delete; // NOLINT(runtime/explicit) Operand(uint64_t) = delete; // NOLINT(runtime/explicit) Operand(float) = delete; // NOLINT(runtime/explicit) Operand(double) = delete; // NOLINT(runtime/explicit) #else VIXL_NO_RETURN_IN_DEBUG_MODE Operand(int64_t) { // NOLINT(runtime/explicit) VIXL_UNREACHABLE(); } VIXL_NO_RETURN_IN_DEBUG_MODE Operand(uint64_t) { // NOLINT(runtime/explicit) VIXL_UNREACHABLE(); } VIXL_NO_RETURN_IN_DEBUG_MODE Operand(float) { // NOLINT VIXL_UNREACHABLE(); } VIXL_NO_RETURN_IN_DEBUG_MODE Operand(double) { // NOLINT VIXL_UNREACHABLE(); } #endif uint32_t imm_; Register rm_; Shift shift_; uint32_t amount_; Register rs_; }; std::ostream& operator<<(std::ostream& os, const Operand& operand); class NeonImmediate { template struct DataTypeIdentity { T data_type_; }; public: // { # } // where is 32 bit number. // This is allowed to be an implicit constructor because NeonImmediate is // a wrapper class that doesn't normally perform any type conversion. NeonImmediate(uint32_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), immediate_type_(I32) {} NeonImmediate(int immediate) // NOLINT(runtime/explicit) : imm_(immediate), immediate_type_(I32) {} // { # } // where is a 64 bit number // This is allowed to be an implicit constructor because NeonImmediate is // a wrapper class that doesn't normally perform any type conversion. NeonImmediate(int64_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), immediate_type_(I64) {} NeonImmediate(uint64_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), immediate_type_(I64) {} // { # } // where is a non zero floating point number which can be encoded // as an 8 bit floating point (checked by the constructor). // This is allowed to be an implicit constructor because NeonImmediate is // a wrapper class that doesn't normally perform any type conversion. NeonImmediate(float immediate) // NOLINT(runtime/explicit) : imm_(immediate), immediate_type_(F32) {} NeonImmediate(double immediate) // NOLINT(runtime/explicit) : imm_(immediate), immediate_type_(F64) {} NeonImmediate(const NeonImmediate& src) : imm_(src.imm_), immediate_type_(src.immediate_type_) {} template T GetImmediate() const { return GetImmediate(DataTypeIdentity()); } template T GetImmediate(const DataTypeIdentity&) const { VIXL_ASSERT(sizeof(T) <= sizeof(uint32_t)); VIXL_ASSERT(CanConvert()); if (immediate_type_.Is(I64)) return static_cast(imm_.u64_ & static_cast(-1)); if (immediate_type_.Is(F64) || immediate_type_.Is(F32)) return 0; return static_cast(imm_.u32_ & static_cast(-1)); } uint64_t GetImmediate(const DataTypeIdentity&) const { VIXL_ASSERT(CanConvert()); if (immediate_type_.Is(I32)) return imm_.u32_; if (immediate_type_.Is(F64) || immediate_type_.Is(F32)) return 0; return imm_.u64_; } float GetImmediate(const DataTypeIdentity&) const { VIXL_ASSERT(CanConvert()); if (immediate_type_.Is(F64)) return static_cast(imm_.d_); return imm_.f_; } double GetImmediate(const DataTypeIdentity&) const { VIXL_ASSERT(CanConvert()); if (immediate_type_.Is(F32)) return static_cast(imm_.f_); return imm_.d_; } bool IsInteger32() const { return immediate_type_.Is(I32); } bool IsInteger64() const { return immediate_type_.Is(I64); } bool IsInteger() const { return IsInteger32() || IsInteger64(); } bool IsFloat() const { return immediate_type_.Is(F32); } bool IsDouble() const { return immediate_type_.Is(F64); } bool IsFloatZero() const { if (immediate_type_.Is(F32)) return imm_.f_ == 0.0f; if (immediate_type_.Is(F64)) return imm_.d_ == 0.0; return false; } template bool CanConvert() const { return CanConvert(DataTypeIdentity()); } template bool CanConvert(const DataTypeIdentity&) const { VIXL_ASSERT(sizeof(T) < sizeof(uint32_t)); return (immediate_type_.Is(I32) && ((imm_.u32_ >> (8 * sizeof(T))) == 0)) || (immediate_type_.Is(I64) && ((imm_.u64_ >> (8 * sizeof(T))) == 0)) || (immediate_type_.Is(F32) && (imm_.f_ == 0.0f)) || (immediate_type_.Is(F64) && (imm_.d_ == 0.0)); } bool CanConvert(const DataTypeIdentity&) const { return immediate_type_.Is(I32) || (immediate_type_.Is(I64) && ((imm_.u64_ >> 32) == 0)) || (immediate_type_.Is(F32) && (imm_.f_ == 0.0f)) || (immediate_type_.Is(F64) && (imm_.d_ == 0.0)); } bool CanConvert(const DataTypeIdentity&) const { return IsInteger() || CanConvert(); } bool CanConvert(const DataTypeIdentity&) const { return IsFloat() || IsDouble(); } bool CanConvert(const DataTypeIdentity&) const { return IsFloat() || IsDouble(); } friend std::ostream& operator<<(std::ostream& os, const NeonImmediate& operand); private: union NeonImmediateType { uint64_t u64_; double d_; uint32_t u32_; float f_; NeonImmediateType(uint64_t u) : u64_(u) {} NeonImmediateType(int64_t u) : u64_(u) {} NeonImmediateType(uint32_t u) : u32_(u) {} NeonImmediateType(int32_t u) : u32_(u) {} NeonImmediateType(double d) : d_(d) {} NeonImmediateType(float f) : f_(f) {} NeonImmediateType(const NeonImmediateType& ref) : u64_(ref.u64_) {} } imm_; DataType immediate_type_; }; std::ostream& operator<<(std::ostream& os, const NeonImmediate& operand); class NeonOperand { public: NeonOperand(int32_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoDReg) {} NeonOperand(uint32_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoDReg) {} NeonOperand(int64_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoDReg) {} NeonOperand(uint64_t immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoDReg) {} NeonOperand(float immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoDReg) {} NeonOperand(double immediate) // NOLINT(runtime/explicit) : imm_(immediate), rm_(NoDReg) {} NeonOperand(const NeonImmediate& imm) // NOLINT(runtime/explicit) : imm_(imm), rm_(NoDReg) {} NeonOperand(const VRegister& rm) // NOLINT(runtime/explicit) : imm_(0), rm_(rm) { VIXL_ASSERT(rm_.IsValid()); } bool IsImmediate() const { return !rm_.IsValid(); } bool IsRegister() const { return rm_.IsValid(); } bool IsFloatZero() const { VIXL_ASSERT(IsImmediate()); return imm_.IsFloatZero(); } const NeonImmediate& GetNeonImmediate() const { return imm_; } VRegister GetRegister() const { VIXL_ASSERT(IsRegister()); return rm_; } protected: NeonImmediate imm_; VRegister rm_; }; std::ostream& operator<<(std::ostream& os, const NeonOperand& operand); // SOperand represents either an immediate or a SRegister. class SOperand : public NeonOperand { public: // # // where is 32bit int // This is allowed to be an implicit constructor because SOperand is // a wrapper class that doesn't normally perform any type conversion. SOperand(int32_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} SOperand(uint32_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} // # // where is 32bit float SOperand(float immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} // where is 64bit float SOperand(double immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} SOperand(const NeonImmediate& imm) // NOLINT(runtime/explicit) : NeonOperand(imm) {} // rm // This is allowed to be an implicit constructor because SOperand is // a wrapper class that doesn't normally perform any type conversion. SOperand(SRegister rm) // NOLINT(runtime/explicit) : NeonOperand(rm) {} SRegister GetRegister() const { VIXL_ASSERT(IsRegister() && (rm_.GetType() == CPURegister::kSRegister)); return SRegister(rm_.GetCode()); } }; // DOperand represents either an immediate or a DRegister. std::ostream& operator<<(std::ostream& os, const SOperand& operand); class DOperand : public NeonOperand { public: // # // where is uint32_t. // This is allowed to be an implicit constructor because DOperand is // a wrapper class that doesn't normally perform any type conversion. DOperand(int32_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} DOperand(uint32_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} DOperand(int64_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} DOperand(uint64_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} // # // where is a non zero floating point number which can be encoded // as an 8 bit floating point (checked by the constructor). // This is allowed to be an implicit constructor because DOperand is // a wrapper class that doesn't normally perform any type conversion. DOperand(float immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} DOperand(double immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} DOperand(const NeonImmediate& imm) // NOLINT(runtime/explicit) : NeonOperand(imm) {} // rm // This is allowed to be an implicit constructor because DOperand is // a wrapper class that doesn't normally perform any type conversion. DOperand(DRegister rm) // NOLINT(runtime/explicit) : NeonOperand(rm) {} DRegister GetRegister() const { VIXL_ASSERT(IsRegister() && (rm_.GetType() == CPURegister::kDRegister)); return DRegister(rm_.GetCode()); } }; std::ostream& operator<<(std::ostream& os, const DOperand& operand); // QOperand represents either an immediate or a QRegister. class QOperand : public NeonOperand { public: // # // where is uint32_t. // This is allowed to be an implicit constructor because QOperand is // a wrapper class that doesn't normally perform any type conversion. QOperand(int32_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} QOperand(uint32_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} QOperand(int64_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} QOperand(uint64_t immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} QOperand(float immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} QOperand(double immediate) // NOLINT(runtime/explicit) : NeonOperand(immediate) {} QOperand(const NeonImmediate& imm) // NOLINT(runtime/explicit) : NeonOperand(imm) {} // rm // This is allowed to be an implicit constructor because QOperand is // a wrapper class that doesn't normally perform any type conversion. QOperand(QRegister rm) // NOLINT(runtime/explicit) : NeonOperand(rm) { VIXL_ASSERT(rm_.IsValid()); } QRegister GetRegister() const { VIXL_ASSERT(IsRegister() && (rm_.GetType() == CPURegister::kQRegister)); return QRegister(rm_.GetCode()); } }; std::ostream& operator<<(std::ostream& os, const QOperand& operand); class ImmediateVFP : public EncodingValue { template struct FloatType { typedef T base_type; }; public: explicit ImmediateVFP(const NeonImmediate& neon_imm) { if (neon_imm.IsFloat()) { const float imm = neon_imm.GetImmediate(); if (VFP::IsImmFP32(imm)) { SetEncodingValue(VFP::FP32ToImm8(imm)); } } else if (neon_imm.IsDouble()) { const double imm = neon_imm.GetImmediate(); if (VFP::IsImmFP64(imm)) { SetEncodingValue(VFP::FP64ToImm8(imm)); } } } template static T Decode(uint32_t v) { return Decode(v, FloatType()); } static float Decode(uint32_t imm8, const FloatType&) { return VFP::Imm8ToFP32(imm8); } static double Decode(uint32_t imm8, const FloatType&) { return VFP::Imm8ToFP64(imm8); } }; class ImmediateVbic : public EncodingValueAndImmediate { public: ImmediateVbic(DataType dt, const NeonImmediate& neon_imm); static DataType DecodeDt(uint32_t cmode); static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate); }; class ImmediateVand : public ImmediateVbic { public: ImmediateVand(DataType dt, const NeonImmediate neon_imm) : ImmediateVbic(dt, neon_imm) { if (IsValid()) { SetEncodedImmediate(~GetEncodedImmediate() & 0xff); } } }; class ImmediateVmov : public EncodingValueAndImmediate { public: ImmediateVmov(DataType dt, const NeonImmediate& neon_imm); static DataType DecodeDt(uint32_t cmode); static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate); }; class ImmediateVmvn : public EncodingValueAndImmediate { public: ImmediateVmvn(DataType dt, const NeonImmediate& neon_imm); static DataType DecodeDt(uint32_t cmode); static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate); }; class ImmediateVorr : public EncodingValueAndImmediate { public: ImmediateVorr(DataType dt, const NeonImmediate& neon_imm); static DataType DecodeDt(uint32_t cmode); static NeonImmediate DecodeImmediate(uint32_t cmode, uint32_t immediate); }; class ImmediateVorn : public ImmediateVorr { public: ImmediateVorn(DataType dt, const NeonImmediate& neon_imm) : ImmediateVorr(dt, neon_imm) { if (IsValid()) { SetEncodedImmediate(~GetEncodedImmediate() & 0xff); } } }; // MemOperand represents the addressing mode of a load or store instruction. // // Usage: , // // where is the instruction to use (e.g., Ldr(), Str(), etc.), // is general purpose register to be transferred, // is the rest of the arguments to the instruction // // can be in one of 3 addressing modes: // // [ , ] == offset addressing // [ , ]! == pre-indexed addressing // [ ], == post-indexed addressing // // where can be one of: // - an immediate constant, such as , // - an index register // - a shifted index register , # // // The index register may have an associated {+/-} sign, // which if ommitted, defaults to + . // // We have two constructors for the offset: // // One with a signed value offset parameter. The value of sign_ is // "sign_of(constructor's offset parameter) and the value of offset_ is // "constructor's offset parameter". // // The other with a sign and a positive value offset parameters. The value of // sign_ is "constructor's sign parameter" and the value of offset_ is // "constructor's sign parameter * constructor's offset parameter". // // The value of offset_ reflects the effective offset. For an offset_ of 0, // sign_ can be positive or negative. Otherwise, sign_ always agrees with // the sign of offset_. class MemOperand { public: // rn // where rn is the general purpose base register only explicit MemOperand(Register rn, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(plus), rm_(NoReg), shift_(LSL), shift_amount_(0), addrmode_(addrmode | kMemOperandRegisterOnly) { VIXL_ASSERT(rn_.IsValid()); } // rn, # // where rn is the general purpose base register, // is a 32-bit offset to add to rn // // Note: if rn is PC, then this form is equivalent to a "label" // Note: the second constructor allow minus zero (-0). MemOperand(Register rn, int32_t offset, AddrMode addrmode = Offset) : rn_(rn), offset_(offset), sign_((offset < 0) ? minus : plus), rm_(NoReg), shift_(LSL), shift_amount_(0), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid()); } MemOperand(Register rn, Sign sign, int32_t offset, AddrMode addrmode = Offset) : rn_(rn), offset_(sign.IsPlus() ? offset : -offset), sign_(sign), rm_(NoReg), shift_(LSL), shift_amount_(0), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid()); // With this constructor, the sign must only be specified by "sign". VIXL_ASSERT(offset >= 0); } // rn, {+/-}rm // where rn is the general purpose base register, // {+/-} is the sign of the index register, // rm is the general purpose index register, MemOperand(Register rn, Sign sign, Register rm, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(sign), rm_(rm), shift_(LSL), shift_amount_(0), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid() && rm_.IsValid()); } // rn, rm // where rn is the general purpose base register, // rm is the general purpose index register, MemOperand(Register rn, Register rm, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(plus), rm_(rm), shift_(LSL), shift_amount_(0), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid() && rm_.IsValid()); } // rn, {+/-}rm, // where rn is the general purpose base register, // {+/-} is the sign of the index register, // rm is the general purpose index register, // is RRX, applied to value from rm MemOperand(Register rn, Sign sign, Register rm, Shift shift, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(sign), rm_(rm), shift_(shift), shift_amount_(0), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid() && rm_.IsValid()); VIXL_ASSERT(shift_.IsRRX()); } // rn, rm, // where rn is the general purpose base register, // rm is the general purpose index register, // is RRX, applied to value from rm MemOperand(Register rn, Register rm, Shift shift, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(plus), rm_(rm), shift_(shift), shift_amount_(0), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid() && rm_.IsValid()); VIXL_ASSERT(shift_.IsRRX()); } // rn, {+/-}rm, # // where rn is the general purpose base register, // {+/-} is the sign of the index register, // rm is the general purpose index register, // is one of {LSL, LSR, ASR, ROR}, applied to value from rm // is optional size to apply to value from rm MemOperand(Register rn, Sign sign, Register rm, Shift shift, uint32_t shift_amount, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(sign), rm_(rm), shift_(shift), shift_amount_(shift_amount), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid() && rm_.IsValid()); CheckShift(); } // rn, rm, # // where rn is the general purpose base register, // rm is the general purpose index register, // is one of {LSL, LSR, ASR, ROR}, applied to value from rm // is optional size to apply to value from rm MemOperand(Register rn, Register rm, Shift shift, uint32_t shift_amount, AddrMode addrmode = Offset) : rn_(rn), offset_(0), sign_(plus), rm_(rm), shift_(shift), shift_amount_(shift_amount), addrmode_(addrmode) { VIXL_ASSERT(rn_.IsValid() && rm_.IsValid()); CheckShift(); } Register GetBaseRegister() const { return rn_; } int32_t GetOffsetImmediate() const { return offset_; } bool IsOffsetImmediateWithinRange(int min, int max, int multiple_of = 1) const { return (offset_ >= min) && (offset_ <= max) && ((offset_ % multiple_of) == 0); } Sign GetSign() const { return sign_; } Register GetOffsetRegister() const { return rm_; } Shift GetShift() const { return shift_; } unsigned GetShiftAmount() const { return shift_amount_; } AddrMode GetAddrMode() const { return static_cast(addrmode_ & kMemOperandAddrModeMask); } bool IsRegisterOnly() const { return (addrmode_ & kMemOperandRegisterOnly) != 0; } bool IsImmediate() const { return !rm_.IsValid(); } bool IsImmediateZero() const { return !rm_.IsValid() && (offset_ == 0); } bool IsPlainRegister() const { return rm_.IsValid() && shift_.IsLSL() && (shift_amount_ == 0); } bool IsShiftedRegister() const { return rm_.IsValid(); } bool IsImmediateOffset() const { return (GetAddrMode() == Offset) && !rm_.IsValid(); } bool IsImmediateZeroOffset() const { return (GetAddrMode() == Offset) && !rm_.IsValid() && (offset_ == 0); } bool IsRegisterOffset() const { return (GetAddrMode() == Offset) && rm_.IsValid() && shift_.IsLSL() && (shift_amount_ == 0); } bool IsShiftedRegisterOffset() const { return (GetAddrMode() == Offset) && rm_.IsValid(); } uint32_t GetTypeEncodingValue() const { return shift_.IsRRX() ? kRRXEncodedValue : shift_.GetValue(); } bool IsOffset() const { return GetAddrMode() == Offset; } bool IsPreIndex() const { return GetAddrMode() == PreIndex; } bool IsPostIndex() const { return GetAddrMode() == PostIndex; } bool IsShiftValid() const { return shift_.IsValidAmount(shift_amount_); } private: static const int kMemOperandRegisterOnly = 0x1000; static const int kMemOperandAddrModeMask = 0xfff; void CheckShift() { #ifdef VIXL_DEBUG // Disallow any zero shift other than RRX #0 and LSL #0 . if ((shift_amount_ == 0) && shift_.IsRRX()) return; if ((shift_amount_ == 0) && !shift_.IsLSL()) { VIXL_ABORT_WITH_MSG( "A shift by 0 is only accepted in " "the case of lsl and will be treated as " "no shift.\n"); } switch (shift_.GetType()) { case LSL: VIXL_ASSERT(shift_amount_ <= 31); break; case ROR: VIXL_ASSERT(shift_amount_ <= 31); break; case LSR: case ASR: VIXL_ASSERT(shift_amount_ <= 32); break; case RRX: default: VIXL_UNREACHABLE(); break; } #endif } Register rn_; int32_t offset_; Sign sign_; Register rm_; Shift shift_; uint32_t shift_amount_; uint32_t addrmode_; }; std::ostream& operator<<(std::ostream& os, const MemOperand& operand); class AlignedMemOperand : public MemOperand { public: AlignedMemOperand(Register rn, Alignment align, AddrMode addrmode = Offset) : MemOperand(rn, addrmode), align_(align) { VIXL_ASSERT(addrmode != PreIndex); } AlignedMemOperand(Register rn, Alignment align, Register rm, AddrMode addrmode) : MemOperand(rn, rm, addrmode), align_(align) { VIXL_ASSERT(addrmode != PreIndex); } Alignment GetAlignment() const { return align_; } private: Alignment align_; }; std::ostream& operator<<(std::ostream& os, const AlignedMemOperand& operand); } // namespace aarch32 } // namespace vixl #endif // VIXL_AARCH32_OPERANDS_AARCH32_H_