| // Copyright 2015, 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. |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <assert.h> |
| #include <iostream> // NOLINT |
| #include "utils-vixl.h" |
| #include "a32/constants-a32.h" |
| #include "a32/instructions-a32.h" |
| |
| namespace vixl { |
| namespace aarch32 { |
| |
| |
| bool Shift::IsValidAmount(uint32_t amount) const { |
| switch (GetType()) { |
| case LSL: |
| return amount <= 31; |
| case ROR: |
| return (amount > 0) && (amount <= 31); |
| case LSR: |
| case ASR: |
| return (amount > 0) && (amount <= 32); |
| case RRX: |
| return amount == 0; |
| default: |
| VIXL_UNREACHABLE(); |
| return false; |
| } |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, const Register reg) { |
| switch (reg.GetCode()) { |
| case 10: |
| return os << "sl"; |
| case 11: |
| return os << "fp"; |
| case 12: |
| return os << "ip"; |
| case 13: |
| return os << "sp"; |
| case 14: |
| return os << "lr"; |
| case 15: |
| return os << "pc"; |
| default: |
| return os << "r" << reg.GetCode(); |
| } |
| } |
| |
| |
| SRegister VRegister::S() const { |
| VIXL_ASSERT(GetType() == kSRegister); |
| return SRegister(GetCode()); |
| } |
| |
| |
| DRegister VRegister::D() const { |
| VIXL_ASSERT(GetType() == kDRegister); |
| return DRegister(GetCode()); |
| } |
| |
| |
| QRegister VRegister::Q() const { |
| VIXL_ASSERT(GetType() == kQRegister); |
| return QRegister(GetCode()); |
| } |
| |
| |
| Register RegisterList::GetFirstAvailableRegister() const { |
| for (uint32_t i = 0; i < kNumberOfRegisters; i++) { |
| if (((list_ >> i) & 1) != 0) return Register(i); |
| } |
| return Register(); |
| } |
| |
| |
| std::ostream& PrintRegisterList(std::ostream& os, uint32_t list) { // NOLINT |
| os << "{"; |
| bool first = true; |
| int code = 0; |
| while (list != 0) { |
| if ((list & 1) != 0) { |
| if (first) { |
| first = false; |
| } else { |
| os << ","; |
| } |
| os << Register(code); |
| } |
| list >>= 1; |
| code++; |
| } |
| os << "}"; |
| return os; |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, RegisterList registers) { |
| return PrintRegisterList(os, registers.GetList()); |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, RegisterListWithPC registers) { |
| return PrintRegisterList(os, registers.GetList() | (1 << kPcCode)); |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, RegisterListWithoutPC registers) { |
| return PrintRegisterList(os, registers.GetList()); |
| } |
| |
| |
| QRegister VRegisterList::GetFirstAvailableQRegister() const { |
| for (uint32_t i = 0; i < kNumberOfQRegisters; i++) { |
| if (((list_ >> (i * 4)) & 0xf) == 0xf) return QRegister(i); |
| } |
| return QRegister(); |
| } |
| |
| |
| DRegister VRegisterList::GetFirstAvailableDRegister() const { |
| for (uint32_t i = 0; i < kMaxNumberOfDRegisters; i++) { |
| if (((list_ >> (i * 2)) & 0x3) == 0x3) return DRegister(i); |
| } |
| return DRegister(); |
| } |
| |
| |
| SRegister VRegisterList::GetFirstAvailableSRegister() const { |
| for (uint32_t i = 0; i < kNumberOfSRegisters; i++) { |
| if (((list_ >> i) & 0x1) != 0) return SRegister(i); |
| } |
| return SRegister(); |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, SRegisterList reglist) { |
| SRegister first = reglist.GetFirstSRegister(); |
| SRegister last = reglist.GetLastSRegister(); |
| if (first.Is(last)) |
| os << "{" << first << "}"; |
| else |
| os << "{" << first << "-" << last << "}"; |
| return os; |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, DRegisterList reglist) { |
| DRegister first = reglist.GetFirstDRegister(); |
| DRegister last = reglist.GetLastDRegister(); |
| if (first.Is(last)) |
| os << "{" << first << "}"; |
| else |
| os << "{" << first << "-" << last << "}"; |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, NeonRegisterList nreglist) { |
| DRegister first = nreglist.GetFirstDRegister(); |
| int increment = nreglist.IsSingleSpaced() ? 1 : 2; |
| int count = |
| nreglist.GetLastDRegister().GetCode() - first.GetCode() + increment; |
| if (count < 0) count += kMaxNumberOfDRegisters; |
| os << "{"; |
| bool first_displayed = false; |
| for (;;) { |
| if (first_displayed) { |
| os << ","; |
| } else { |
| first_displayed = true; |
| } |
| os << first; |
| if (nreglist.IsTransferOneLane()) { |
| os << "[" << nreglist.GetTransferLane() << "]"; |
| } else if (nreglist.IsTransferAllLanes()) { |
| os << "[]"; |
| } |
| count -= increment; |
| if (count <= 0) break; |
| unsigned next = first.GetCode() + increment; |
| if (next >= kMaxNumberOfDRegisters) next -= kMaxNumberOfDRegisters; |
| first = DRegister(next); |
| } |
| os << "}"; |
| return os; |
| } |
| |
| |
| const char* SpecialRegister::GetName() const { |
| switch (reg_) { |
| case APSR: |
| return "APSR"; |
| case SPSR: |
| return "SPSR"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| const char* MaskedSpecialRegister::GetName() const { |
| switch (reg_) { |
| case APSR_nzcvq: |
| return "APSR_nzcvq"; |
| case APSR_g: |
| return "APSR_g"; |
| case APSR_nzcvqg: |
| return "APSR_nzcvqg"; |
| case CPSR_c: |
| return "CPSR_c"; |
| case CPSR_x: |
| return "CPSR_x"; |
| case CPSR_xc: |
| return "CPSR_xc"; |
| case CPSR_sc: |
| return "CPSR_sc"; |
| case CPSR_sx: |
| return "CPSR_sx"; |
| case CPSR_sxc: |
| return "CPSR_sxc"; |
| case CPSR_fc: |
| return "CPSR_fc"; |
| case CPSR_fx: |
| return "CPSR_fx"; |
| case CPSR_fxc: |
| return "CPSR_fxc"; |
| case CPSR_fsc: |
| return "CPSR_fsc"; |
| case CPSR_fsx: |
| return "CPSR_fsx"; |
| case CPSR_fsxc: |
| return "CPSR_fsxc"; |
| case SPSR_c: |
| return "SPSR_c"; |
| case SPSR_x: |
| return "SPSR_x"; |
| case SPSR_xc: |
| return "SPSR_xc"; |
| case SPSR_s: |
| return "SPSR_s"; |
| case SPSR_sc: |
| return "SPSR_sc"; |
| case SPSR_sx: |
| return "SPSR_sx"; |
| case SPSR_sxc: |
| return "SPSR_sxc"; |
| case SPSR_f: |
| return "SPSR_f"; |
| case SPSR_fc: |
| return "SPSR_fc"; |
| case SPSR_fx: |
| return "SPSR_fx"; |
| case SPSR_fxc: |
| return "SPSR_fxc"; |
| case SPSR_fs: |
| return "SPSR_fs"; |
| case SPSR_fsc: |
| return "SPSR_fsc"; |
| case SPSR_fsx: |
| return "SPSR_fsx"; |
| case SPSR_fsxc: |
| return "SPSR_fsxc"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| const char* BankedRegister::GetName() const { |
| switch (reg_) { |
| case R8_usr: |
| return "R8_usr"; |
| case R9_usr: |
| return "R9_usr"; |
| case R10_usr: |
| return "R10_usr"; |
| case R11_usr: |
| return "R11_usr"; |
| case R12_usr: |
| return "R12_usr"; |
| case SP_usr: |
| return "SP_usr"; |
| case LR_usr: |
| return "LR_usr"; |
| case R8_fiq: |
| return "R8_fiq"; |
| case R9_fiq: |
| return "R9_fiq"; |
| case R10_fiq: |
| return "R10_fiq"; |
| case R11_fiq: |
| return "R11_fiq"; |
| case R12_fiq: |
| return "R12_fiq"; |
| case SP_fiq: |
| return "SP_fiq"; |
| case LR_fiq: |
| return "LR_fiq"; |
| case LR_irq: |
| return "LR_irq"; |
| case SP_irq: |
| return "SP_irq"; |
| case LR_svc: |
| return "LR_svc"; |
| case SP_svc: |
| return "SP_svc"; |
| case LR_abt: |
| return "LR_abt"; |
| case SP_abt: |
| return "SP_abt"; |
| case LR_und: |
| return "LR_und"; |
| case SP_und: |
| return "SP_und"; |
| case LR_mon: |
| return "LR_mon"; |
| case SP_mon: |
| return "SP_mon"; |
| case ELR_hyp: |
| return "ELR_hyp"; |
| case SP_hyp: |
| return "SP_hyp"; |
| case SPSR_fiq: |
| return "SPSR_fiq"; |
| case SPSR_irq: |
| return "SPSR_irq"; |
| case SPSR_svc: |
| return "SPSR_svc"; |
| case SPSR_abt: |
| return "SPSR_abt"; |
| case SPSR_und: |
| return "SPSR_und"; |
| case SPSR_mon: |
| return "SPSR_mon"; |
| case SPSR_hyp: |
| return "SPSR_hyp"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| const char* SpecialFPRegister::GetName() const { |
| switch (reg_) { |
| case FPSID: |
| return "FPSID"; |
| case FPSCR: |
| return "FPSCR"; |
| case MVFR2: |
| return "MVFR2"; |
| case MVFR1: |
| return "MVFR1"; |
| case MVFR0: |
| return "MVFR0"; |
| case FPEXC: |
| return "FPEXC"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| const char* Condition::GetName() const { |
| switch (condition_) { |
| case eq: |
| return "eq"; |
| case ne: |
| return "ne"; |
| case cs: |
| return "cs"; |
| case cc: |
| return "cc"; |
| case mi: |
| return "mi"; |
| case pl: |
| return "pl"; |
| case vs: |
| return "vs"; |
| case vc: |
| return "vc"; |
| case hi: |
| return "hi"; |
| case ls: |
| return "ls"; |
| case ge: |
| return "ge"; |
| case lt: |
| return "lt"; |
| case gt: |
| return "gt"; |
| case le: |
| return "le"; |
| case al: |
| return ""; |
| case Condition::kNone: |
| return ""; |
| } |
| return "<und>"; |
| } |
| |
| |
| const char* Shift::GetName() const { |
| switch (shift_) { |
| case LSL: |
| return "lsl"; |
| case LSR: |
| return "lsr"; |
| case ASR: |
| return "asr"; |
| case ROR: |
| return "ror"; |
| case RRX: |
| return "rrx"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| const char* EncodingSize::GetName() const { |
| switch (size_) { |
| case Best: |
| return ""; |
| case Narrow: |
| return ".n"; |
| case Wide: |
| return ".w"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| const char* DataType::GetName() const { |
| switch (value_) { |
| case kDataTypeValueInvalid: |
| return ".??"; |
| case kDataTypeValueNone: |
| return ""; |
| case S8: |
| return ".s8"; |
| case S16: |
| return ".s16"; |
| case S32: |
| return ".s32"; |
| case S64: |
| return ".s64"; |
| case U8: |
| return ".u8"; |
| case U16: |
| return ".u16"; |
| case U32: |
| return ".u32"; |
| case U64: |
| return ".u64"; |
| case F16: |
| return ".f16"; |
| case F32: |
| return ".f32"; |
| case F64: |
| return ".f64"; |
| case I8: |
| return ".i8"; |
| case I16: |
| return ".i16"; |
| case I32: |
| return ".i32"; |
| case I64: |
| return ".i64"; |
| case P8: |
| return ".p8"; |
| case P64: |
| return ".p64"; |
| case Untyped8: |
| return ".8"; |
| case Untyped16: |
| return ".16"; |
| case Untyped32: |
| return ".32"; |
| case Untyped64: |
| return ".64"; |
| } |
| VIXL_UNREACHABLE(); |
| return ".??"; |
| } |
| |
| |
| const char* MemoryBarrier::GetName() const { |
| switch (type_) { |
| case OSHLD: |
| return "oshld"; |
| case OSHST: |
| return "oshst"; |
| case OSH: |
| return "osh"; |
| case NSHLD: |
| return "nshld"; |
| case NSHST: |
| return "nshst"; |
| case NSH: |
| return "nsh"; |
| case ISHLD: |
| return "ishld"; |
| case ISHST: |
| return "ishst"; |
| case ISH: |
| return "ish"; |
| case LD: |
| return "ld"; |
| case ST: |
| return "st"; |
| case SY: |
| return "sy"; |
| } |
| switch (static_cast<int>(type_)) { |
| case 0: |
| return "#0x0"; |
| case 4: |
| return "#0x4"; |
| case 8: |
| return "#0x8"; |
| case 0xc: |
| return "#0xc"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| const char* InterruptFlags::GetName() const { |
| switch (type_) { |
| case F: |
| return "f"; |
| case I: |
| return "i"; |
| case IF: |
| return "if"; |
| case A: |
| return "a"; |
| case AF: |
| return "af"; |
| case AI: |
| return "ai"; |
| case AIF: |
| return "aif"; |
| } |
| VIXL_ASSERT(type_ == 0); |
| return ""; |
| } |
| |
| |
| const char* Endianness::GetName() const { |
| switch (type_) { |
| case LE: |
| return "le"; |
| case BE: |
| return "be"; |
| } |
| VIXL_UNREACHABLE(); |
| return "??"; |
| } |
| |
| |
| // Constructor used for disassembly. |
| ImmediateShiftOperand::ImmediateShiftOperand(int shift_value, int amount_value) |
| : Shift(shift_value) { |
| switch (shift_value) { |
| case LSL: |
| amount_ = amount_value; |
| break; |
| case LSR: |
| case ASR: |
| amount_ = (amount_value == 0) ? 32 : amount_value; |
| break; |
| case ROR: |
| amount_ = amount_value; |
| if (amount_value == 0) SetType(RRX); |
| break; |
| default: |
| VIXL_UNREACHABLE(); |
| SetType(LSL); |
| amount_ = 0; |
| break; |
| } |
| } |
| |
| |
| ImmediateT32::ImmediateT32(uint32_t imm) { |
| // 00000000 00000000 00000000 abcdefgh |
| if ((imm & ~0xff) == 0) { |
| SetEncodingValue(imm); |
| return; |
| } |
| if ((imm >> 16) == (imm & 0xffff)) { |
| if ((imm & 0xff00) == 0) { |
| // 00000000 abcdefgh 00000000 abcdefgh |
| SetEncodingValue((imm & 0xff) | (0x1 << 8)); |
| return; |
| } |
| if ((imm & 0xff) == 0) { |
| // abcdefgh 00000000 abcdefgh 00000000 |
| SetEncodingValue(((imm >> 8) & 0xff) | (0x2 << 8)); |
| return; |
| } |
| if (((imm >> 8) & 0xff) == (imm & 0xff)) { |
| // abcdefgh abcdefgh abcdefgh abcdefgh |
| SetEncodingValue((imm & 0xff) | (0x3 << 8)); |
| return; |
| } |
| } |
| for (int shift = 0; shift < 24; shift++) { |
| uint32_t imm8 = imm >> (24 - shift); |
| uint32_t overflow = imm << (8 + shift); |
| if ((imm8 <= 0xff) && ((imm8 & 0x80) != 0) && (overflow == 0)) { |
| SetEncodingValue(((shift + 8) << 7) | (imm8 & 0x7F)); |
| return; |
| } |
| } |
| } |
| |
| |
| static inline uint32_t ror(uint32_t x, int i) { |
| VIXL_ASSERT((0 < i) && (i < 32)); |
| return (x >> i) | (x << (32 - i)); |
| } |
| |
| |
| bool ImmediateT32::IsImmediateT32(uint32_t imm) { |
| /* abcdefgh abcdefgh abcdefgh abcdefgh */ |
| if ((imm ^ ror(imm, 8)) == 0) return true; |
| /* 00000000 abcdefgh 00000000 abcdefgh */ |
| /* abcdefgh 00000000 abcdefgh 00000000 */ |
| if ((imm ^ ror(imm, 16)) == 0 && |
| (((imm & 0xff00) == 0) || ((imm & 0xff) == 0))) |
| return true; |
| /* isolate least-significant set bit */ |
| uint32_t lsb = imm & -imm; |
| /* if imm is less than lsb*256 then it fits, but instead we test imm/256 to |
| * avoid overflow (underflow is always a successful case) */ |
| return ((imm >> 8) < lsb); |
| } |
| |
| |
| uint32_t ImmediateT32::Decode(uint32_t value) { |
| uint32_t base = value & 0xff; |
| switch (value >> 8) { |
| case 0: |
| return base; |
| case 1: |
| return base | (base << 16); |
| case 2: |
| return (base << 8) | (base << 24); |
| case 3: |
| return base | (base << 8) | (base << 16) | (base << 24); |
| default: |
| base |= 0x80; |
| return base << (32 - (value >> 7)); |
| } |
| } |
| |
| |
| ImmediateA32::ImmediateA32(uint32_t imm) { |
| // Deal with rot = 0 first to avoid undefined shift by 32. |
| if (imm <= 0xff) { |
| SetEncodingValue(imm); |
| return; |
| } |
| for (int rot = 2; rot < 32; rot += 2) { |
| uint32_t imm8 = (imm << rot) | (imm >> (32 - rot)); |
| if (imm8 <= 0xff) { |
| SetEncodingValue((rot << 7) | imm8); |
| return; |
| } |
| } |
| } |
| |
| |
| bool ImmediateA32::IsImmediateA32(uint32_t imm) { |
| /* fast-out */ |
| if (imm < 256) return true; |
| /* avoid getting confused by wrapped-around bytes (this transform has no |
| * effect on pass/fail results) */ |
| if (imm & 0xff000000) imm = ror(imm, 16); |
| /* copy odd-numbered set bits into even-numbered bits immediately below, so |
| * that the least-significant set bit is always an even bit */ |
| imm = imm | ((imm >> 1) & 0x55555555); |
| /* isolate least-significant set bit (always even) */ |
| uint32_t lsb = imm & -imm; |
| /* if imm is less than lsb*256 then it fits, but instead we test imm/256 to |
| * avoid overflow (underflow is always a successful case) */ |
| return ((imm >> 8) < lsb); |
| } |
| |
| |
| uint32_t ImmediateA32::Decode(uint32_t value) { |
| int rotation = value >> 8; |
| VIXL_ASSERT(rotation <= 15); |
| rotation *= 2; |
| value &= 0xff; |
| return (value >> rotation) | (value << (32 - rotation)); |
| } |
| |
| |
| uint32_t TypeEncodingValue(Shift shift) { |
| return shift.IsRRX() ? kRRXEncodedValue : shift.GetValue(); |
| } |
| |
| |
| uint32_t AmountEncodingValue(Shift shift, uint32_t amount) { |
| switch (shift.GetType()) { |
| case LSL: |
| case ROR: |
| return amount; |
| case LSR: |
| case ASR: |
| return amount % 32; |
| case RRX: |
| return 0; |
| } |
| return 0; |
| } |
| |
| } // namespace aarch32 |
| } // namespace vixl |