Duckstation/dep/vixl/src/aarch32/operands-aarch32.cc
2019-12-04 20:32:38 +10:00

564 lines
15 KiB
C++

// Copyright 2017, 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 <inttypes.h>
#include <stdint.h>
}
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#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.IsRegisterOnly()) return 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