| // Copyright 2016, 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. |
| |
| #include "operands-aarch64.h" |
| |
| namespace vixl { |
| namespace aarch64 { |
| |
| // CPURegList utilities. |
| CPURegister CPURegList::PopLowestIndex() { |
| if (IsEmpty()) { |
| return NoCPUReg; |
| } |
| int index = CountTrailingZeros(list_); |
| VIXL_ASSERT((1 << index) & list_); |
| Remove(index); |
| return CPURegister(index, size_, type_); |
| } |
| |
| |
| CPURegister CPURegList::PopHighestIndex() { |
| VIXL_ASSERT(IsValid()); |
| if (IsEmpty()) { |
| return NoCPUReg; |
| } |
| int index = CountLeadingZeros(list_); |
| index = kRegListSizeInBits - 1 - index; |
| VIXL_ASSERT((1 << index) & list_); |
| Remove(index); |
| return CPURegister(index, size_, type_); |
| } |
| |
| |
| bool CPURegList::IsValid() const { |
| if ((type_ == CPURegister::kRegister) || (type_ == CPURegister::kVRegister)) { |
| bool is_valid = true; |
| // Try to create a CPURegister for each element in the list. |
| for (int i = 0; i < kRegListSizeInBits; i++) { |
| if (((list_ >> i) & 1) != 0) { |
| is_valid &= CPURegister(i, size_, type_).IsValid(); |
| } |
| } |
| return is_valid; |
| } else if (type_ == CPURegister::kNoRegister) { |
| // We can't use IsEmpty here because that asserts IsValid(). |
| return list_ == 0; |
| } else { |
| return false; |
| } |
| } |
| |
| |
| void CPURegList::RemoveCalleeSaved() { |
| if (GetType() == CPURegister::kRegister) { |
| Remove(GetCalleeSaved(GetRegisterSizeInBits())); |
| } else if (GetType() == CPURegister::kVRegister) { |
| Remove(GetCalleeSavedV(GetRegisterSizeInBits())); |
| } else { |
| VIXL_ASSERT(GetType() == CPURegister::kNoRegister); |
| VIXL_ASSERT(IsEmpty()); |
| // The list must already be empty, so do nothing. |
| } |
| } |
| |
| |
| CPURegList CPURegList::Union(const CPURegList& list_1, |
| const CPURegList& list_2, |
| const CPURegList& list_3) { |
| return Union(list_1, Union(list_2, list_3)); |
| } |
| |
| |
| CPURegList CPURegList::Union(const CPURegList& list_1, |
| const CPURegList& list_2, |
| const CPURegList& list_3, |
| const CPURegList& list_4) { |
| return Union(Union(list_1, list_2), Union(list_3, list_4)); |
| } |
| |
| |
| CPURegList CPURegList::Intersection(const CPURegList& list_1, |
| const CPURegList& list_2, |
| const CPURegList& list_3) { |
| return Intersection(list_1, Intersection(list_2, list_3)); |
| } |
| |
| |
| CPURegList CPURegList::Intersection(const CPURegList& list_1, |
| const CPURegList& list_2, |
| const CPURegList& list_3, |
| const CPURegList& list_4) { |
| return Intersection(Intersection(list_1, list_2), |
| Intersection(list_3, list_4)); |
| } |
| |
| |
| CPURegList CPURegList::GetCalleeSaved(unsigned size) { |
| return CPURegList(CPURegister::kRegister, size, 19, 29); |
| } |
| |
| |
| CPURegList CPURegList::GetCalleeSavedV(unsigned size) { |
| return CPURegList(CPURegister::kVRegister, size, 8, 15); |
| } |
| |
| |
| CPURegList CPURegList::GetCallerSaved(unsigned size) { |
| // Registers x0-x18 and lr (x30) are caller-saved. |
| CPURegList list = CPURegList(CPURegister::kRegister, size, 0, 18); |
| // Do not use lr directly to avoid initialisation order fiasco bugs for users. |
| list.Combine(Register(30, kXRegSize)); |
| return list; |
| } |
| |
| |
| CPURegList CPURegList::GetCallerSavedV(unsigned size) { |
| // Registers d0-d7 and d16-d31 are caller-saved. |
| CPURegList list = CPURegList(CPURegister::kVRegister, size, 0, 7); |
| list.Combine(CPURegList(CPURegister::kVRegister, size, 16, 31)); |
| return list; |
| } |
| |
| |
| const CPURegList kCalleeSaved = CPURegList::GetCalleeSaved(); |
| const CPURegList kCalleeSavedV = CPURegList::GetCalleeSavedV(); |
| const CPURegList kCallerSaved = CPURegList::GetCallerSaved(); |
| const CPURegList kCallerSavedV = CPURegList::GetCallerSavedV(); |
| |
| |
| // Registers. |
| #define WREG(n) w##n, |
| const Register Register::wregisters[] = {AARCH64_REGISTER_CODE_LIST(WREG)}; |
| #undef WREG |
| |
| #define XREG(n) x##n, |
| const Register Register::xregisters[] = {AARCH64_REGISTER_CODE_LIST(XREG)}; |
| #undef XREG |
| |
| #define BREG(n) b##n, |
| const VRegister VRegister::bregisters[] = {AARCH64_REGISTER_CODE_LIST(BREG)}; |
| #undef BREG |
| |
| #define HREG(n) h##n, |
| const VRegister VRegister::hregisters[] = {AARCH64_REGISTER_CODE_LIST(HREG)}; |
| #undef HREG |
| |
| #define SREG(n) s##n, |
| const VRegister VRegister::sregisters[] = {AARCH64_REGISTER_CODE_LIST(SREG)}; |
| #undef SREG |
| |
| #define DREG(n) d##n, |
| const VRegister VRegister::dregisters[] = {AARCH64_REGISTER_CODE_LIST(DREG)}; |
| #undef DREG |
| |
| #define QREG(n) q##n, |
| const VRegister VRegister::qregisters[] = {AARCH64_REGISTER_CODE_LIST(QREG)}; |
| #undef QREG |
| |
| #define VREG(n) v##n, |
| const VRegister VRegister::vregisters[] = {AARCH64_REGISTER_CODE_LIST(VREG)}; |
| #undef VREG |
| |
| |
| const Register& Register::GetWRegFromCode(unsigned code) { |
| if (code == kSPRegInternalCode) { |
| return wsp; |
| } else { |
| VIXL_ASSERT(code < kNumberOfRegisters); |
| return wregisters[code]; |
| } |
| } |
| |
| |
| const Register& Register::GetXRegFromCode(unsigned code) { |
| if (code == kSPRegInternalCode) { |
| return sp; |
| } else { |
| VIXL_ASSERT(code < kNumberOfRegisters); |
| return xregisters[code]; |
| } |
| } |
| |
| |
| const VRegister& VRegister::GetBRegFromCode(unsigned code) { |
| VIXL_ASSERT(code < kNumberOfVRegisters); |
| return bregisters[code]; |
| } |
| |
| |
| const VRegister& VRegister::GetHRegFromCode(unsigned code) { |
| VIXL_ASSERT(code < kNumberOfVRegisters); |
| return hregisters[code]; |
| } |
| |
| |
| const VRegister& VRegister::GetSRegFromCode(unsigned code) { |
| VIXL_ASSERT(code < kNumberOfVRegisters); |
| return sregisters[code]; |
| } |
| |
| |
| const VRegister& VRegister::GetDRegFromCode(unsigned code) { |
| VIXL_ASSERT(code < kNumberOfVRegisters); |
| return dregisters[code]; |
| } |
| |
| |
| const VRegister& VRegister::GetQRegFromCode(unsigned code) { |
| VIXL_ASSERT(code < kNumberOfVRegisters); |
| return qregisters[code]; |
| } |
| |
| |
| const VRegister& VRegister::GetVRegFromCode(unsigned code) { |
| VIXL_ASSERT(code < kNumberOfVRegisters); |
| return vregisters[code]; |
| } |
| |
| |
| const Register& CPURegister::W() const { |
| VIXL_ASSERT(IsValidRegister()); |
| return Register::GetWRegFromCode(code_); |
| } |
| |
| |
| const Register& CPURegister::X() const { |
| VIXL_ASSERT(IsValidRegister()); |
| return Register::GetXRegFromCode(code_); |
| } |
| |
| |
| const VRegister& CPURegister::B() const { |
| VIXL_ASSERT(IsValidVRegister()); |
| return VRegister::GetBRegFromCode(code_); |
| } |
| |
| |
| const VRegister& CPURegister::H() const { |
| VIXL_ASSERT(IsValidVRegister()); |
| return VRegister::GetHRegFromCode(code_); |
| } |
| |
| |
| const VRegister& CPURegister::S() const { |
| VIXL_ASSERT(IsValidVRegister()); |
| return VRegister::GetSRegFromCode(code_); |
| } |
| |
| |
| const VRegister& CPURegister::D() const { |
| VIXL_ASSERT(IsValidVRegister()); |
| return VRegister::GetDRegFromCode(code_); |
| } |
| |
| |
| const VRegister& CPURegister::Q() const { |
| VIXL_ASSERT(IsValidVRegister()); |
| return VRegister::GetQRegFromCode(code_); |
| } |
| |
| |
| const VRegister& CPURegister::V() const { |
| VIXL_ASSERT(IsValidVRegister()); |
| return VRegister::GetVRegFromCode(code_); |
| } |
| |
| |
| // Operand. |
| Operand::Operand(int64_t immediate) |
| : immediate_(immediate), |
| reg_(NoReg), |
| shift_(NO_SHIFT), |
| extend_(NO_EXTEND), |
| shift_amount_(0) {} |
| |
| |
| Operand::Operand(Register reg, Shift shift, unsigned shift_amount) |
| : reg_(reg), |
| shift_(shift), |
| extend_(NO_EXTEND), |
| shift_amount_(shift_amount) { |
| VIXL_ASSERT(shift != MSL); |
| VIXL_ASSERT(reg.Is64Bits() || (shift_amount < kWRegSize)); |
| VIXL_ASSERT(reg.Is32Bits() || (shift_amount < kXRegSize)); |
| VIXL_ASSERT(!reg.IsSP()); |
| } |
| |
| |
| Operand::Operand(Register reg, Extend extend, unsigned shift_amount) |
| : reg_(reg), |
| shift_(NO_SHIFT), |
| extend_(extend), |
| shift_amount_(shift_amount) { |
| VIXL_ASSERT(reg.IsValid()); |
| VIXL_ASSERT(shift_amount <= 4); |
| VIXL_ASSERT(!reg.IsSP()); |
| |
| // Extend modes SXTX and UXTX require a 64-bit register. |
| VIXL_ASSERT(reg.Is64Bits() || ((extend != SXTX) && (extend != UXTX))); |
| } |
| |
| |
| bool Operand::IsImmediate() const { return reg_.Is(NoReg); } |
| |
| |
| bool Operand::IsPlainRegister() const { |
| return reg_.IsValid() && |
| (((shift_ == NO_SHIFT) && (extend_ == NO_EXTEND)) || |
| // No-op shifts. |
| ((shift_ != NO_SHIFT) && (shift_amount_ == 0)) || |
| // No-op extend operations. |
| ((extend_ == UXTX) || (extend_ == SXTX) || |
| (reg_.IsW() && ((extend_ == UXTW) || (extend_ == SXTW))))); |
| } |
| |
| |
| bool Operand::IsShiftedRegister() const { |
| return reg_.IsValid() && (shift_ != NO_SHIFT); |
| } |
| |
| |
| bool Operand::IsExtendedRegister() const { |
| return reg_.IsValid() && (extend_ != NO_EXTEND); |
| } |
| |
| |
| bool Operand::IsZero() const { |
| if (IsImmediate()) { |
| return GetImmediate() == 0; |
| } else { |
| return GetRegister().IsZero(); |
| } |
| } |
| |
| |
| Operand Operand::ToExtendedRegister() const { |
| VIXL_ASSERT(IsShiftedRegister()); |
| VIXL_ASSERT((shift_ == LSL) && (shift_amount_ <= 4)); |
| return Operand(reg_, reg_.Is64Bits() ? UXTX : UXTW, shift_amount_); |
| } |
| |
| |
| // MemOperand |
| MemOperand::MemOperand() |
| : base_(NoReg), |
| regoffset_(NoReg), |
| offset_(0), |
| addrmode_(Offset), |
| shift_(NO_SHIFT), |
| extend_(NO_EXTEND) {} |
| |
| |
| MemOperand::MemOperand(Register base, int64_t offset, AddrMode addrmode) |
| : base_(base), |
| regoffset_(NoReg), |
| offset_(offset), |
| addrmode_(addrmode), |
| shift_(NO_SHIFT), |
| extend_(NO_EXTEND), |
| shift_amount_(0) { |
| VIXL_ASSERT(base.Is64Bits() && !base.IsZero()); |
| } |
| |
| |
| MemOperand::MemOperand(Register base, |
| Register regoffset, |
| Extend extend, |
| unsigned shift_amount) |
| : base_(base), |
| regoffset_(regoffset), |
| offset_(0), |
| addrmode_(Offset), |
| shift_(NO_SHIFT), |
| extend_(extend), |
| shift_amount_(shift_amount) { |
| VIXL_ASSERT(base.Is64Bits() && !base.IsZero()); |
| VIXL_ASSERT(!regoffset.IsSP()); |
| VIXL_ASSERT((extend == UXTW) || (extend == SXTW) || (extend == SXTX)); |
| |
| // SXTX extend mode requires a 64-bit offset register. |
| VIXL_ASSERT(regoffset.Is64Bits() || (extend != SXTX)); |
| } |
| |
| |
| MemOperand::MemOperand(Register base, |
| Register regoffset, |
| Shift shift, |
| unsigned shift_amount) |
| : base_(base), |
| regoffset_(regoffset), |
| offset_(0), |
| addrmode_(Offset), |
| shift_(shift), |
| extend_(NO_EXTEND), |
| shift_amount_(shift_amount) { |
| VIXL_ASSERT(base.Is64Bits() && !base.IsZero()); |
| VIXL_ASSERT(regoffset.Is64Bits() && !regoffset.IsSP()); |
| VIXL_ASSERT(shift == LSL); |
| } |
| |
| |
| MemOperand::MemOperand(Register base, const Operand& offset, AddrMode addrmode) |
| : base_(base), |
| regoffset_(NoReg), |
| addrmode_(addrmode), |
| shift_(NO_SHIFT), |
| extend_(NO_EXTEND), |
| shift_amount_(0) { |
| VIXL_ASSERT(base.Is64Bits() && !base.IsZero()); |
| |
| if (offset.IsImmediate()) { |
| offset_ = offset.GetImmediate(); |
| } else if (offset.IsShiftedRegister()) { |
| VIXL_ASSERT((addrmode == Offset) || (addrmode == PostIndex)); |
| |
| regoffset_ = offset.GetRegister(); |
| shift_ = offset.GetShift(); |
| shift_amount_ = offset.GetShiftAmount(); |
| |
| extend_ = NO_EXTEND; |
| offset_ = 0; |
| |
| // These assertions match those in the shifted-register constructor. |
| VIXL_ASSERT(regoffset_.Is64Bits() && !regoffset_.IsSP()); |
| VIXL_ASSERT(shift_ == LSL); |
| } else { |
| VIXL_ASSERT(offset.IsExtendedRegister()); |
| VIXL_ASSERT(addrmode == Offset); |
| |
| regoffset_ = offset.GetRegister(); |
| extend_ = offset.GetExtend(); |
| shift_amount_ = offset.GetShiftAmount(); |
| |
| shift_ = NO_SHIFT; |
| offset_ = 0; |
| |
| // These assertions match those in the extended-register constructor. |
| VIXL_ASSERT(!regoffset_.IsSP()); |
| VIXL_ASSERT((extend_ == UXTW) || (extend_ == SXTW) || (extend_ == SXTX)); |
| VIXL_ASSERT((regoffset_.Is64Bits() || (extend_ != SXTX))); |
| } |
| } |
| |
| |
| bool MemOperand::IsImmediateOffset() const { |
| return (addrmode_ == Offset) && regoffset_.Is(NoReg); |
| } |
| |
| |
| bool MemOperand::IsRegisterOffset() const { |
| return (addrmode_ == Offset) && !regoffset_.Is(NoReg); |
| } |
| |
| |
| bool MemOperand::IsPreIndex() const { return addrmode_ == PreIndex; } |
| |
| |
| bool MemOperand::IsPostIndex() const { return addrmode_ == PostIndex; } |
| |
| |
| void MemOperand::AddOffset(int64_t offset) { |
| VIXL_ASSERT(IsImmediateOffset()); |
| offset_ += offset; |
| } |
| |
| |
| GenericOperand::GenericOperand(const CPURegister& reg) |
| : cpu_register_(reg), mem_op_size_(0) { |
| if (reg.IsQ()) { |
| VIXL_ASSERT(reg.GetSizeInBits() > static_cast<int>(kXRegSize)); |
| // Support for Q registers is not implemented yet. |
| VIXL_UNIMPLEMENTED(); |
| } |
| } |
| |
| |
| GenericOperand::GenericOperand(const MemOperand& mem_op, size_t mem_op_size) |
| : cpu_register_(NoReg), mem_op_(mem_op), mem_op_size_(mem_op_size) { |
| if (mem_op_size_ > kXRegSizeInBytes) { |
| // We only support generic operands up to the size of X registers. |
| VIXL_UNIMPLEMENTED(); |
| } |
| } |
| |
| bool GenericOperand::Equals(const GenericOperand& other) const { |
| if (!IsValid() || !other.IsValid()) { |
| // Two invalid generic operands are considered equal. |
| return !IsValid() && !other.IsValid(); |
| } |
| if (IsCPURegister() && other.IsCPURegister()) { |
| return GetCPURegister().Is(other.GetCPURegister()); |
| } else if (IsMemOperand() && other.IsMemOperand()) { |
| return GetMemOperand().Equals(other.GetMemOperand()) && |
| (GetMemOperandSizeInBytes() == other.GetMemOperandSizeInBytes()); |
| } |
| return false; |
| } |
| } |
| } // namespace vixl::aarch64 |