// 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. extern "C" { #include <stdint.h> #include <inttypes.h> } #include <cassert> #include <cmath> #include <cstdio> #include <cstdlib> #include <cstring> #include <iostream> #include "utils-vixl.h" #include "aarch32/constants-aarch32.h" #include "aarch32/instructions-aarch32.h" #include "aarch32/operands-aarch32.h" namespace vixl { namespace aarch32 { // Operand std::ostream& operator<<(std::ostream& os, const Operand& operand) { if (operand.IsImmediate()) { return os << "#" << operand.GetImmediate(); } if (operand.IsImmediateShiftedRegister()) { if ((operand.GetShift().IsLSL() || operand.GetShift().IsROR()) && (operand.GetShiftAmount() == 0)) { return os << operand.GetBaseRegister(); } if (operand.GetShift().IsRRX()) { return os << operand.GetBaseRegister() << ", rrx"; } return os << operand.GetBaseRegister() << ", " << operand.GetShift() << " #" << operand.GetShiftAmount(); } if (operand.IsRegisterShiftedRegister()) { return os << operand.GetBaseRegister() << ", " << operand.GetShift() << " " << operand.GetShiftRegister(); } VIXL_UNREACHABLE(); return os; } std::ostream& operator<<(std::ostream& os, const NeonImmediate& neon_imm) { if (neon_imm.IsDouble()) { if (neon_imm.imm_.d_ == 0) { if (copysign(1.0, neon_imm.imm_.d_) < 0.0) { return os << "#-0.0"; } return os << "#0.0"; } return os << "#" << std::setprecision(9) << neon_imm.imm_.d_; } if (neon_imm.IsFloat()) { if (neon_imm.imm_.f_ == 0) { if (copysign(1.0, neon_imm.imm_.d_) < 0.0) return os << "#-0.0"; return os << "#0.0"; } return os << "#" << std::setprecision(9) << neon_imm.imm_.f_; } if (neon_imm.IsInteger64()) { return os << "#0x" << std::hex << std::setw(16) << std::setfill('0') << neon_imm.imm_.u64_ << std::dec; } return os << "#" << neon_imm.imm_.u32_; } // SOperand std::ostream& operator<<(std::ostream& os, const SOperand& operand) { if (operand.IsImmediate()) { return os << operand.GetNeonImmediate(); } return os << operand.GetRegister(); } // DOperand std::ostream& operator<<(std::ostream& os, const DOperand& operand) { if (operand.IsImmediate()) { return os << operand.GetNeonImmediate(); } return os << operand.GetRegister(); } // QOperand std::ostream& operator<<(std::ostream& os, const QOperand& operand) { if (operand.IsImmediate()) { return os << operand.GetNeonImmediate(); } return os << operand.GetRegister(); } ImmediateVbic::ImmediateVbic(DataType dt, const NeonImmediate& neon_imm) { if (neon_imm.IsInteger32()) { uint32_t immediate = neon_imm.GetImmediate<uint32_t>(); if (dt.GetValue() == I16) { if ((immediate & ~0xff) == 0) { SetEncodingValue(0x9); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0xb); SetEncodedImmediate(immediate >> 8); } } else if (dt.GetValue() == I32) { if ((immediate & ~0xff) == 0) { SetEncodingValue(0x1); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0x3); SetEncodedImmediate(immediate >> 8); } else if ((immediate & ~0xff0000) == 0) { SetEncodingValue(0x5); SetEncodedImmediate(immediate >> 16); } else if ((immediate & ~0xff000000) == 0) { SetEncodingValue(0x7); SetEncodedImmediate(immediate >> 24); } } } } DataType ImmediateVbic::DecodeDt(uint32_t cmode) { switch (cmode) { case 0x1: case 0x3: case 0x5: case 0x7: return I32; case 0x9: case 0xb: return I16; default: break; } VIXL_UNREACHABLE(); return kDataTypeValueInvalid; } NeonImmediate ImmediateVbic::DecodeImmediate(uint32_t cmode, uint32_t immediate) { switch (cmode) { case 0x1: case 0x9: return immediate; case 0x3: case 0xb: return immediate << 8; case 0x5: return immediate << 16; case 0x7: return immediate << 24; default: break; } VIXL_UNREACHABLE(); return 0; } ImmediateVmov::ImmediateVmov(DataType dt, const NeonImmediate& neon_imm) { if (neon_imm.IsInteger()) { switch (dt.GetValue()) { case I8: if (neon_imm.CanConvert<uint8_t>()) { SetEncodingValue(0xe); SetEncodedImmediate(neon_imm.GetImmediate<uint8_t>()); } break; case I16: if (neon_imm.IsInteger32()) { uint32_t immediate = neon_imm.GetImmediate<uint32_t>(); if ((immediate & ~0xff) == 0) { SetEncodingValue(0x8); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0xa); SetEncodedImmediate(immediate >> 8); } } break; case I32: if (neon_imm.IsInteger32()) { uint32_t immediate = neon_imm.GetImmediate<uint32_t>(); if ((immediate & ~0xff) == 0) { SetEncodingValue(0x0); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0x2); SetEncodedImmediate(immediate >> 8); } else if ((immediate & ~0xff0000) == 0) { SetEncodingValue(0x4); SetEncodedImmediate(immediate >> 16); } else if ((immediate & ~0xff000000) == 0) { SetEncodingValue(0x6); SetEncodedImmediate(immediate >> 24); } else if ((immediate & ~0xff00) == 0xff) { SetEncodingValue(0xc); SetEncodedImmediate(immediate >> 8); } else if ((immediate & ~0xff0000) == 0xffff) { SetEncodingValue(0xd); SetEncodedImmediate(immediate >> 16); } } break; case I64: { bool is_valid = true; uint32_t encoding = 0; if (neon_imm.IsInteger32()) { uint32_t immediate = neon_imm.GetImmediate<uint32_t>(); uint32_t mask = 0xff000000; for (uint32_t set_bit = 1 << 3; set_bit != 0; set_bit >>= 1) { if ((immediate & mask) == mask) { encoding |= set_bit; } else if ((immediate & mask) != 0) { is_valid = false; break; } mask >>= 8; } } else { uint64_t immediate = neon_imm.GetImmediate<uint64_t>(); uint64_t mask = UINT64_C(0xff) << 56; for (uint32_t set_bit = 1 << 7; set_bit != 0; set_bit >>= 1) { if ((immediate & mask) == mask) { encoding |= set_bit; } else if ((immediate & mask) != 0) { is_valid = false; break; } mask >>= 8; } } if (is_valid) { SetEncodingValue(0x1e); SetEncodedImmediate(encoding); } break; } default: break; } } else { switch (dt.GetValue()) { case F32: if (neon_imm.IsFloat() || neon_imm.IsDouble()) { ImmediateVFP vfp(neon_imm.GetImmediate<float>()); if (vfp.IsValid()) { SetEncodingValue(0xf); SetEncodedImmediate(vfp.GetEncodingValue()); } } break; default: break; } } } DataType ImmediateVmov::DecodeDt(uint32_t cmode) { switch (cmode & 0xf) { case 0x0: case 0x2: case 0x4: case 0x6: case 0xc: case 0xd: return I32; case 0x8: case 0xa: return I16; case 0xe: return ((cmode & 0x10) == 0) ? I8 : I64; case 0xf: if ((cmode & 0x10) == 0) return F32; break; default: break; } VIXL_UNREACHABLE(); return kDataTypeValueInvalid; } NeonImmediate ImmediateVmov::DecodeImmediate(uint32_t cmode, uint32_t immediate) { switch (cmode & 0xf) { case 0x8: case 0x0: return immediate; case 0x2: case 0xa: return immediate << 8; case 0x4: return immediate << 16; case 0x6: return immediate << 24; case 0xc: return (immediate << 8) | 0xff; case 0xd: return (immediate << 16) | 0xffff; case 0xe: { if (cmode == 0x1e) { uint64_t encoding = 0; for (uint32_t set_bit = 1 << 7; set_bit != 0; set_bit >>= 1) { encoding <<= 8; if ((immediate & set_bit) != 0) { encoding |= 0xff; } } return encoding; } else { return immediate; } } case 0xf: { return ImmediateVFP::Decode<float>(immediate); } default: break; } VIXL_UNREACHABLE(); return 0; } ImmediateVmvn::ImmediateVmvn(DataType dt, const NeonImmediate& neon_imm) { if (neon_imm.IsInteger32()) { uint32_t immediate = neon_imm.GetImmediate<uint32_t>(); switch (dt.GetValue()) { case I16: if ((immediate & ~0xff) == 0) { SetEncodingValue(0x8); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0xa); SetEncodedImmediate(immediate >> 8); } break; case I32: if ((immediate & ~0xff) == 0) { SetEncodingValue(0x0); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0x2); SetEncodedImmediate(immediate >> 8); } else if ((immediate & ~0xff0000) == 0) { SetEncodingValue(0x4); SetEncodedImmediate(immediate >> 16); } else if ((immediate & ~0xff000000) == 0) { SetEncodingValue(0x6); SetEncodedImmediate(immediate >> 24); } else if ((immediate & ~0xff00) == 0xff) { SetEncodingValue(0xc); SetEncodedImmediate(immediate >> 8); } else if ((immediate & ~0xff0000) == 0xffff) { SetEncodingValue(0xd); SetEncodedImmediate(immediate >> 16); } break; default: break; } } } DataType ImmediateVmvn::DecodeDt(uint32_t cmode) { switch (cmode) { case 0x0: case 0x2: case 0x4: case 0x6: case 0xc: case 0xd: return I32; case 0x8: case 0xa: return I16; default: break; } VIXL_UNREACHABLE(); return kDataTypeValueInvalid; } NeonImmediate ImmediateVmvn::DecodeImmediate(uint32_t cmode, uint32_t immediate) { switch (cmode) { case 0x0: case 0x8: return immediate; case 0x2: case 0xa: return immediate << 8; case 0x4: return immediate << 16; case 0x6: return immediate << 24; case 0xc: return (immediate << 8) | 0xff; case 0xd: return (immediate << 16) | 0xffff; default: break; } VIXL_UNREACHABLE(); return 0; } ImmediateVorr::ImmediateVorr(DataType dt, const NeonImmediate& neon_imm) { if (neon_imm.IsInteger32()) { uint32_t immediate = neon_imm.GetImmediate<uint32_t>(); if (dt.GetValue() == I16) { if ((immediate & ~0xff) == 0) { SetEncodingValue(0x9); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0xb); SetEncodedImmediate(immediate >> 8); } } else if (dt.GetValue() == I32) { if ((immediate & ~0xff) == 0) { SetEncodingValue(0x1); SetEncodedImmediate(immediate); } else if ((immediate & ~0xff00) == 0) { SetEncodingValue(0x3); SetEncodedImmediate(immediate >> 8); } else if ((immediate & ~0xff0000) == 0) { SetEncodingValue(0x5); SetEncodedImmediate(immediate >> 16); } else if ((immediate & ~0xff000000) == 0) { SetEncodingValue(0x7); SetEncodedImmediate(immediate >> 24); } } } } DataType ImmediateVorr::DecodeDt(uint32_t cmode) { switch (cmode) { case 0x1: case 0x3: case 0x5: case 0x7: return I32; case 0x9: case 0xb: return I16; default: break; } VIXL_UNREACHABLE(); return kDataTypeValueInvalid; } NeonImmediate ImmediateVorr::DecodeImmediate(uint32_t cmode, uint32_t immediate) { switch (cmode) { case 0x1: case 0x9: return immediate; case 0x3: case 0xb: return immediate << 8; case 0x5: return immediate << 16; case 0x7: return immediate << 24; default: break; } VIXL_UNREACHABLE(); return 0; } // MemOperand std::ostream& operator<<(std::ostream& os, const MemOperand& operand) { os << "[" << operand.GetBaseRegister(); if (operand.GetAddrMode() == PostIndex) { os << "]"; } if (operand.IsImmediate()) { if ((operand.GetOffsetImmediate() != 0) || operand.GetSign().IsMinus() || ((operand.GetAddrMode() != Offset) && !operand.IsRegisterOnly())) { if (operand.GetOffsetImmediate() == 0) { os << ", #" << operand.GetSign() << operand.GetOffsetImmediate(); } else { os << ", #" << operand.GetOffsetImmediate(); } } } else if (operand.IsPlainRegister()) { os << ", " << operand.GetSign() << operand.GetOffsetRegister(); } else if (operand.IsShiftedRegister()) { os << ", " << operand.GetSign() << operand.GetOffsetRegister() << ImmediateShiftOperand(operand.GetShift(), operand.GetShiftAmount()); } else { VIXL_UNREACHABLE(); return os; } if (operand.GetAddrMode() == Offset) { os << "]"; } else if (operand.GetAddrMode() == PreIndex) { os << "]!"; } return os; } std::ostream& operator<<(std::ostream& os, const AlignedMemOperand& operand) { os << "[" << operand.GetBaseRegister() << operand.GetAlignment() << "]"; if (operand.GetAddrMode() == PostIndex) { if (operand.IsPlainRegister()) { os << ", " << operand.GetOffsetRegister(); } else { os << "!"; } } return os; } } // namespace aarch32 } // namespace vixl