early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <algorithm>
#include <functional>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "frontend/decoder/decoder_detail.h"
#include "frontend/decoder/matcher.h"
namespace Dynarmic::A64 {
template <typename Visitor>
using Matcher = Decoder::Matcher<Visitor, u32>;
template <typename Visitor>
using DecodeTable = std::array<std::vector<Matcher<Visitor>>, 0x1000>;
namespace detail {
inline size_t ToFastLookupIndex(u32 instruction) {
return ((instruction >> 10) & 0x00F) | ((instruction >> 18) & 0xFF0);
}
} // namespace detail
template <typename Visitor>
DecodeTable<Visitor> GetDecodeTable() {
std::vector<Matcher<Visitor>> list = {
#define INST(fn, name, bitstring) Decoder::detail::detail<Matcher<Visitor>>::GetMatcher(&Visitor::fn, name, bitstring),
#include "a64.inc"
#undef INST
};
std::stable_sort(list.begin(), list.end(), [](const auto& matcher1, const auto& matcher2) {
// If a matcher has more bits in its mask it is more specific, so it should come first.
return Common::BitCount(matcher1.GetMask()) > Common::BitCount(matcher2.GetMask());
});
// Exceptions to the above rule of thumb.
const std::set<std::string> comes_first {
"MOVI, MVNI, ORR, BIC (vector, immediate)",
"FMOV (vector, immediate)",
"Unallocated SIMD modified immediate",
};
std::stable_partition(list.begin(), list.end(), [&](const auto& matcher) {
return comes_first.count(matcher.GetName()) > 0;
});
DecodeTable<Visitor> table{};
for (size_t i = 0; i < table.size(); ++i) {
for (auto matcher : list) {
const auto expect = detail::ToFastLookupIndex(matcher.GetExpected());
const auto mask = detail::ToFastLookupIndex(matcher.GetMask());
if ((i & mask) == expect) {
table[i].push_back(matcher);
}
}
}
return table;
}
template<typename Visitor>
std::optional<std::reference_wrapper<const Matcher<Visitor>>> Decode(u32 instruction) {
static const auto table = GetDecodeTable<Visitor>();
const auto matches_instruction = [instruction](const auto& matcher) { return matcher.Matches(instruction); };
const auto& subtable = table[detail::ToFastLookupIndex(instruction)];
auto iter = std::find_if(subtable.begin(), subtable.end(), matches_instruction);
return iter != subtable.end() ? std::optional<std::reference_wrapper<const Matcher<Visitor>>>(*iter) : std::nullopt;
}
} // namespace Dynarmic::A64

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/assert.h"
#include "frontend/A64/ir_emitter.h"
#include "frontend/ir/opcodes.h"
namespace Dynarmic::A64 {
using Opcode = IR::Opcode;
u64 IREmitter::PC() const {
return current_location->PC();
}
u64 IREmitter::AlignPC(size_t alignment) const {
const u64 pc = PC();
return static_cast<u64>(pc - pc % alignment);
}
void IREmitter::SetCheckBit(const IR::U1& value) {
Inst(Opcode::A64SetCheckBit, value);
}
IR::U1 IREmitter::GetCFlag() {
return Inst<IR::U1>(Opcode::A64GetCFlag);
}
IR::U32 IREmitter::GetNZCVRaw() {
return Inst<IR::U32>(Opcode::A64GetNZCVRaw);
}
void IREmitter::SetNZCVRaw(IR::U32 value) {
Inst(Opcode::A64SetNZCVRaw, value);
}
void IREmitter::SetNZCV(const IR::NZCV& nzcv) {
Inst(Opcode::A64SetNZCV, nzcv);
}
void IREmitter::OrQC(const IR::U1& value) {
Inst(Opcode::A64OrQC, value);
}
void IREmitter::CallSupervisor(u32 imm) {
Inst(Opcode::A64CallSupervisor, Imm32(imm));
}
void IREmitter::ExceptionRaised(Exception exception) {
Inst(Opcode::A64ExceptionRaised, Imm64(PC()), Imm64(static_cast<u64>(exception)));
}
void IREmitter::DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value) {
Inst(Opcode::A64DataCacheOperationRaised, Imm64(static_cast<u64>(op)), value);
}
void IREmitter::DataSynchronizationBarrier() {
Inst(Opcode::A64DataSynchronizationBarrier);
}
void IREmitter::DataMemoryBarrier() {
Inst(Opcode::A64DataMemoryBarrier);
}
void IREmitter::InstructionSynchronizationBarrier() {
Inst(Opcode::A64InstructionSynchronizationBarrier);
}
IR::U32 IREmitter::GetCNTFRQ() {
return Inst<IR::U32>(Opcode::A64GetCNTFRQ);
}
IR::U64 IREmitter::GetCNTPCT() {
return Inst<IR::U64>(Opcode::A64GetCNTPCT);
}
IR::U32 IREmitter::GetCTR() {
return Inst<IR::U32>(Opcode::A64GetCTR);
}
IR::U32 IREmitter::GetDCZID() {
return Inst<IR::U32>(Opcode::A64GetDCZID);
}
IR::U64 IREmitter::GetTPIDR() {
return Inst<IR::U64>(Opcode::A64GetTPIDR);
}
void IREmitter::SetTPIDR(const IR::U64& value) {
Inst(Opcode::A64SetTPIDR, value);
}
IR::U64 IREmitter::GetTPIDRRO() {
return Inst<IR::U64>(Opcode::A64GetTPIDRRO);
}
void IREmitter::ClearExclusive() {
Inst(Opcode::A64ClearExclusive);
}
IR::U8 IREmitter::ReadMemory8(const IR::U64& vaddr) {
return Inst<IR::U8>(Opcode::A64ReadMemory8, vaddr);
}
IR::U16 IREmitter::ReadMemory16(const IR::U64& vaddr) {
return Inst<IR::U16>(Opcode::A64ReadMemory16, vaddr);
}
IR::U32 IREmitter::ReadMemory32(const IR::U64& vaddr) {
return Inst<IR::U32>(Opcode::A64ReadMemory32, vaddr);
}
IR::U64 IREmitter::ReadMemory64(const IR::U64& vaddr) {
return Inst<IR::U64>(Opcode::A64ReadMemory64, vaddr);
}
IR::U128 IREmitter::ReadMemory128(const IR::U64& vaddr) {
return Inst<IR::U128>(Opcode::A64ReadMemory128, vaddr);
}
IR::U8 IREmitter::ExclusiveReadMemory8(const IR::U64& vaddr) {
return Inst<IR::U8>(Opcode::A64ExclusiveReadMemory8, vaddr);
}
IR::U16 IREmitter::ExclusiveReadMemory16(const IR::U64& vaddr) {
return Inst<IR::U16>(Opcode::A64ExclusiveReadMemory16, vaddr);
}
IR::U32 IREmitter::ExclusiveReadMemory32(const IR::U64& vaddr) {
return Inst<IR::U32>(Opcode::A64ExclusiveReadMemory32, vaddr);
}
IR::U64 IREmitter::ExclusiveReadMemory64(const IR::U64& vaddr) {
return Inst<IR::U64>(Opcode::A64ExclusiveReadMemory64, vaddr);
}
IR::U128 IREmitter::ExclusiveReadMemory128(const IR::U64& vaddr) {
return Inst<IR::U128>(Opcode::A64ExclusiveReadMemory128, vaddr);
}
void IREmitter::WriteMemory8(const IR::U64& vaddr, const IR::U8& value) {
Inst(Opcode::A64WriteMemory8, vaddr, value);
}
void IREmitter::WriteMemory16(const IR::U64& vaddr, const IR::U16& value) {
Inst(Opcode::A64WriteMemory16, vaddr, value);
}
void IREmitter::WriteMemory32(const IR::U64& vaddr, const IR::U32& value) {
Inst(Opcode::A64WriteMemory32, vaddr, value);
}
void IREmitter::WriteMemory64(const IR::U64& vaddr, const IR::U64& value) {
Inst(Opcode::A64WriteMemory64, vaddr, value);
}
void IREmitter::WriteMemory128(const IR::U64& vaddr, const IR::U128& value) {
Inst(Opcode::A64WriteMemory128, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory8, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory16, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory32, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory64, vaddr, value);
}
IR::U32 IREmitter::ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value) {
return Inst<IR::U32>(Opcode::A64ExclusiveWriteMemory128, vaddr, value);
}
IR::U32 IREmitter::GetW(Reg reg) {
if (reg == Reg::ZR)
return Imm32(0);
return Inst<IR::U32>(Opcode::A64GetW, IR::Value(reg));
}
IR::U64 IREmitter::GetX(Reg reg) {
if (reg == Reg::ZR)
return Imm64(0);
return Inst<IR::U64>(Opcode::A64GetX, IR::Value(reg));
}
IR::U128 IREmitter::GetS(Vec vec) {
return Inst<IR::U128>(Opcode::A64GetS, IR::Value(vec));
}
IR::U128 IREmitter::GetD(Vec vec) {
return Inst<IR::U128>(Opcode::A64GetD, IR::Value(vec));
}
IR::U128 IREmitter::GetQ(Vec vec) {
return Inst<IR::U128>(Opcode::A64GetQ, IR::Value(vec));
}
IR::U64 IREmitter::GetSP() {
return Inst<IR::U64>(Opcode::A64GetSP);
}
IR::U32 IREmitter::GetFPCR() {
return Inst<IR::U32>(Opcode::A64GetFPCR);
}
IR::U32 IREmitter::GetFPSR() {
return Inst<IR::U32>(Opcode::A64GetFPSR);
}
void IREmitter::SetW(const Reg reg, const IR::U32& value) {
if (reg == Reg::ZR)
return;
Inst(Opcode::A64SetW, IR::Value(reg), value);
}
void IREmitter::SetX(const Reg reg, const IR::U64& value) {
if (reg == Reg::ZR)
return;
Inst(Opcode::A64SetX, IR::Value(reg), value);
}
void IREmitter::SetS(const Vec vec, const IR::U128& value) {
Inst(Opcode::A64SetS, IR::Value(vec), value);
}
void IREmitter::SetD(const Vec vec, const IR::U128& value) {
Inst(Opcode::A64SetD, IR::Value(vec), value);
}
void IREmitter::SetQ(const Vec vec, const IR::U128& value) {
Inst(Opcode::A64SetQ, IR::Value(vec), value);
}
void IREmitter::SetSP(const IR::U64& value) {
Inst(Opcode::A64SetSP, value);
}
void IREmitter::SetFPCR(const IR::U32& value) {
Inst(Opcode::A64SetFPCR, value);
}
void IREmitter::SetFPSR(const IR::U32& value) {
Inst(Opcode::A64SetFPSR, value);
}
void IREmitter::SetPC(const IR::U64& value) {
Inst(Opcode::A64SetPC, value);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,97 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <optional>
#include <dynarmic/A64/config.h>
#include "common/common_types.h"
#include "frontend/A64/location_descriptor.h"
#include "frontend/A64/types.h"
#include "frontend/ir/ir_emitter.h"
#include "frontend/ir/value.h"
namespace Dynarmic::A64 {
/**
* Convenience class to construct a basic block of the intermediate representation.
* `block` is the resulting block.
* The user of this class updates `current_location` as appropriate.
*/
class IREmitter : public IR::IREmitter {
public:
explicit IREmitter(IR::Block& block) : IR::IREmitter(block) {}
explicit IREmitter(IR::Block& block, LocationDescriptor descriptor) : IR::IREmitter(block), current_location(descriptor) {}
std::optional<LocationDescriptor> current_location;
u64 PC() const;
u64 AlignPC(size_t alignment) const;
void SetCheckBit(const IR::U1& value);
IR::U1 GetCFlag();
IR::U32 GetNZCVRaw();
void SetNZCVRaw(IR::U32 value);
void SetNZCV(const IR::NZCV& nzcv);
void OrQC(const IR::U1& value);
void CallSupervisor(u32 imm);
void ExceptionRaised(Exception exception);
void DataCacheOperationRaised(DataCacheOperation op, const IR::U64& value);
void DataSynchronizationBarrier();
void DataMemoryBarrier();
void InstructionSynchronizationBarrier();
IR::U32 GetCNTFRQ();
IR::U64 GetCNTPCT(); // TODO: Ensure sub-basic-block cycle counts are updated before this.
IR::U32 GetCTR();
IR::U32 GetDCZID();
IR::U64 GetTPIDR();
IR::U64 GetTPIDRRO();
void SetTPIDR(const IR::U64& value);
void ClearExclusive();
IR::U8 ReadMemory8(const IR::U64& vaddr);
IR::U16 ReadMemory16(const IR::U64& vaddr);
IR::U32 ReadMemory32(const IR::U64& vaddr);
IR::U64 ReadMemory64(const IR::U64& vaddr);
IR::U128 ReadMemory128(const IR::U64& vaddr);
IR::U8 ExclusiveReadMemory8(const IR::U64& vaddr);
IR::U16 ExclusiveReadMemory16(const IR::U64& vaddr);
IR::U32 ExclusiveReadMemory32(const IR::U64& vaddr);
IR::U64 ExclusiveReadMemory64(const IR::U64& vaddr);
IR::U128 ExclusiveReadMemory128(const IR::U64& vaddr);
void WriteMemory8(const IR::U64& vaddr, const IR::U8& value);
void WriteMemory16(const IR::U64& vaddr, const IR::U16& value);
void WriteMemory32(const IR::U64& vaddr, const IR::U32& value);
void WriteMemory64(const IR::U64& vaddr, const IR::U64& value);
void WriteMemory128(const IR::U64& vaddr, const IR::U128& value);
IR::U32 ExclusiveWriteMemory8(const IR::U64& vaddr, const IR::U8& value);
IR::U32 ExclusiveWriteMemory16(const IR::U64& vaddr, const IR::U16& value);
IR::U32 ExclusiveWriteMemory32(const IR::U64& vaddr, const IR::U32& value);
IR::U32 ExclusiveWriteMemory64(const IR::U64& vaddr, const IR::U64& value);
IR::U32 ExclusiveWriteMemory128(const IR::U64& vaddr, const IR::U128& value);
IR::U32 GetW(Reg source_reg);
IR::U64 GetX(Reg source_reg);
IR::U128 GetS(Vec source_vec);
IR::U128 GetD(Vec source_vec);
IR::U128 GetQ(Vec source_vec);
IR::U64 GetSP();
IR::U32 GetFPCR();
IR::U32 GetFPSR();
void SetW(Reg dest_reg, const IR::U32& value);
void SetX(Reg dest_reg, const IR::U64& value);
void SetS(Vec dest_vec, const IR::U128& value);
void SetD(Vec dest_vec, const IR::U128& value);
void SetQ(Vec dest_vec, const IR::U128& value);
void SetSP(const IR::U64& value);
void SetFPCR(const IR::U32& value);
void SetFPSR(const IR::U32& value);
void SetPC(const IR::U64& value);
};
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,18 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <ostream>
#include <fmt/format.h>
#include "frontend/A64/location_descriptor.h"
namespace Dynarmic::A64 {
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor) {
o << fmt::format("{{{}, {}{}}}", descriptor.PC(), descriptor.FPCR().Value(), descriptor.SingleStepping() ? ", step" : "");
return o;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,108 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <functional>
#include <iosfwd>
#include <tuple>
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/fp/fpcr.h"
#include "frontend/ir/location_descriptor.h"
namespace Dynarmic::A64 {
/**
* LocationDescriptor describes the location of a basic block.
* The location is not solely based on the PC because other flags influence the way
* instructions should be translated.
*/
class LocationDescriptor {
public:
static constexpr size_t pc_bit_count = 56;
static constexpr u64 pc_mask = Common::Ones<u64>(pc_bit_count);
static constexpr u32 fpcr_mask = 0x07C8'0000;
static constexpr size_t fpcr_shift = 37;
static constexpr size_t single_stepping_bit = 57;
static_assert((pc_mask & (u64(fpcr_mask) << fpcr_shift) & (u64(1) << single_stepping_bit)) == 0);
LocationDescriptor(u64 pc, FP::FPCR fpcr, bool single_stepping = false)
: pc(pc & pc_mask), fpcr(fpcr.Value() & fpcr_mask), single_stepping(single_stepping)
{}
explicit LocationDescriptor(const IR::LocationDescriptor& o)
: pc(o.Value() & pc_mask)
, fpcr((o.Value() >> fpcr_shift) & fpcr_mask)
, single_stepping(Common::Bit<single_stepping_bit>(o.Value()))
{}
u64 PC() const { return Common::SignExtend<pc_bit_count>(pc); }
FP::FPCR FPCR() const { return fpcr; }
bool SingleStepping() const { return single_stepping; }
bool operator == (const LocationDescriptor& o) const {
return std::tie(pc, fpcr, single_stepping) == std::tie(o.pc, o.fpcr, single_stepping);
}
bool operator != (const LocationDescriptor& o) const {
return !operator==(o);
}
LocationDescriptor SetPC(u64 new_pc) const {
return LocationDescriptor(new_pc, fpcr, single_stepping);
}
LocationDescriptor AdvancePC(int amount) const {
return LocationDescriptor(static_cast<u64>(pc + amount), fpcr, single_stepping);
}
LocationDescriptor SetSingleStepping(bool new_single_stepping) const {
return LocationDescriptor(pc, fpcr, new_single_stepping);
}
u64 UniqueHash() const noexcept {
// This value MUST BE UNIQUE.
// This calculation has to match up with EmitTerminalPopRSBHint
const u64 fpcr_u64 = static_cast<u64>(fpcr.Value()) << fpcr_shift;
const u64 single_stepping_u64 = static_cast<u64>(single_stepping) << single_stepping_bit;
return pc | fpcr_u64 | single_stepping_u64;
}
operator IR::LocationDescriptor() const {
return IR::LocationDescriptor{UniqueHash()};
}
private:
u64 pc; ///< Current program counter value.
FP::FPCR fpcr; ///< Floating point control register.
bool single_stepping;
};
/**
* Provides a string representation of a LocationDescriptor.
*
* @param o Output stream
* @param descriptor The descriptor to get a string representation of
*/
std::ostream& operator<<(std::ostream& o, const LocationDescriptor& descriptor);
} // namespace Dynarmic::A64
namespace std {
template <>
struct less<Dynarmic::A64::LocationDescriptor> {
bool operator()(const Dynarmic::A64::LocationDescriptor& x, const Dynarmic::A64::LocationDescriptor& y) const noexcept {
return x.UniqueHash() < y.UniqueHash();
}
};
template <>
struct hash<Dynarmic::A64::LocationDescriptor> {
size_t operator()(const Dynarmic::A64::LocationDescriptor& x) const noexcept {
return std::hash<u64>()(x.UniqueHash());
}
};
} // namespace std

View File

@@ -0,0 +1,128 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::B_cond(Imm<19> imm19, Cond cond) {
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::If{cond, cond_pass, cond_fail});
return false;
}
bool TranslatorVisitor::B_uncond(Imm<26> imm26) {
const s64 offset = concatenate(imm26, Imm<2>{0}).SignExtend<s64>();
const u64 target = ir.PC() + offset;
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
return false;
}
bool TranslatorVisitor::BL(Imm<26> imm26) {
const s64 offset = concatenate(imm26, Imm<2>{0}).SignExtend<s64>();
X(64, Reg::R30, ir.Imm64(ir.PC() + 4));
ir.PushRSB(ir.current_location->AdvancePC(4));
const u64 target = ir.PC() + offset;
ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)});
return false;
}
bool TranslatorVisitor::BLR(Reg Rn) {
const auto target = X(64, Rn);
X(64, Reg::R30, ir.Imm64(ir.PC() + 4));
ir.PushRSB(ir.current_location->AdvancePC(4));
ir.SetPC(target);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
bool TranslatorVisitor::BR(Reg Rn) {
const auto target = X(64, Rn);
ir.SetPC(target);
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
}
bool TranslatorVisitor::RET(Reg Rn) {
const auto target = X(64, Rn);
ir.SetPC(target);
ir.SetTerm(IR::Term::PopRSBHint{});
return false;
}
bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) {
const size_t datasize = sf ? 64 : 32;
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const IR::U32U64 operand1 = X(datasize, Rt);
ir.SetCheckBit(ir.IsZero(operand1));
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) {
const size_t datasize = sf ? 64 : 32;
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const IR::U32U64 operand1 = X(datasize, Rt);
ir.SetCheckBit(ir.IsZero(operand1));
const u64 target = ir.PC() + offset;
const auto cond_pass = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_fail = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail});
return false;
}
bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
const size_t datasize = b5 == 1 ? 64 : 32;
const u8 bit_pos = concatenate(b5, b40).ZeroExtend<u8>();
const s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend<s64>();
const auto operand = X(datasize, Rt);
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
const u64 target = ir.PC() + offset;
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
return false;
}
bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) {
const size_t datasize = b5 == 1 ? 64 : 32;
const u8 bit_pos = concatenate(b5, b40).ZeroExtend<u8>();
const s64 offset = concatenate(imm14, Imm<2>{0}).SignExtend<s64>();
const auto operand = X(datasize, Rt);
ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos)));
const u64 target = ir.PC() + offset;
const auto cond_1 = IR::Term::LinkBlock{ir.current_location->SetPC(target)};
const auto cond_0 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)};
ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0});
return false;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,330 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::ADD_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Add(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADDS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Add(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Sub(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUBS_imm(bool sf, Imm<2> shift, Imm<12> imm12, Reg Rn, Reg Rd) {
u64 imm;
switch (shift.ZeroExtend()) {
case 0b00:
imm = imm12.ZeroExtend<u64>();
break;
case 0b01:
imm = imm12.ZeroExtend<u64>() << 12;
break;
default:
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto result = ir.Sub(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADD_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Add(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Add(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Sub(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUBS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (shift == 0b11) {
return ReservedValue();
}
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Sub(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADD_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Add(operand1, operand2);
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ADDS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Add(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SUB_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Sub(operand1, operand2);
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::SUBS_ext(bool sf, Reg Rm, Imm<3> option, Imm<3> imm3, Reg Rn, Reg Rd) {
const u8 shift = imm3.ZeroExtend<u8>();
if (shift > 4) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = Rn == Reg::SP ? SP(datasize) : IR::U32U64(X(datasize, Rn));
const auto operand2 = ExtendReg(datasize, Rm, option, shift);
const auto result = ir.Sub(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.AddWithCarry(operand1, operand2, ir.GetCFlag());
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ADCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.AddWithCarry(operand1, operand2, ir.GetCFlag());
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SBC(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.SubWithCarry(operand1, operand2, ir.GetCFlag());
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SBCS(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const auto result = ir.SubWithCarry(operand1, operand2, ir.GetCFlag());
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,155 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::SBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (sf && !N) {
return ReservedValue();
}
if (!sf && (N || immr.Bit<5>() || imms.Bit<5>())) {
return ReservedValue();
}
const u8 R = immr.ZeroExtend<u8>();
const u8 S = imms.ZeroExtend<u8>();
const auto masks = DecodeBitMasks(N, imms, immr, false);
if (!masks) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto src = X(datasize, Rn);
auto bot = ir.And(ir.RotateRight(src, ir.Imm8(R)), I(datasize, masks->wmask));
auto top = ir.ReplicateBit(src, S);
top = ir.And(top, I(datasize, ~masks->tmask));
bot = ir.And(bot, I(datasize, masks->tmask));
X(datasize, Rd, ir.Or(top, bot));
return true;
}
bool TranslatorVisitor::BFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (sf && !N) {
return ReservedValue();
}
if (!sf && (N || immr.Bit<5>() || imms.Bit<5>())) {
return ReservedValue();
}
const u8 R = immr.ZeroExtend<u8>();
const auto masks = DecodeBitMasks(N, imms, immr, false);
if (!masks) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto dst = X(datasize, Rd);
const auto src = X(datasize, Rn);
const auto bot = ir.Or(ir.And(dst, I(datasize, ~masks->wmask)), ir.And(ir.RotateRight(src, ir.Imm8(R)), I(datasize, masks->wmask)));
X(datasize, Rd, ir.Or(ir.And(dst, I(datasize, ~masks->tmask)), ir.And(bot, I(datasize, masks->tmask))));
return true;
}
bool TranslatorVisitor::UBFM(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (sf && !N) {
return ReservedValue();
}
if (!sf && (N || immr.Bit<5>() || imms.Bit<5>())) {
return ReservedValue();
}
const u8 R = immr.ZeroExtend<u8>();
const auto masks = DecodeBitMasks(N, imms, immr, false);
if (!masks) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto src = X(datasize, Rn);
const auto bot = ir.And(ir.RotateRight(src, ir.Imm8(R)), I(datasize, masks->wmask));
X(datasize, Rd, ir.And(bot, I(datasize, masks->tmask)));
return true;
}
bool TranslatorVisitor::ASR_1(Imm<5> immr, Reg Rn, Reg Rd) {
const auto src = X(32, Rn);
const auto result = ir.ArithmeticShiftRightMasked(src, ir.Imm32(immr.ZeroExtend<u32>()));
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::ASR_2(Imm<6> immr, Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.ArithmeticShiftRightMasked(src, ir.Imm64(immr.ZeroExtend<u64>()));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SXTB_1(Reg Rn, Reg Rd) {
const auto src = X(32, Rn);
const auto result = ir.SignExtendToWord(ir.LeastSignificantByte(src));
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::SXTB_2(Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.SignExtendToLong(ir.LeastSignificantByte(src));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SXTH_1(Reg Rn, Reg Rd) {
const auto src = X(32, Rn);
const auto result = ir.SignExtendToWord(ir.LeastSignificantHalf(src));
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::SXTH_2(Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.SignExtendToLong(ir.LeastSignificantHalf(src));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SXTW(Reg Rn, Reg Rd) {
const auto src = X(64, Rn);
const auto result = ir.SignExtendToLong(ir.LeastSignificantWord(src));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::EXTR(bool sf, bool N, Reg Rm, Imm<6> imms, Reg Rn, Reg Rd) {
if (N != sf) {
return UnallocatedEncoding();
}
if (!sf && imms.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.ExtractRegister(m, n, ir.Imm8(imms.ZeroExtend<u8>()));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,62 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CCMN_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, operand2, ir.Imm1(0)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
bool TranslatorVisitor::CCMP_reg(bool sf, Reg Rm, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, ir.Not(operand2), ir.Imm1(1)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
bool TranslatorVisitor::CCMN_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = I(datasize, imm5.ZeroExtend<u32>());
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, operand2, ir.Imm1(0)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
bool TranslatorVisitor::CCMP_imm(bool sf, Imm<5> imm5, Cond cond, Reg Rn, Imm<4> nzcv) {
const size_t datasize = sf ? 64 : 32;
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = I(datasize, imm5.ZeroExtend<u32>());
const IR::NZCV then_flags = ir.NZCVFrom(ir.AddWithCarry(operand1, ir.Not(operand2), ir.Imm1(1)));
const IR::NZCV else_flags = ir.NZCVFromPackedFlags(ir.Imm32(flags));
ir.SetNZCV(ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,58 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CSEL(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CSINC(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, ir.Add(operand2, I(datasize, 1)));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CSINV(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, ir.Not(operand2));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CSNEG(bool sf, Reg Rm, Cond cond, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand1 = X(datasize, Rn);
const IR::U32U64 operand2 = X(datasize, Rm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, ir.Add(ir.Not(operand2), I(datasize, 1)));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,76 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CRC32(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) {
const u32 integral_size = sz.ZeroExtend();
if (sf && integral_size != 0b11) {
return UnallocatedEncoding();
}
if (!sf && integral_size == 0b11) {
return UnallocatedEncoding();
}
const IR::U32 result = [&] {
const size_t datasize = sf ? 64 : 32;
const IR::U32 accumulator = ir.GetW(Rn);
const IR::U32U64 data = X(datasize, Rm);
switch (integral_size) {
case 0b00:
return ir.CRC32ISO8(accumulator, data);
case 0b01:
return ir.CRC32ISO16(accumulator, data);
case 0b10:
return ir.CRC32ISO32(accumulator, data);
case 0b11:
default:
return ir.CRC32ISO64(accumulator, data);
}
}();
X(32, Rd, result);
return true;
}
bool TranslatorVisitor::CRC32C(bool sf, Reg Rm, Imm<2> sz, Reg Rn, Reg Rd) {
const u32 integral_size = sz.ZeroExtend();
if (sf && integral_size != 0b11) {
return UnallocatedEncoding();
}
if (!sf && integral_size == 0b11) {
return UnallocatedEncoding();
}
const IR::U32 result = [&] {
const size_t datasize = sf ? 64 : 32;
const IR::U32 accumulator = ir.GetW(Rn);
const IR::U32U64 data = X(datasize, Rm);
switch (integral_size) {
case 0b00:
return ir.CRC32Castagnoli8(accumulator, data);
case 0b01:
return ir.CRC32Castagnoli16(accumulator, data);
case 0b10:
return ir.CRC32Castagnoli32(accumulator, data);
case 0b11:
default:
return ir.CRC32Castagnoli64(accumulator, data);
}
}();
X(32, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,236 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::AND_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.And(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ORR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.Or(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::EOR_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.Eor(operand1, I(datasize, imm));
if (Rd == Reg::SP) {
SP(datasize, result);
} else {
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::ANDS_imm(bool sf, bool N, Imm<6> immr, Imm<6> imms, Reg Rn, Reg Rd) {
if (!sf && N) {
return ReservedValue();
}
u64 imm;
if (auto masks = DecodeBitMasks(N, imms, immr, true)) {
imm = masks->wmask;
} else {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const auto operand1 = X(datasize, Rn);
const auto result = ir.And(operand1, I(datasize, imm));
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::AND_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.And(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::BIC_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.And(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ORR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Or(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ORN_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.Or(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::EOR_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.Eor(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::EON(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.Eor(operand1, operand2);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ANDS_shift(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount));
const auto result = ir.And(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::BICS(bool sf, Imm<2> shift, Reg Rm, Imm<6> imm6, Reg Rn, Reg Rd) {
if (!sf && imm6.Bit<5>()) {
return ReservedValue();
}
const size_t datasize = sf ? 64 : 32;
const u8 shift_amount = imm6.ZeroExtend<u8>();
const auto operand1 = X(datasize, Rn);
const auto operand2 = ir.Not(ShiftReg(datasize, Rm, shift, ir.Imm8(shift_amount)));
const auto result = ir.And(operand1, operand2);
ir.SetNZCV(ir.NZCVFrom(result));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,100 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::MADD(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 a = X(datasize, Ra);
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.Add(a, ir.Mul(n, m));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::MSUB(bool sf, Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 a = X(datasize, Ra);
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.Sub(a, ir.Mul(n, m));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.SignExtendToLong(X(32, Rm));
const IR::U64 n = ir.SignExtendToLong(X(32, Rn));
const IR::U64 result = ir.Add(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.SignExtendToLong(X(32, Rm));
const IR::U64 n = ir.SignExtendToLong(X(32, Rn));
const IR::U64 result = ir.Sub(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::SMULH(Reg Rm, Reg Rn, Reg Rd) {
const IR::U64 m = X(64, Rm);
const IR::U64 n = X(64, Rn);
const IR::U64 result = ir.SignedMultiplyHigh(n, m);
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::UMADDL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.ZeroExtendToLong(X(32, Rm));
const IR::U64 n = ir.ZeroExtendToLong(X(32, Rn));
const IR::U64 result = ir.Add(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::UMSUBL(Reg Rm, Reg Ra, Reg Rn, Reg Rd) {
const IR::U64 a = X(64, Ra);
const IR::U64 m = ir.ZeroExtendToLong(X(32, Rm));
const IR::U64 n = ir.ZeroExtendToLong(X(32, Rn));
const IR::U64 result = ir.Sub(a, ir.Mul(n, m));
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::UMULH(Reg Rm, Reg Rn, Reg Rd) {
const IR::U64 m = X(64, Rm);
const IR::U64 n = X(64, Rn);
const IR::U64 result = ir.UnsignedMultiplyHigh(n, m);
X(64, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::ADR(Imm<2> immlo, Imm<19> immhi, Reg Rd) {
const u64 imm = concatenate(immhi, immlo).SignExtend<u64>();
const u64 base = ir.PC();
X(64, Rd, ir.Imm64(base + imm));
return true;
}
bool TranslatorVisitor::ADRP(Imm<2> immlo, Imm<19> immhi, Reg Rd) {
const u64 imm = concatenate(immhi, immlo).SignExtend<u64>() << 12;
const u64 base = ir.PC() & ~u64(0xFFF);
X(64, Rd, ir.Imm64(base + imm));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,138 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CLZ_int(bool sf, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 result = ir.CountLeadingZeros(operand);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::CLS_int(bool sf, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 result = ir.Sub(ir.CountLeadingZeros(ir.Eor(operand, ir.ArithmeticShiftRight(operand, ir.Imm8(u8(datasize))))), I(datasize, 1));
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::RBIT_int(bool sf, Reg Rn, Reg Rd) {
const auto rbit32 = [this](const IR::U32& operand) {
// x = (x & 0x55555555) << 1 | ((x >> 1) & 0x55555555);
const IR::U32 first_lsl = ir.LogicalShiftLeft(ir.And(operand, ir.Imm32(0x55555555)), ir.Imm8(1));
const IR::U32 first_lsr = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(1)), ir.Imm32(0x55555555));
const IR::U32 first = ir.Or(first_lsl, first_lsr);
// x = (x & 0x33333333) << 2 | ((x >> 2) & 0x33333333);
const IR::U32 second_lsl = ir.LogicalShiftLeft(ir.And(first, ir.Imm32(0x33333333)), ir.Imm8(2));
const IR::U32 second_lsr = ir.And(ir.LogicalShiftRight(first, ir.Imm8(2)), ir.Imm32(0x33333333));
const IR::U32 second = ir.Or(second_lsl, second_lsr);
// x = (x & 0x0F0F0F0F) << 4 | ((x >> 4) & 0x0F0F0F0F);
const IR::U32 third_lsl = ir.LogicalShiftLeft(ir.And(second, ir.Imm32(0x0F0F0F0F)), ir.Imm8(4));
const IR::U32 third_lsr = ir.And(ir.LogicalShiftRight(second, ir.Imm8(4)), ir.Imm32(0x0F0F0F0F));
const IR::U32 third = ir.Or(third_lsl, third_lsr);
// x = (x << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | (x >> 24);
const IR::U32 fourth_lsl = ir.Or(ir.LogicalShiftLeft(third, ir.Imm8(24)),
ir.LogicalShiftLeft(ir.And(third, ir.Imm32(0xFF00)), ir.Imm8(8)));
const IR::U32 fourth_lsr = ir.Or(ir.And(ir.LogicalShiftRight(third, ir.Imm8(8)), ir.Imm32(0xFF00)),
ir.LogicalShiftRight(third, ir.Imm8(24)));
return ir.Or(fourth_lsl, fourth_lsr);
};
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
if (sf) {
const IR::U32 lsw = rbit32(ir.LeastSignificantWord(operand));
const IR::U32 msw = rbit32(ir.MostSignificantWord(operand).result);
const IR::U64 result = ir.Pack2x32To1x64(msw, lsw);
X(datasize, Rd, result);
} else {
X(datasize, Rd, rbit32(operand));
}
return true;
}
bool TranslatorVisitor::REV(bool sf, bool opc_0, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
if (!sf && opc_0) return UnallocatedEncoding();
const IR::U32U64 operand = X(datasize, Rn);
if (sf) {
X(datasize, Rd, ir.ByteReverseDual(operand));
} else {
X(datasize, Rd, ir.ByteReverseWord(operand));
}
return true;
}
bool TranslatorVisitor::REV32_int(Reg Rn, Reg Rd) {
const IR::U64 operand = ir.GetX(Rn);
const IR::U32 lo = ir.ByteReverseWord(ir.LeastSignificantWord(operand));
const IR::U32 hi = ir.ByteReverseWord(ir.MostSignificantWord(operand).result);
const IR::U64 result = ir.Pack2x32To1x64(lo, hi);
X(64, Rd, result);
return true;
}
bool TranslatorVisitor::REV16_int(bool sf, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
if (sf) {
const IR::U64 operand = X(datasize, Rn);
const IR::U64 hihalf = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(8)), ir.Imm64(0x00FF00FF00FF00FF));
const IR::U64 lohalf = ir.And(ir.LogicalShiftLeft(operand, ir.Imm8(8)), ir.Imm64(0xFF00FF00FF00FF00));
const IR::U64 result = ir.Or(hihalf, lohalf);
X(datasize, Rd, result);
} else {
const IR::U32 operand = X(datasize, Rn);
const IR::U32 hihalf = ir.And(ir.LogicalShiftRight(operand, ir.Imm8(8)), ir.Imm32(0x00FF00FF));
const IR::U32 lohalf = ir.And(ir.LogicalShiftLeft(operand, ir.Imm8(8)), ir.Imm32(0xFF00FF00));
const IR::U32 result = ir.Or(hihalf, lohalf);
X(datasize, Rd, result);
}
return true;
}
bool TranslatorVisitor::UDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.UnsignedDiv(n,m);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::SDIV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 m = X(datasize, Rm);
const IR::U32U64 n = X(datasize, Rn);
const IR::U32U64 result = ir.SignedDiv(n,m);
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,58 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::LSLV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.LogicalShiftLeftMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::LSRV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.LogicalShiftRightMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::ASRV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.ArithmeticShiftRightMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::RORV(bool sf, Reg Rm, Reg Rn, Reg Rd) {
const size_t datasize = sf ? 64 : 32;
const IR::U32U64 operand = X(datasize, Rn);
const IR::U32U64 shift_amount = X(datasize, Rm);
const IR::U32U64 result = ir.RotateRightMasked(operand, shift_amount);
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::BRK([[maybe_unused]] Imm<16> imm16) {
ir.ExceptionRaised(Exception::Breakpoint);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
bool TranslatorVisitor::SVC(Imm<16> imm16) {
ir.PushRSB(ir.current_location->AdvancePC(4));
ir.SetPC(ir.Imm64(ir.current_location->PC() + 4));
ir.CallSupervisor(imm16.ZeroExtend());
ir.SetTerm(IR::Term::CheckHalt{IR::Term::PopRSBHint{}});
return false;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,38 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
bool FPCompare(TranslatorVisitor& v, Imm<2> type, Vec Vm, Vec Vn, bool exc_on_qnan, bool cmp_with_zero) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return v.UnallocatedEncoding();
}
const IR::U32U64 operand1 = v.V_scalar(*datasize, Vn);
IR::U32U64 operand2;
if (cmp_with_zero) {
operand2 = v.I(*datasize, 0);
} else {
operand2 = v.V_scalar(*datasize, Vm);
}
const auto nzcv = v.ir.FPCompare(operand1, operand2, exc_on_qnan);
v.ir.SetNZCV(nzcv);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::FCMP_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) {
return FPCompare(*this, type, Vm, Vn, false, cmp_with_zero);
}
bool TranslatorVisitor::FCMPE_float(Imm<2> type, Vec Vm, Vec Vn, bool cmp_with_zero) {
return FPCompare(*this, type, Vm, Vn, true, cmp_with_zero);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,35 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
bool FPCompare(TranslatorVisitor& v, Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv, bool exc_on_qnan) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return v.UnallocatedEncoding();
}
const u32 flags = nzcv.ZeroExtend<u32>() << 28;
const IR::U32U64 operand1 = v.V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = v.V_scalar(*datasize, Vm);
const IR::NZCV then_flags = v.ir.FPCompare(operand1, operand2, exc_on_qnan);
const IR::NZCV else_flags = v.ir.NZCVFromPackedFlags(v.ir.Imm32(flags));
v.ir.SetNZCV(v.ir.ConditionalSelect(cond, then_flags, else_flags));
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::FCCMP_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) {
return FPCompare(*this, type, Vm, cond, Vn, nzcv, false);
}
bool TranslatorVisitor::FCCMPE_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Imm<4> nzcv) {
return FPCompare(*this, type, Vm, cond, Vn, nzcv, true);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,24 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FCSEL_float(Imm<2> type, Vec Vm, Cond cond, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.ConditionalSelect(cond, operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,114 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::SCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const FP::RoundingMode rounding_mode = ir.current_location->FPCR().RMode();
const IR::U32U64 intval = X(intsize, Rn);
const IR::U32U64 fltval = [&]() -> IR::U32U64 {
switch (*fltsize) {
case 32:
return ir.FPSignedFixedToSingle(intval, fracbits, rounding_mode);
case 64:
return ir.FPSignedFixedToDouble(intval, fracbits, rounding_mode);
}
UNREACHABLE();
}();
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::UCVTF_float_fix(bool sf, Imm<2> type, Imm<6> scale, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const FP::RoundingMode rounding_mode = ir.current_location->FPCR().RMode();
const IR::U32U64 intval = X(intsize, Rn);
const IR::U32U64 fltval = [&]() -> IR::U32U64 {
switch (*fltsize) {
case 32:
return ir.FPUnsignedFixedToSingle(intval, fracbits, rounding_mode);
case 64:
return ir.FPUnsignedFixedToDouble(intval, fracbits, rounding_mode);
}
UNREACHABLE();
}();
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::FCVTZS_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const IR::U16U32U64 fltval = V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = ir.FPToFixedS32(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else if (intsize == 64) {
intval = ir.FPToFixedS64(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else {
UNREACHABLE();
}
X(intsize, Rd, intval);
return true;
}
bool TranslatorVisitor::FCVTZU_float_fix(bool sf, Imm<2> type, Imm<6> scale, Vec Vn, Reg Rd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return UnallocatedEncoding();
}
if (!sf && !scale.Bit<5>()) {
return UnallocatedEncoding();
}
const u8 fracbits = 64 - scale.ZeroExtend<u8>();
const IR::U16U32U64 fltval = V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = ir.FPToFixedU32(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else if (intsize == 64) {
intval = ir.FPToFixedU64(fltval, fracbits, FP::RoundingMode::TowardsZero);
} else {
UNREACHABLE();
}
X(intsize, Rd, intval);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,199 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/rounding_mode.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::SCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 intval = X(intsize, Rn);
IR::U32U64 fltval;
if (*fltsize == 32) {
fltval = ir.FPSignedFixedToSingle(intval, 0, ir.current_location->FPCR().RMode());
} else if (*fltsize == 64) {
fltval = ir.FPSignedFixedToDouble(intval, 0, ir.current_location->FPCR().RMode());
} else {
UNREACHABLE();
}
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::UCVTF_float_int(bool sf, Imm<2> type, Reg Rn, Vec Vd) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize || *fltsize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 intval = X(intsize, Rn);
IR::U32U64 fltval;
if (*fltsize == 32) {
fltval = ir.FPUnsignedFixedToSingle(intval, 0, ir.current_location->FPCR().RMode());
} else if (*fltsize == 64) {
fltval = ir.FPUnsignedFixedToDouble(intval, 0, ir.current_location->FPCR().RMode());
} else {
UNREACHABLE();
}
V_scalar(*fltsize, Vd, fltval);
return true;
}
bool TranslatorVisitor::FMOV_float_gen(bool sf, Imm<2> type, Imm<1> rmode_0, Imm<1> opc_0, size_t n, size_t d) {
// NOTE:
// opcode<2:1> == 0b11
// rmode<1> == 0b0
if (type == 0b10 && rmode_0 != 1) {
return UnallocatedEncoding();
}
const size_t intsize = sf ? 64 : 32;
size_t fltsize = [type] {
switch (type.ZeroExtend()) {
case 0b00:
return 32;
case 0b01:
return 64;
case 0b10:
return 128;
case 0b11:
return 16;
default:
UNREACHABLE();
}
}();
bool integer_to_float;
size_t part;
switch (rmode_0.ZeroExtend()) {
case 0b0:
if (fltsize != 16 && fltsize != intsize) {
return UnallocatedEncoding();
}
integer_to_float = opc_0 == 0b1;
part = 0;
break;
default:
case 0b1:
if (intsize != 64 || fltsize != 128) {
return UnallocatedEncoding();
}
integer_to_float = opc_0 == 0b1;
part = 1;
fltsize = 64;
break;
}
if (integer_to_float) {
const IR::U16U32U64 intval = X(fltsize, static_cast<Reg>(n));
Vpart_scalar(fltsize, static_cast<Vec>(d), part, intval);
} else {
const IR::UAny fltval = Vpart_scalar(fltsize, static_cast<Vec>(n), part);
const IR::U32U64 intval = ZeroExtend(fltval, intsize);
X(intsize, static_cast<Reg>(d), intval);
}
return true;
}
static bool FloaingPointConvertSignedInteger(TranslatorVisitor& v, bool sf, Imm<2> type, Vec Vn, Reg Rd, FP::RoundingMode rounding_mode) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return v.UnallocatedEncoding();
}
const IR::U16U32U64 fltval = v.V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = v.ir.FPToFixedS32(fltval, 0, rounding_mode);
} else if (intsize == 64) {
intval = v.ir.FPToFixedS64(fltval, 0, rounding_mode);
} else {
UNREACHABLE();
}
v.X(intsize, Rd, intval);
return true;
}
static bool FloaingPointConvertUnsignedInteger(TranslatorVisitor& v, bool sf, Imm<2> type, Vec Vn, Reg Rd, FP::RoundingMode rounding_mode) {
const size_t intsize = sf ? 64 : 32;
const auto fltsize = FPGetDataSize(type);
if (!fltsize) {
return v.UnallocatedEncoding();
}
const IR::U16U32U64 fltval = v.V_scalar(*fltsize, Vn);
IR::U32U64 intval;
if (intsize == 32) {
intval = v.ir.FPToFixedU32(fltval, 0, rounding_mode);
} else if (intsize == 64) {
intval = v.ir.FPToFixedU64(fltval, 0, rounding_mode);
} else {
UNREACHABLE();
}
v.X(intsize, Rd, intval);
return true;
}
bool TranslatorVisitor::FCVTNS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieEven);
}
bool TranslatorVisitor::FCVTNU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieEven);
}
bool TranslatorVisitor::FCVTZS_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTZU_float_int(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTAS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieAwayFromZero);
}
bool TranslatorVisitor::FCVTAU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::ToNearest_TieAwayFromZero);
}
bool TranslatorVisitor::FCVTPS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsPlusInfinity);
}
bool TranslatorVisitor::FCVTPU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsPlusInfinity);
}
bool TranslatorVisitor::FCVTMS_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertSignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsMinusInfinity);
}
bool TranslatorVisitor::FCVTMU_float(bool sf, Imm<2> type, Vec Vn, Reg Rd) {
return FloaingPointConvertUnsignedInteger(*this, sf, type, Vn, Rd, FP::RoundingMode::TowardsMinusInfinity);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,187 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FMOV_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operand = V_scalar(*datasize, Vn);
V_scalar(*datasize, Vd, operand);
return true;
}
bool TranslatorVisitor::FABS_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operand = V_scalar(*datasize, Vn);
const IR::U16U32U64 result = ir.FPAbs(operand);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNEG_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operand = V_scalar(*datasize, Vn);
const IR::U16U32U64 result = ir.FPNeg(operand);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FSQRT_float(Imm<2> type, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand = V_scalar(*datasize, Vn);
const IR::U32U64 result = ir.FPSqrt(operand);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMOV_float_imm(Imm<2> type, Imm<8> imm8, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
IR::UAny result = [&]() -> IR::UAny {
switch (*datasize) {
case 16: {
const u16 sign = imm8.Bit<7>() ? 1 : 0;
const u16 exp = (imm8.Bit<6>() ? 0b0'1100 : 0b1'0000) | imm8.Bits<4, 5, u16>();
const u16 fract = imm8.Bits<0, 3, u16>() << 6;
return ir.Imm16((sign << 15) | (exp << 10) | fract);
}
case 32: {
const u32 sign = imm8.Bit<7>() ? 1 : 0;
const u32 exp = (imm8.Bit<6>() ? 0b0111'1100 : 0b1000'0000) | imm8.Bits<4, 5, u32>();
const u32 fract = imm8.Bits<0, 3, u32>() << 19;
return ir.Imm32((sign << 31) | (exp << 23) | fract);
}
case 64:
default: {
const u64 sign = imm8.Bit<7>() ? 1 : 0;
const u64 exp = (imm8.Bit<6>() ? 0b011'1111'1100 : 0b100'0000'0000) | imm8.Bits<4, 5, u64>();
const u64 fract = imm8.Bits<0, 3, u64>() << 48;
return ir.Imm64((sign << 63) | (exp << 52) | fract);
}
}
}();
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FCVT_float(Imm<2> type, Imm<2> opc, Vec Vn, Vec Vd) {
if (type == opc) {
return UnallocatedEncoding();
}
const auto srcsize = FPGetDataSize(type);
const auto dstsize = FPGetDataSize(opc);
if (!srcsize || !dstsize) {
return UnallocatedEncoding();
}
const IR::UAny operand = V_scalar(*srcsize, Vn);
const auto rounding_mode = ir.current_location->FPCR().RMode();
IR::UAny result;
switch (*srcsize) {
case 16:
switch (*dstsize) {
case 32:
result = ir.FPHalfToSingle(operand, rounding_mode);
break;
case 64:
result = ir.FPHalfToDouble(operand, rounding_mode);
break;
}
break;
case 32:
switch (*dstsize) {
case 16:
result = ir.FPSingleToHalf(operand, rounding_mode);
break;
case 64:
result = ir.FPSingleToDouble(operand, rounding_mode);
break;
}
break;
case 64:
switch (*dstsize) {
case 16:
result = ir.FPDoubleToHalf(operand, rounding_mode);
break;
case 32:
result = ir.FPDoubleToSingle(operand, rounding_mode);
break;
}
break;
}
V_scalar(*dstsize, Vd, result);
return true;
}
static bool FloatingPointRoundToIntegral(TranslatorVisitor& v, Imm<2> type, Vec Vn, Vec Vd,
FP::RoundingMode rounding_mode, bool exact) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return v.UnallocatedEncoding();
}
const IR::U16U32U64 operand = v.V_scalar(*datasize, Vn);
const IR::U16U32U64 result = v.ir.FPRoundInt(operand, rounding_mode, exact);
v.V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FRINTN_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, false);
}
bool TranslatorVisitor::FRINTP_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, false);
}
bool TranslatorVisitor::FRINTM_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, false);
}
bool TranslatorVisitor::FRINTZ_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::TowardsZero, false);
}
bool TranslatorVisitor::FRINTA_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, false);
}
bool TranslatorVisitor::FRINTX_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, ir.current_location->FPCR().RMode(), true);
}
bool TranslatorVisitor::FRINTI_float(Imm<2> type, Vec Vn, Vec Vd) {
return FloatingPointRoundToIntegral(*this, type, Vn, Vd, ir.current_location->FPCR().RMode(), false);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,66 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(operanda, operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(operanda, ir.FPNeg(operand1), operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNMADD_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(ir.FPNeg(operanda), ir.FPNeg(operand1), operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNMSUB_float(Imm<2> type, Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize) {
return UnallocatedEncoding();
}
const IR::U16U32U64 operanda = V_scalar(*datasize, Va);
const IR::U16U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U16U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U16U32U64 result = ir.FPMulAdd(ir.FPNeg(operanda), operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,145 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::FMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMul(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FDIV_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPDiv(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FADD_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPAdd(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FSUB_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPSub(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMAX_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMax(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMIN_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMin(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMAXNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMaxNumeric(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FMINNM_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPMinNumeric(operand1, operand2);
V_scalar(*datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNMUL_float(Imm<2> type, Vec Vm, Vec Vn, Vec Vd) {
const auto datasize = FPGetDataSize(type);
if (!datasize || *datasize == 16) {
return UnallocatedEncoding();
}
const IR::U32U64 operand1 = V_scalar(*datasize, Vn);
const IR::U32U64 operand2 = V_scalar(*datasize, Vm);
const IR::U32U64 result = ir.FPNeg(ir.FPMul(operand1, operand2));
V_scalar(*datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,405 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/ir/terminal.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::InterpretThisInstruction() {
ir.SetTerm(IR::Term::Interpret(*ir.current_location));
return false;
}
bool TranslatorVisitor::UnpredictableInstruction() {
return RaiseException(Exception::UnpredictableInstruction);
}
bool TranslatorVisitor::DecodeError() {
UNREACHABLE();
}
bool TranslatorVisitor::ReservedValue() {
return RaiseException(Exception::ReservedValue);
}
bool TranslatorVisitor::UnallocatedEncoding() {
return RaiseException(Exception::UnallocatedEncoding);
}
bool TranslatorVisitor::RaiseException(Exception exception) {
ir.SetPC(ir.Imm64(ir.current_location->PC() + 4));
ir.ExceptionRaised(exception);
ir.SetTerm(IR::Term::CheckHalt{IR::Term::ReturnToDispatch{}});
return false;
}
std::optional<TranslatorVisitor::BitMasks> TranslatorVisitor::DecodeBitMasks(bool immN, Imm<6> imms, Imm<6> immr, bool immediate) {
const int len = Common::HighestSetBit((immN ? 1 << 6 : 0) | (imms.ZeroExtend() ^ 0b111111));
if (len < 1) {
return std::nullopt;
}
const size_t levels = Common::Ones<size_t>(len);
if (immediate && (imms.ZeroExtend() & levels) == levels) {
return std::nullopt;
}
const s32 S = s32(imms.ZeroExtend() & levels);
const s32 R = s32(immr.ZeroExtend() & levels);
const u64 d = u64(S - R) & levels;
const size_t esize = size_t{1} << len;
const u64 welem = Common::Ones<u64>(S + 1);
const u64 telem = Common::Ones<u64>(d + 1);
const u64 wmask = Common::RotateRight(Common::Replicate(welem, esize), R);
const u64 tmask = Common::Replicate(telem, esize);
return BitMasks{wmask, tmask};
}
IR::UAny TranslatorVisitor::I(size_t bitsize, u64 value) {
switch (bitsize) {
case 8:
return ir.Imm8(static_cast<u8>(value));
case 16:
return ir.Imm16(static_cast<u16>(value));
case 32:
return ir.Imm32(static_cast<u32>(value));
case 64:
return ir.Imm64(value);
default:
ASSERT_FALSE("Imm - get: Invalid bitsize");
}
}
IR::UAny TranslatorVisitor::X(size_t bitsize, Reg reg) {
switch (bitsize) {
case 8:
return ir.LeastSignificantByte(ir.GetW(reg));
case 16:
return ir.LeastSignificantHalf(ir.GetW(reg));
case 32:
return ir.GetW(reg);
case 64:
return ir.GetX(reg);
default:
ASSERT_FALSE("X - get: Invalid bitsize");
}
}
void TranslatorVisitor::X(size_t bitsize, Reg reg, IR::U32U64 value) {
switch (bitsize) {
case 32:
ir.SetW(reg, value);
return;
case 64:
ir.SetX(reg, value);
return;
default:
ASSERT_FALSE("X - set: Invalid bitsize");
}
}
IR::U32U64 TranslatorVisitor::SP(size_t bitsize) {
switch (bitsize) {
case 32:
return ir.LeastSignificantWord(ir.GetSP());
case 64:
return ir.GetSP();
default:
ASSERT_FALSE("SP - get : Invalid bitsize");
}
}
void TranslatorVisitor::SP(size_t bitsize, IR::U32U64 value) {
switch (bitsize) {
case 32:
ir.SetSP(ir.ZeroExtendWordToLong(value));
break;
case 64:
ir.SetSP(value);
break;
default:
ASSERT_FALSE("SP - set : Invalid bitsize");
}
}
IR::U128 TranslatorVisitor::V(size_t bitsize, Vec vec) {
switch (bitsize) {
case 32:
return ir.GetS(vec);
case 64:
return ir.GetD(vec);
case 128:
return ir.GetQ(vec);
default:
ASSERT_FALSE("V - get : Invalid bitsize");
}
}
void TranslatorVisitor::V(size_t bitsize, Vec vec, IR::U128 value) {
switch (bitsize) {
case 32:
ir.SetS(vec, value);
return;
case 64:
// TODO: Remove VectorZeroUpper when possible.
ir.SetD(vec, ir.VectorZeroUpper(value));
return;
case 128:
ir.SetQ(vec, value);
return;
default:
ASSERT_FALSE("V - Set : Invalid bitsize");
}
}
IR::UAnyU128 TranslatorVisitor::V_scalar(size_t bitsize, Vec vec) {
if (bitsize == 128) {
return V(128, vec);
}
// TODO: Optimize
return ir.VectorGetElement(bitsize, ir.GetQ(vec), 0);
}
void TranslatorVisitor::V_scalar(size_t bitsize, Vec vec, IR::UAnyU128 value) {
if (bitsize == 128) {
V(128, vec, value);
return;
}
// TODO: Optimize
ir.SetQ(vec, ir.ZeroExtendToQuad(value));
}
IR::U128 TranslatorVisitor::Vpart(size_t bitsize, Vec vec, size_t part) {
ASSERT(part == 0 || part == 1);
ASSERT(bitsize == 64);
if (part == 0) {
return V(64, vec);
}
return ir.ZeroExtendToQuad(ir.VectorGetElement(bitsize, V(128, vec), part));
}
void TranslatorVisitor::Vpart(size_t bitsize, Vec vec, size_t part, IR::U128 value) {
ASSERT(part == 0 || part == 1);
if (part == 0) {
ASSERT(bitsize == 64);
V(128, vec, ir.VectorZeroExtend(bitsize, value));
} else {
ASSERT(bitsize == 64);
V(128, vec, ir.VectorInterleaveLower(64, V(128, vec), value));
}
}
IR::UAny TranslatorVisitor::Vpart_scalar(size_t bitsize, Vec vec, size_t part) {
ASSERT(part == 0 || part == 1);
if (part == 0) {
ASSERT(bitsize == 8 || bitsize == 16 || bitsize == 32 || bitsize == 64);
} else {
ASSERT(bitsize == 64);
}
return ir.VectorGetElement(bitsize, V(128, vec), part);
}
void TranslatorVisitor::Vpart_scalar(size_t bitsize, Vec vec, size_t part, IR::UAny value) {
ASSERT(part == 0 || part == 1);
if (part == 0) {
ASSERT(bitsize == 8 || bitsize == 16 || bitsize == 32 || bitsize == 64);
V(128, vec, ir.ZeroExtendToQuad(value));
} else {
ASSERT(bitsize == 64);
V(128, vec, ir.VectorSetElement(64, V(128, vec), 1, value));
}
}
IR::UAnyU128 TranslatorVisitor::Mem(IR::U64 address, size_t bytesize, IR::AccType /*acc_type*/) {
switch (bytesize) {
case 1:
return ir.ReadMemory8(address);
case 2:
return ir.ReadMemory16(address);
case 4:
return ir.ReadMemory32(address);
case 8:
return ir.ReadMemory64(address);
case 16:
return ir.ReadMemory128(address);
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
void TranslatorVisitor::Mem(IR::U64 address, size_t bytesize, IR::AccType /*acc_type*/, IR::UAnyU128 value) {
switch (bytesize) {
case 1:
ir.WriteMemory8(address, value);
return;
case 2:
ir.WriteMemory16(address, value);
return;
case 4:
ir.WriteMemory32(address, value);
return;
case 8:
ir.WriteMemory64(address, value);
return;
case 16:
ir.WriteMemory128(address, value);
return;
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
IR::UAnyU128 TranslatorVisitor::ExclusiveMem(IR::U64 address, size_t bytesize, IR::AccType /*acctype*/) {
switch (bytesize) {
case 1:
return ir.ExclusiveReadMemory8(address);
case 2:
return ir.ExclusiveReadMemory16(address);
case 4:
return ir.ExclusiveReadMemory32(address);
case 8:
return ir.ExclusiveReadMemory64(address);
case 16:
return ir.ExclusiveReadMemory128(address);
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
IR::U32 TranslatorVisitor::ExclusiveMem(IR::U64 address, size_t bytesize, IR::AccType /*acctype*/, IR::UAnyU128 value) {
switch (bytesize) {
case 1:
return ir.ExclusiveWriteMemory8(address, value);
case 2:
return ir.ExclusiveWriteMemory16(address, value);
case 4:
return ir.ExclusiveWriteMemory32(address, value);
case 8:
return ir.ExclusiveWriteMemory64(address, value);
case 16:
return ir.ExclusiveWriteMemory128(address, value);
default:
ASSERT_FALSE("Invalid bytesize parameter {}", bytesize);
}
}
IR::U32U64 TranslatorVisitor::SignExtend(IR::UAny value, size_t to_size) {
switch (to_size) {
case 32:
return ir.SignExtendToWord(value);
case 64:
return ir.SignExtendToLong(value);
default:
ASSERT_FALSE("Invalid size parameter {}", to_size);
}
}
IR::U32U64 TranslatorVisitor::ZeroExtend(IR::UAny value, size_t to_size) {
switch (to_size) {
case 32:
return ir.ZeroExtendToWord(value);
case 64:
return ir.ZeroExtendToLong(value);
default:
ASSERT_FALSE("Invalid size parameter {}", to_size);
}
}
IR::U32U64 TranslatorVisitor::ShiftReg(size_t bitsize, Reg reg, Imm<2> shift, IR::U8 amount) {
IR::U32U64 result = X(bitsize, reg);
switch (shift.ZeroExtend()) {
case 0b00:
return ir.LogicalShiftLeft(result, amount);
case 0b01:
return ir.LogicalShiftRight(result, amount);
case 0b10:
return ir.ArithmeticShiftRight(result, amount);
case 0b11:
return ir.RotateRight(result, amount);
}
UNREACHABLE();
}
IR::U32U64 TranslatorVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
ASSERT(shift <= 4);
ASSERT(bitsize == 32 || bitsize == 64);
IR::UAny val = X(bitsize, reg);
size_t len;
IR::U32U64 extended;
bool signed_extend;
switch (option.ZeroExtend()) {
case 0b000: { // UXTB
val = ir.LeastSignificantByte(val);
len = 8;
signed_extend = false;
break;
}
case 0b001: { // UXTH
val = ir.LeastSignificantHalf(val);
len = 16;
signed_extend = false;
break;
}
case 0b010: { // UXTW
if (bitsize != 32) {
val = ir.LeastSignificantWord(val);
}
len = 32;
signed_extend = false;
break;
}
case 0b011: { // UXTX
len = 64;
signed_extend = false;
break;
}
case 0b100: { // SXTB
val = ir.LeastSignificantByte(val);
len = 8;
signed_extend = true;
break;
}
case 0b101: { // SXTH
val = ir.LeastSignificantHalf(val);
len = 16;
signed_extend = true;
break;
}
case 0b110: { // SXTW
if (bitsize != 32) {
val = ir.LeastSignificantWord(val);
}
len = 32;
signed_extend = true;
break;
}
case 0b111: { // SXTX
len = 64;
signed_extend = true;
break;
}
default:
UNREACHABLE();
}
if (len < bitsize) {
if (bitsize == 32) {
extended = signed_extend ? ir.SignExtendToWord(val) : ir.ZeroExtendToWord(val);
} else {
extended = signed_extend ? ir.SignExtendToLong(val) : ir.ZeroExtendToLong(val);
}
} else {
extended = val;
}
return ir.LogicalShiftLeft(extended, ir.Imm8(shift));
}
} // namespace Dynarmic::A64

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,209 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool ExclusiveSharedDecodeAndOperation(TranslatorVisitor& v, bool pair, size_t size, bool L, bool o0, std::optional<Reg> Rs, std::optional<Reg> Rt2, Reg Rn, Reg Rt) {
// Shared Decode
const auto acctype = o0 ? IR::AccType::ORDERED : IR::AccType::ATOMIC;
const auto memop = L ? IR::MemOp::LOAD : IR::MemOp::STORE;
const size_t elsize = 8 << size;
const size_t regsize = elsize == 64 ? 64 : 32;
const size_t datasize = pair ? elsize * 2 : elsize;
// Operation
const size_t dbytes = datasize / 8;
if (memop == IR::MemOp::LOAD && pair && Rt == *Rt2) {
return v.UnpredictableInstruction();
} else if (memop == IR::MemOp::STORE && (*Rs == Rt || (pair && *Rs == *Rt2))) {
if (!v.options.define_unpredictable_behaviour) {
return v.UnpredictableInstruction();
}
// UNPREDICTABLE: The Constraint_NONE case is executed.
} else if (memop == IR::MemOp::STORE && *Rs == Rn && Rn != Reg::R31) {
return v.UnpredictableInstruction();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
switch (memop) {
case IR::MemOp::STORE: {
IR::UAnyU128 data;
if (pair && elsize == 64) {
data = v.ir.Pack2x64To1x128(v.X(64, Rt), v.X(64, *Rt2));
} else if (pair && elsize == 32) {
data = v.ir.Pack2x32To1x64(v.X(32, Rt), v.X(32, *Rt2));
} else {
data = v.X(elsize, Rt);
}
const IR::U32 status = v.ExclusiveMem(address, dbytes, acctype, data);
v.X(32, *Rs, status);
break;
}
case IR::MemOp::LOAD: {
const IR::UAnyU128 data = v.ExclusiveMem(address, dbytes, acctype);
if (pair && elsize == 64) {
v.X(64, Rt, v.ir.VectorGetElement(64, data, 0));
v.X(64, *Rt2, v.ir.VectorGetElement(64, data, 1));
} else if (pair && elsize == 32) {
v.X(32, Rt, v.ir.LeastSignificantWord(data));
v.X(32, *Rt2, v.ir.MostSignificantWord(data).result);
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STXR(Imm<2> sz, Reg Rs, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, {}, Rn, Rt);
}
bool TranslatorVisitor::STLXR(Imm<2> sz, Reg Rs, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, {}, Rn, Rt);
}
bool TranslatorVisitor::STXP(Imm<1> sz, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, Rt2, Rn, Rt);
}
bool TranslatorVisitor::STLXP(Imm<1> sz, Reg Rs, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, Rs, Rt2, Rn, Rt);
}
bool TranslatorVisitor::LDXR(Imm<2> sz, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, {}, Rn, Rt);
}
bool TranslatorVisitor::LDAXR(Imm<2> sz, Reg Rn, Reg Rt) {
const bool pair = false;
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, {}, Rn, Rt);
}
bool TranslatorVisitor::LDXP(Imm<1> sz, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, Rt2, Rn, Rt);
}
bool TranslatorVisitor::LDAXP(Imm<1> sz, Reg Rt2, Reg Rn, Reg Rt) {
const bool pair = true;
const size_t size = concatenate(Imm<1>{1}, sz).ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return ExclusiveSharedDecodeAndOperation(*this, pair, size, L, o0, {}, Rt2, Rn, Rt);
}
static bool OrderedSharedDecodeAndOperation(TranslatorVisitor& v, size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
// Shared Decode
const auto acctype = !o0 ? IR::AccType::LIMITEDORDERED : IR::AccType::ORDERED;
const auto memop = L ? IR::MemOp::LOAD : IR::MemOp::STORE;
const size_t elsize = 8 << size;
const size_t regsize = elsize == 64 ? 64 : 32;
const size_t datasize = elsize;
// Operation
const size_t dbytes = datasize / 8;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
switch (memop) {
case IR::MemOp::STORE: {
const IR::UAny data = v.X(datasize, Rt);
v.Mem(address, dbytes, acctype, data);
break;
}
case IR::MemOp::LOAD: {
const IR::UAny data = v.Mem(address, dbytes, acctype);
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
break;
}
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 0;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
bool TranslatorVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 0;
const bool o0 = 1;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
bool TranslatorVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 0;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
bool TranslatorVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) {
const size_t size = sz.ZeroExtend<size_t>();
const bool L = 1;
const bool o0 = 1;
return OrderedSharedDecodeAndOperation(*this, size, L, o0, Rn, Rt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,55 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
const size_t size = opc_0 == 0 ? 4 : 8;
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 address = ir.PC() + offset;
const auto data = Mem(ir.Imm64(address), size, IR::AccType::NORMAL);
X(8 * size, Rt, data);
return true;
}
bool TranslatorVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
if (opc == 0b11) {
return UnallocatedEncoding();
}
const u64 size = 4 << opc.ZeroExtend();
const u64 offset = imm19.SignExtend<u64>() << 2;
const IR::U64 address = ir.Imm64(ir.PC() + offset);
const IR::UAnyU128 data = Mem(address, size, IR::AccType::VEC);
if (size == 16) {
V(128, Vt, data);
} else {
V(128, Vt, ir.ZeroExtendToQuad(data));
}
return true;
}
bool TranslatorVisitor::LDRSW_lit(Imm<19> imm19, Reg Rt) {
const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
const u64 address = ir.PC() + offset;
const auto data = Mem(ir.Imm64(address), 4, IR::AccType::NORMAL);
X(64, Rt, ir.SignExtendWordToLong(data));
return true;
}
bool TranslatorVisitor::PRFM_lit(Imm<19> /*imm19*/, Imm<5> /*prfop*/) {
// s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
// u64 address = ir.PC() + offset;
// Prefetch(address, prfop);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,134 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool SharedDecodeAndOperation(TranslatorVisitor& v, bool wback, IR::MemOp memop, bool Q, std::optional<Reg> Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t elements = datasize / esize;
const size_t ebytes = esize / 8;
size_t rpt, selem;
switch (opcode.ZeroExtend()) {
case 0b0000:
rpt = 1;
selem = 4;
break;
case 0b0010:
rpt = 4;
selem = 1;
break;
case 0b0100:
rpt = 1;
selem = 3;
break;
case 0b0110:
rpt = 3;
selem = 1;
break;
case 0b0111:
rpt = 1;
selem = 1;
break;
case 0b1000:
rpt = 1;
selem = 2;
break;
case 0b1010:
rpt = 2;
selem = 1;
break;
default:
return v.UnallocatedEncoding();
}
ASSERT(rpt == 1 || selem == 1);
if ((size == 0b11 && !Q) && selem != 1) {
return v.ReservedValue();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
IR::U64 offs = v.ir.Imm64(0);
if (selem == 1) {
for (size_t r = 0; r < rpt; r++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + r) % 32);
if (memop == IR::MemOp::LOAD) {
const IR::UAnyU128 vec = v.Mem(v.ir.Add(address, offs), ebytes * elements, IR::AccType::VEC);
v.V_scalar(datasize, tt, vec);
} else {
const IR::UAnyU128 vec = v.V_scalar(datasize, tt);
v.Mem(v.ir.Add(address, offs), ebytes * elements, IR::AccType::VEC, vec);
}
offs = v.ir.Add(offs, v.ir.Imm64(ebytes * elements));
}
} else {
for (size_t e = 0; e < elements; e++) {
for (size_t s = 0; s < selem; s++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + s) % 32);
if (memop == IR::MemOp::LOAD) {
const IR::UAny elem = v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC);
const IR::U128 vec = v.ir.VectorSetElement(esize, v.V(datasize, tt), e, elem);
v.V(datasize, tt, vec);
} else {
const IR::UAny elem = v.ir.VectorGetElement(esize, v.V(datasize, tt), e);
v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC, elem);
}
offs = v.ir.Add(offs, v.ir.Imm64(ebytes));
}
}
}
if (wback) {
if (*Rm != Reg::SP) {
offs = v.X(64, *Rm);
}
if (Rn == Reg::SP) {
v.SP(64, v.ir.Add(address, offs));
} else {
v.X(64, Rn, v.ir.Add(address, offs));
}
}
return true;
}
bool TranslatorVisitor::STx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = false;
const auto memop = IR::MemOp::STORE;
return SharedDecodeAndOperation(*this, wback, memop, Q, {}, opcode, size, Rn, Vt);
}
bool TranslatorVisitor::STx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = true;
const auto memop = IR::MemOp::STORE;
return SharedDecodeAndOperation(*this, wback, memop, Q, Rm, opcode, size, Rn, Vt);
}
bool TranslatorVisitor::LDx_mult_1(bool Q, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = false;
const auto memop = IR::MemOp::LOAD;
return SharedDecodeAndOperation(*this, wback, memop, Q, {}, opcode, size, Rn, Vt);
}
bool TranslatorVisitor::LDx_mult_2(bool Q, Reg Rm, Imm<4> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const bool wback = true;
const auto memop = IR::MemOp::LOAD;
return SharedDecodeAndOperation(*this, wback, memop, Q, Rm, opcode, size, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,22 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
// Given the LDNP and STNP instructions are simply hinting at non-temporal
// accesses, we can treat them as regular LDP and STP instructions for the
// time being since we don't perform data caching.
bool TranslatorVisitor::STNP_LDNP_gen(Imm<1> upper_opc, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
return STP_LDP_gen(Imm<2>{upper_opc.ZeroExtend() << 1}, true, false, L, imm7, Rt2, Rn, Rt);
}
bool TranslatorVisitor::STNP_LDNP_fpsimd(Imm<2> opc, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
return STP_LDP_fpsimd(opc, true, false, L, imm7, Vt2, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,249 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool LoadStoreRegisterImmediate(TranslatorVisitor& v, bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) {
IR::MemOp memop;
bool signed_ = false;
size_t regsize = 0;
if (opc.Bit<1>() == 0) {
memop = opc.Bit<0>() ? IR::MemOp::LOAD : IR::MemOp::STORE;
regsize = size == 0b11 ? 64 : 32;
signed_ = false;
} else if (size == 0b11) {
memop = IR::MemOp::PREFETCH;
ASSERT(!opc.Bit<0>());
} else {
memop = IR::MemOp::LOAD;
ASSERT(!(size == 0b10 && opc.Bit<0>() == 1));
regsize = opc.Bit<0>() ? 32 : 64;
signed_ = true;
}
if (memop == IR::MemOp::LOAD && wback && Rn == Rt && Rn != Reg::R31) {
return v.UnpredictableInstruction();
}
if (memop == IR::MemOp::STORE && wback && Rn == Rt && Rn != Reg::R31) {
return v.UnpredictableInstruction();
}
// TODO: Check SP alignment
IR::U64 address = Rn == Reg::SP ? IR::U64(v.SP(64)) : IR::U64(v.X(64, Rn));
if (!postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
const size_t datasize = 8 << scale;
switch (memop) {
case IR::MemOp::STORE: {
const auto data = v.X(datasize, Rt);
v.Mem(address, datasize / 8, IR::AccType::NORMAL, data);
break;
}
case IR::MemOp::LOAD: {
const auto data = v.Mem(address, datasize / 8, IR::AccType::NORMAL);
if (signed_) {
v.X(regsize, Rt, v.SignExtend(data, regsize));
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
case IR::MemOp::PREFETCH:
// Prefetch(address, Rt)
break;
}
if (wback) {
if (postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
if (Rn == Reg::SP) {
v.SP(64, address);
} else {
v.X(64, Rn, address);
}
}
return true;
}
bool TranslatorVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn, Reg Rt) {
const bool wback = true;
const bool postindex = !not_postindex;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreRegisterImmediate(*this, wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool TranslatorVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
const bool wback = false;
const bool postindex = false;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return LoadStoreRegisterImmediate(*this, wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool TranslatorVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
const bool wback = false;
const bool postindex = false;
const size_t scale = size.ZeroExtend<size_t>();
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreRegisterImmediate(*this, wback, postindex, scale, offset, size, opc, Rn, Rt);
}
bool TranslatorVisitor::PRFM_imm([[maybe_unused]] Imm<12> imm12, [[maybe_unused]] Reg Rn, [[maybe_unused]] Reg Rt) {
// Currently a NOP (which is valid behavior, as indicated by
// the ARMv8 architecture reference manual)
return true;
}
bool TranslatorVisitor::PRFM_unscaled_imm([[maybe_unused]] Imm<9> imm9, [[maybe_unused]] Reg Rn, [[maybe_unused]] Reg Rt) {
// Currently a NOP (which is valid behavior, as indicated by
// the ARMv8 architecture reference manual)
return true;
}
static bool LoadStoreSIMD(TranslatorVisitor& v, bool wback, bool postindex, size_t scale, u64 offset, IR::MemOp memop, Reg Rn, Vec Vt) {
const auto acctype = IR::AccType::VEC;
const size_t datasize = 8 << scale;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
if (!postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
switch (memop) {
case IR::MemOp::STORE:
if (datasize == 128) {
const IR::U128 data = v.V(128, Vt);
v.Mem(address, 16, acctype, data);
} else {
const IR::UAny data = v.ir.VectorGetElement(datasize, v.V(128, Vt), 0);
v.Mem(address, datasize / 8, acctype, data);
}
break;
case IR::MemOp::LOAD:
if (datasize == 128) {
const IR::U128 data = v.Mem(address, 16, acctype);
v.V(128, Vt, data);
} else {
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
v.V(128, Vt, v.ir.ZeroExtendToQuad(data));
}
break;
default:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address = v.ir.Add(address, v.ir.Imm64(offset));
}
if (Rn == Reg::SP) {
v.SP(64, address);
} else {
v.X(64, Rn, address);
}
}
return true;
}
bool TranslatorVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = true;
const bool postindex = !not_postindex;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::STORE, Rn, Vt);
}
bool TranslatorVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::STORE, Rn, Vt);
}
bool TranslatorVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = true;
const bool postindex = !not_postindex;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::LOAD, Rn, Vt);
}
bool TranslatorVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm12.ZeroExtend<u64>() << scale;
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::LOAD, Rn, Vt);
}
bool TranslatorVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::STORE, Rn, Vt);
}
bool TranslatorVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const bool wback = false;
const bool postindex = false;
const u64 offset = imm9.SignExtend<u64>();
return LoadStoreSIMD(*this, wback, postindex, scale, offset, IR::MemOp::LOAD, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,154 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) {
return UnallocatedEncoding();
}
const auto memop = L == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
if (memop == IR::MemOp::LOAD && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
return UnpredictableInstruction();
}
if (memop == IR::MemOp::STORE && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
return UnpredictableInstruction();
}
if (memop == IR::MemOp::LOAD && Rt == Rt2) {
return UnpredictableInstruction();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = SP(64);
} else {
address = X(64, Rn);
}
const bool postindex = !not_postindex;
const bool signed_ = opc.Bit<0>() != 0;
const size_t scale = 2 + opc.Bit<1>();
const size_t datasize = 8 << scale;
const u64 offset = imm7.SignExtend<u64>() << scale;
if (!postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
const size_t dbytes = datasize / 8;
switch (memop) {
case IR::MemOp::STORE: {
const IR::U32U64 data1 = X(datasize, Rt);
const IR::U32U64 data2 = X(datasize, Rt2);
Mem(address, dbytes, IR::AccType::NORMAL, data1);
Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::NORMAL, data2);
break;
}
case IR::MemOp::LOAD: {
const IR::U32U64 data1 = Mem(address, dbytes, IR::AccType::NORMAL);
const IR::U32U64 data2 = Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::NORMAL);
if (signed_) {
X(64, Rt, SignExtend(data1, 64));
X(64, Rt2, SignExtend(data2, 64));
} else {
X(datasize, Rt, data1);
X(datasize, Rt2, data2);
}
break;
}
case IR::MemOp::PREFETCH:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
if (Rn == Reg::SP) {
SP(64, address);
} else {
X(64, Rn, address);
}
}
return true;
}
bool TranslatorVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
if (opc == 0b11) {
return UnallocatedEncoding();
}
const auto memop = L == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
if (memop == IR::MemOp::LOAD && Vt == Vt2) {
return UnpredictableInstruction();
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = SP(64);
} else {
address = X(64, Rn);
}
const bool postindex = !not_postindex;
const size_t scale = 2 + opc.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
const u64 offset = imm7.SignExtend<u64>() << scale;
const size_t dbytes = datasize / 8;
if (!postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
switch (memop) {
case IR::MemOp::STORE: {
IR::UAnyU128 data1 = V(datasize, Vt);
IR::UAnyU128 data2 = V(datasize, Vt2);
if (datasize != 128) {
data1 = ir.VectorGetElement(datasize, data1, 0);
data2 = ir.VectorGetElement(datasize, data2, 0);
}
Mem(address, dbytes, IR::AccType::VEC, data1);
Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::VEC, data2);
break;
}
case IR::MemOp::LOAD: {
IR::UAnyU128 data1 = Mem(address, dbytes, IR::AccType::VEC);
IR::UAnyU128 data2 = Mem(ir.Add(address, ir.Imm64(dbytes)), dbytes, IR::AccType::VEC);
if (datasize != 128) {
data1 = ir.ZeroExtendToQuad(data1);
data2 = ir.ZeroExtendToQuad(data2);
}
V(datasize, Vt, data1);
V(datasize, Vt2, data2);
break;
}
case IR::MemOp::PREFETCH:
UNREACHABLE();
}
if (wback) {
if (postindex) {
address = ir.Add(address, ir.Imm64(offset));
}
if (Rn == Reg::SP) {
SP(64, address);
} else {
X(64, Rn, address);
}
}
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,160 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool RegSharedDecodeAndOperation(TranslatorVisitor& v, size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) {
// Shared Decode
const auto acctype = IR::AccType::NORMAL;
IR::MemOp memop;
size_t regsize = 64;
bool signed_ = false;
if (opc_1 == 0) {
memop = opc_0 == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
regsize = size == 0b11 ? 64 : 32;
signed_ = false;
} else if (size == 0b11) {
memop = IR::MemOp::PREFETCH;
if (opc_0 == 1) {
return v.UnallocatedEncoding();
}
} else {
memop = IR::MemOp::LOAD;
if (size == 0b10 && opc_0 == 1) {
return v.UnallocatedEncoding();
}
regsize = opc_0 == 1 ? 32 : 64;
signed_ = true;
}
const size_t datasize = 8 << scale;
// Operation
const IR::U64 offset = v.ExtendReg(64, Rm, option, shift);
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, offset);
switch (memop) {
case IR::MemOp::STORE: {
const IR::UAny data = v.X(datasize, Rt);
v.Mem(address, datasize / 8, acctype, data);
break;
}
case IR::MemOp::LOAD: {
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
if (signed_) {
v.X(regsize, Rt, v.SignExtend(data, regsize));
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
case IR::MemOp::PREFETCH:
// TODO: Prefetch
break;
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) {
const Imm<1> opc_0{0};
const size_t scale = size.ZeroExtend<size_t>();
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return RegSharedDecodeAndOperation(*this, scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
}
bool TranslatorVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Reg Rt) {
const Imm<1> opc_0{1};
const size_t scale = size.ZeroExtend<size_t>();
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return RegSharedDecodeAndOperation(*this, scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
}
static bool VecSharedDecodeAndOperation(TranslatorVisitor& v, size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt) {
// Shared Decode
const auto acctype = IR::AccType::VEC;
const auto memop = opc_0 == 1 ? IR::MemOp::LOAD : IR::MemOp::STORE;
const size_t datasize = 8 << scale;
// Operation
const IR::U64 offset = v.ExtendReg(64, Rm, option, shift);
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, offset);
switch (memop) {
case IR::MemOp::STORE: {
const IR::UAnyU128 data = v.V_scalar(datasize, Vt);
v.Mem(address, datasize / 8, acctype, data);
break;
}
case IR::MemOp::LOAD: {
const IR::UAnyU128 data = v.Mem(address, datasize / 8, acctype);
v.V_scalar(datasize, Vt, data);
break;
}
default:
UNREACHABLE();
}
return true;
}
bool TranslatorVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) {
const Imm<1> opc_0{0};
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return VecSharedDecodeAndOperation(*this, scale, shift, opc_0, Rm, option, Rn, Vt);
}
bool TranslatorVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn, Vec Vt) {
const Imm<1> opc_0{1};
const size_t scale = concatenate(opc_1, size).ZeroExtend<size_t>();
if (scale > 4) {
return UnallocatedEncoding();
}
const u8 shift = S ? static_cast<u8>(scale) : 0;
if (!option.Bit<1>()) {
return UnallocatedEncoding();
}
return VecSharedDecodeAndOperation(*this, scale, shift, opc_0, Rm, option, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,152 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool StoreRegister(TranslatorVisitor& v, const size_t datasize,
const Imm<9> imm9, const Reg Rn, const Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, v.ir.Imm64(offset));
const IR::UAny data = v.X(datasize, Rt);
v.Mem(address, datasize / 8, acctype, data);
return true;
}
static bool LoadRegister(TranslatorVisitor& v, const size_t datasize,
const Imm<9> imm9, const Reg Rn, const Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, v.ir.Imm64(offset));
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
// max is used to zeroextend < 32 to 32, and > 32 to 64
const size_t extended_size = std::max<size_t>(32, datasize);
v.X(extended_size, Rt, v.ZeroExtend(data, extended_size));
return true;
}
static bool LoadRegisterSigned(TranslatorVisitor& v, const size_t datasize,
const Imm<2> opc, const Imm<9> imm9, const Reg Rn, const Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::MemOp memop;
bool is_signed;
size_t regsize;
if (opc.Bit<1>() == 0) {
// store or zero-extending load
memop = opc.Bit<0>() ? IR::MemOp::LOAD : IR::MemOp::STORE;
regsize = 32;
is_signed = false;
} else {
// sign-extending load
memop = IR::MemOp::LOAD;
regsize = opc.Bit<0>() ? 32 : 64;
is_signed = true;
}
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
address = v.ir.Add(address, v.ir.Imm64(offset));
switch (memop) {
case IR::MemOp::STORE:
v.Mem(address, datasize / 8, acctype, v.X(datasize, Rt));
break;
case IR::MemOp::LOAD: {
const IR::UAny data = v.Mem(address, datasize / 8, acctype);
if (is_signed) {
v.X(regsize, Rt, v.SignExtend(data, regsize));
} else {
v.X(regsize, Rt, v.ZeroExtend(data, regsize));
}
break;
}
case IR::MemOp::PREFETCH:
// Prefetch(address, Rt);
break;
}
return true;
}
bool TranslatorVisitor::STTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
return StoreRegister(*this, 8, imm9, Rn, Rt);
}
bool TranslatorVisitor::STTRH(Imm<9> imm9, Reg Rn, Reg Rt) {
return StoreRegister(*this, 16, imm9, Rn, Rt);
}
bool TranslatorVisitor::STTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) {
const size_t scale = size.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
return StoreRegister(*this, datasize, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRB(Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegister(*this, 8, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRH(Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegister(*this, 16, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTR(Imm<2> size, Imm<9> imm9, Reg Rn, Reg Rt) {
const size_t scale = size.ZeroExtend<size_t>();
const size_t datasize = 8 << scale;
return LoadRegister(*this, datasize, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRSB(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegisterSigned(*this, 8, opc, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRSH(Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
return LoadRegisterSigned(*this, 16, opc, imm9, Rn, Rt);
}
bool TranslatorVisitor::LDTRSW(Imm<9> imm9, Reg Rn, Reg Rt) {
const u64 offset = imm9.SignExtend<u64>();
const auto acctype = IR::AccType::UNPRIV;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check Stack Alignment
address = SP(64);
} else {
address = X(64, Rn);
}
address = ir.Add(address, ir.Imm64(offset));
const IR::UAny data = Mem(address, 4, acctype);
X(64, Rt, SignExtend(data, 64));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,226 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool SharedDecodeAndOperation(TranslatorVisitor& v, bool wback, IR::MemOp memop,
bool Q, bool S, bool R, bool replicate, std::optional<Reg> Rm,
Imm<3> opcode, Imm<2> size, Reg Rn, Vec Vt) {
const size_t selem = (opcode.Bit<0>() << 1 | u32{R}) + 1;
size_t scale = opcode.Bits<1, 2>();
size_t index = 0;
switch (scale) {
case 0:
index = Q << 3 | S << 2 | size.ZeroExtend();
break;
case 1:
if (size.Bit<0>()) {
return v.UnallocatedEncoding();
}
index = Q << 2 | S << 1 | u32{size.Bit<1>()};
break;
case 2:
if (size.Bit<1>()) {
return v.UnallocatedEncoding();
}
if (size.Bit<0>()) {
if (S) {
return v.UnallocatedEncoding();
}
index = Q;
scale = 3;
} else {
index = Q << 1 | u32{S};
}
break;
case 3:
if (memop == IR::MemOp::STORE || S) {
return v.UnallocatedEncoding();
}
scale = size.ZeroExtend();
break;
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << scale;
const size_t ebytes = esize / 8;
IR::U64 address;
if (Rn == Reg::SP) {
// TODO: Check SP Alignment
address = v.SP(64);
} else {
address = v.X(64, Rn);
}
IR::U64 offs = v.ir.Imm64(0);
if (replicate) {
for (size_t s = 0; s < selem; s++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + s) % 32);
const IR::UAnyU128 element = v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC);
const IR::U128 broadcasted_element = v.ir.VectorBroadcast(esize, element);
v.V(datasize, tt, broadcasted_element);
offs = v.ir.Add(offs, v.ir.Imm64(ebytes));
}
} else {
for (size_t s = 0; s < selem; s++) {
const Vec tt = static_cast<Vec>((VecNumber(Vt) + s) % 32);
const IR::U128 rval = v.V(128, tt);
if (memop == IR::MemOp::LOAD) {
const IR::UAny elem = v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC);
const IR::U128 vec = v.ir.VectorSetElement(esize, rval, index, elem);
v.V(128, tt, vec);
} else {
const IR::UAny elem = v.ir.VectorGetElement(esize, rval, index);
v.Mem(v.ir.Add(address, offs), ebytes, IR::AccType::VEC, elem);
}
offs = v.ir.Add(offs, v.ir.Imm64(ebytes));
}
}
if (wback) {
if (*Rm != Reg::SP) {
offs = v.X(64, *Rm);
}
if (Rn == Reg::SP) {
v.SP(64, v.ir.Add(address, offs));
} else {
v.X(64, Rn, v.ir.Add(address, offs));
}
}
return true;
}
bool TranslatorVisitor::LD1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, false, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, false, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD1R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, false, true,
{}, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD1R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, false, true,
Rm, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, true, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, true, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, true, true,
{}, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD2R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, true, true,
Rm, Imm<3>{0b110}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, false, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, false, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, false, true,
{}, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::LD3R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, false, true,
Rm, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, S, true, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, S, true, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4R_1(bool Q, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::LOAD, Q, false, true, true,
{}, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::LD4R_2(bool Q, Reg Rm, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::LOAD, Q, false, true, true,
Rm, Imm<3>{0b111}, size, Rn, Vt);
}
bool TranslatorVisitor::ST1_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, false, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST1_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, false, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST2_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, true, false, {},
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST2_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, true, false, Rm,
Imm<3>{upper_opcode.ZeroExtend() << 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST3_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, false, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST3_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, false, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST4_sngl_1(bool Q, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, false, IR::MemOp::STORE, Q, S, true, false, {},
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
bool TranslatorVisitor::ST4_sngl_2(bool Q, Reg Rm, Imm<2> upper_opcode, bool S, Imm<2> size, Reg Rn, Vec Vt) {
return SharedDecodeAndOperation(*this, true, IR::MemOp::STORE, Q, S, true, false, Rm,
Imm<3>{(upper_opcode.ZeroExtend() << 1) | 1}, size, Rn, Vt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,60 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::MOVN(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
if (!sf && hw.Bit<1>()) {
return UnallocatedEncoding();
}
const size_t datasize = sf ? 64 : 32;
const size_t pos = hw.ZeroExtend<size_t>() << 4;
u64 value = imm16.ZeroExtend<u64>() << pos;
value = ~value;
const auto result = I(datasize, value);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::MOVZ(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
if (!sf && hw.Bit<1>()) {
return UnallocatedEncoding();
}
const size_t datasize = sf ? 64 : 32;
const size_t pos = hw.ZeroExtend<size_t>() << 4;
const u64 value = imm16.ZeroExtend<u64>() << pos;
const auto result = I(datasize, value);
X(datasize, Rd, result);
return true;
}
bool TranslatorVisitor::MOVK(bool sf, Imm<2> hw, Imm<16> imm16, Reg Rd) {
if (!sf && hw.Bit<1>()) {
return UnallocatedEncoding();
}
const size_t datasize = sf ? 64 : 32;
const size_t pos = hw.ZeroExtend<size_t>() << 4;
const u64 mask = u64(0xFFFF) << pos;
const u64 value = imm16.ZeroExtend<u64>() << pos;
auto result = X(datasize, Rd);
result = ir.And(result, I(datasize, ~mask));
result = ir.Or(result, I(datasize, value));
X(datasize, Rd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,238 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class Signedness {
Signed,
Unsigned
};
bool LongAdd(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Signedness sign) {
if ((size == 0b10 && !Q) || size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const IR::U128 operand = v.V(datasize, Vn);
const auto get_element = [&](IR::U128 vec, size_t element) {
const auto vec_element = v.ir.VectorGetElement(esize, vec, element);
if (sign == Signedness::Signed) {
return v.ir.SignExtendToLong(vec_element);
}
return v.ir.ZeroExtendToLong(vec_element);
};
IR::U64 sum = get_element(operand, 0);
for (size_t i = 1; i < elements; i++) {
sum = v.ir.Add(sum, get_element(operand, i));
}
if (size == 0b00) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantHalf(sum)));
} else if (size == 0b01) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantWord(sum)));
} else {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(sum));
}
return true;
}
enum class MinMaxOperation {
Max,
MaxNumeric,
Min,
MinNumeric,
};
bool FPMinMax(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, MinMaxOperation operation) {
if (!Q || sz) {
return v.ReservedValue();
}
const size_t esize = 32;
const size_t datasize = 128;
const size_t elements = datasize / esize;
const IR::U128 operand = v.V(datasize, Vn);
const auto op = [&](const IR::U32U64& lhs, const IR::U32U64& rhs) {
switch (operation) {
case MinMaxOperation::Max:
return v.ir.FPMax(lhs, rhs);
case MinMaxOperation::MaxNumeric:
return v.ir.FPMaxNumeric(lhs, rhs);
case MinMaxOperation::Min:
return v.ir.FPMin(lhs, rhs);
case MinMaxOperation::MinNumeric:
return v.ir.FPMinNumeric(lhs, rhs);
default:
UNREACHABLE();
}
};
const auto reduce = [&](size_t start, size_t end) {
IR::U32U64 result = v.ir.VectorGetElement(esize, operand, start);
for (size_t i = start + 1; i < end; i++) {
const IR::U32U64 element = v.ir.VectorGetElement(esize, operand, i);
result = op(result, element);
}
return result;
};
const IR::U32U64 hi = reduce(elements / 2, elements);
const IR::U32U64 lo = reduce(0, elements / 2);
const IR::U32U64 result = op(lo, hi);
v.V_scalar(esize, Vd, result);
return true;
}
enum class ScalarMinMaxOperation {
Max,
Min,
};
bool ScalarMinMax(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd,
ScalarMinMaxOperation operation, Signedness sign) {
if ((size == 0b10 && !Q) || size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const auto get_element = [&](IR::U128 vec, size_t element) {
const auto vec_element = v.ir.VectorGetElement(esize, vec, element);
if (sign == Signedness::Signed) {
return v.ir.SignExtendToWord(vec_element);
}
return v.ir.ZeroExtendToWord(vec_element);
};
const auto op_func = [&](const auto& a, const auto& b) {
switch (operation) {
case ScalarMinMaxOperation::Max:
if (sign == Signedness::Signed) {
return v.ir.MaxSigned(a, b);
}
return v.ir.MaxUnsigned(a, b);
case ScalarMinMaxOperation::Min:
if (sign == Signedness::Signed) {
return v.ir.MinSigned(a, b);
}
return v.ir.MinUnsigned(a, b);
default:
UNREACHABLE();
}
};
const IR::U128 operand = v.V(datasize, Vn);
IR::U32 value = get_element(operand, 0);
for (size_t i = 1; i < elements; i++) {
value = op_func(value, get_element(operand, i));
}
if (size == 0b00) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantByte(value)));
} else if (size == 0b01) {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(v.ir.LeastSignificantHalf(value)));
} else {
v.V(datasize, Vd, v.ir.ZeroExtendToQuad(value));
}
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::ADDV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if ((size == 0b10 && !Q) || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const IR::U128 operand = V(datasize, Vn);
const auto get_element = [&](IR::U128 vec, size_t element) {
return ir.ZeroExtendToWord(ir.VectorGetElement(esize, vec, element));
};
IR::U32 sum = get_element(operand, 0);
for (size_t i = 1; i < elements; i++) {
sum = ir.Add(sum, get_element(operand, i));
}
if (size == 0b00) {
V(datasize, Vd, ir.ZeroExtendToQuad(ir.LeastSignificantByte(sum)));
} else if (size == 0b01) {
V(datasize, Vd, ir.ZeroExtendToQuad(ir.LeastSignificantHalf(sum)));
} else {
V(datasize, Vd, ir.ZeroExtendToQuad(sum));
}
return true;
}
bool TranslatorVisitor::FMAXNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::MaxNumeric);
}
bool TranslatorVisitor::FMAXV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::Max);
}
bool TranslatorVisitor::FMINNMV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::MinNumeric);
}
bool TranslatorVisitor::FMINV_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPMinMax(*this, Q, sz, Vn, Vd, MinMaxOperation::Min);
}
bool TranslatorVisitor::SADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return LongAdd(*this, Q, size, Vn, Vd, Signedness::Signed);
}
bool TranslatorVisitor::SMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Max, Signedness::Signed);
}
bool TranslatorVisitor::SMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Min, Signedness::Signed);
}
bool TranslatorVisitor::UADDLV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return LongAdd(*this, Q, size, Vn, Vd, Signedness::Unsigned);
}
bool TranslatorVisitor::UMAXV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Max, Signedness::Unsigned);
}
bool TranslatorVisitor::UMINV(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return ScalarMinMax(*this, Q, size, Vn, Vd, ScalarMinMaxOperation::Min, Signedness::Unsigned);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,46 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::AESD(Vec Vn, Vec Vd) {
const IR::U128 operand1 = ir.GetQ(Vd);
const IR::U128 operand2 = ir.GetQ(Vn);
const IR::U128 result = ir.AESDecryptSingleRound(ir.VectorEor(operand1, operand2));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::AESE(Vec Vn, Vec Vd) {
const IR::U128 operand1 = ir.GetQ(Vd);
const IR::U128 operand2 = ir.GetQ(Vn);
const IR::U128 result = ir.AESEncryptSingleRound(ir.VectorEor(operand1, operand2));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::AESIMC(Vec Vn, Vec Vd) {
const IR::U128 operand = ir.GetQ(Vn);
const IR::U128 result = ir.AESInverseMixColumns(operand);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::AESMC(Vec Vn, Vec Vd) {
const IR::U128 operand = ir.GetQ(Vn);
const IR::U128 result = ir.AESMixColumns(operand);
ir.SetQ(Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,158 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::DUP_elt_1(Imm<5> imm5, Vec Vn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t esize = 8 << size;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny element = ir.VectorGetElement(esize, operand, index);
const IR::U128 result = ir.ZeroExtendToQuad(element);
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::DUP_elt_2(bool Q, Imm<5> imm5, Vec Vn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
if (size == 3 && !Q) {
return ReservedValue();
}
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t esize = 8 << size;
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny element = ir.VectorGetElement(esize, operand, index);
const IR::U128 result = Q ? ir.VectorBroadcast(esize, element) : ir.VectorBroadcastLower(esize, element);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::DUP_gen(bool Q, Imm<5> imm5, Reg Rn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
if (size == 3 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size;
const size_t datasize = Q ? 128 : 64;
const IR::UAny element = X(esize, Rn);
const IR::U128 result = Q ? ir.VectorBroadcast(esize, element) : ir.VectorBroadcastLower(esize, element);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size == 2 && !Q) {
return UnallocatedEncoding();
}
if (size > 2) {
return ReservedValue();
}
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t esize = 8 << size;
const size_t datasize = Q ? 64 : 32;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny elem = ir.VectorGetElement(esize, operand, index);
X(datasize, Rd, SignExtend(elem, datasize));
return true;
}
bool TranslatorVisitor::UMOV(bool Q, Imm<5> imm5, Vec Vn, Reg Rd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size < 3 && Q) {
return UnallocatedEncoding();
}
if (size == 3 && !Q) {
return UnallocatedEncoding();
}
if (size > 3) {
return ReservedValue();
}
const size_t idxdsize = imm5.Bit<4>() ? 128 : 64;
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t esize = 8 << size;
const size_t datasize = Q ? 64 : 32;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny elem = ir.VectorGetElement(esize, operand, index);
X(datasize, Rd, ZeroExtend(elem, datasize));
return true;
}
bool TranslatorVisitor::INS_gen(Imm<5> imm5, Reg Rn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
const size_t index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t esize = 8 << size;
const size_t datasize = 128;
const IR::UAny element = X(esize, Rn);
const IR::U128 result = ir.VectorSetElement(esize, V(datasize, Vd), index, element);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::INS_elt(Imm<5> imm5, Imm<4> imm4, Vec Vn, Vec Vd) {
const size_t size = Common::LowestSetBit(imm5.ZeroExtend());
if (size > 3) {
return ReservedValue();
}
const size_t dst_index = imm5.ZeroExtend<size_t>() >> (size + 1);
const size_t src_index = imm4.ZeroExtend<size_t>() >> size;
const size_t idxdsize = imm4.Bit<3>() ? 128 : 64;
const size_t esize = 8 << size;
const IR::U128 operand = V(idxdsize, Vn);
const IR::UAny elem = ir.VectorGetElement(esize, operand, src_index);
const IR::U128 result = ir.VectorSetElement(esize, V(128, Vd), dst_index, elem);
V(128, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,52 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::EOR3(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 a = ir.GetQ(Va);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 result = ir.VectorEor(ir.VectorEor(n, m), a);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::BCAX(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 a = ir.GetQ(Va);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 result = ir.VectorEor(n, ir.VectorAnd(m, ir.VectorNot(a)));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SM3SS1(Vec Vm, Vec Va, Vec Vn, Vec Vd) {
const IR::U128 a = ir.GetQ(Va);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U32 top_a = ir.VectorGetElement(32, a, 3);
const IR::U32 top_m = ir.VectorGetElement(32, m, 3);
const IR::U32 top_n = ir.VectorGetElement(32, n, 3);
const IR::U32 rotated_n = ir.RotateRight(top_n, ir.Imm8(20));
const IR::U32 sum = ir.Add(ir.Add(rotated_n, top_m), top_a);
const IR::U32 result = ir.RotateRight(sum, ir.Imm8(25));
const IR::U128 zero_vector = ir.ZeroVector();
const IR::U128 vector_result = ir.VectorSetElement(32, zero_vector, 3, result);
ir.SetQ(Vd, vector_result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,102 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class SM3TTVariant {
A,
B,
};
bool SM3TT1(TranslatorVisitor& v, Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd, SM3TTVariant behavior) {
const IR::U128 d = v.ir.GetQ(Vd);
const IR::U128 m = v.ir.GetQ(Vm);
const IR::U128 n = v.ir.GetQ(Vn);
const u32 index = imm2.ZeroExtend();
const IR::U32 top_d = v.ir.VectorGetElement(32, d, 3);
const IR::U32 before_top_d = v.ir.VectorGetElement(32, d, 2);
const IR::U32 after_low_d = v.ir.VectorGetElement(32, d, 1);
const IR::U32 low_d = v.ir.VectorGetElement(32, d, 0);
const IR::U32 top_n = v.ir.VectorGetElement(32, n, 3);
const IR::U32 wj_prime = v.ir.VectorGetElement(32, m, index);
const IR::U32 ss2 = v.ir.Eor(top_n, v.ir.RotateRight(top_d, v.ir.Imm8(20)));
const IR::U32 tt1 = [&] {
if (behavior == SM3TTVariant::A) {
return v.ir.Eor(after_low_d, v.ir.Eor(top_d, before_top_d));
}
const IR::U32 tmp1 = v.ir.And(top_d, after_low_d);
const IR::U32 tmp2 = v.ir.And(top_d, before_top_d);
const IR::U32 tmp3 = v.ir.And(after_low_d, before_top_d);
return v.ir.Or(v.ir.Or(tmp1, tmp2), tmp3);
}();
const IR::U32 final_tt1 = v.ir.Add(tt1, v.ir.Add(low_d, v.ir.Add(ss2, wj_prime)));
const IR::U128 zero_vector = v.ir.ZeroVector();
const IR::U128 tmp1 = v.ir.VectorSetElement(32, zero_vector, 0, after_low_d);
const IR::U128 tmp2 = v.ir.VectorSetElement(32, tmp1, 1, v.ir.RotateRight(before_top_d, v.ir.Imm8(23)));
const IR::U128 tmp3 = v.ir.VectorSetElement(32, tmp2, 2, top_d);
const IR::U128 result = v.ir.VectorSetElement(32, tmp3, 3, final_tt1);
v.ir.SetQ(Vd, result);
return true;
}
bool SM3TT2(TranslatorVisitor& v, Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd, SM3TTVariant behavior) {
const IR::U128 d = v.ir.GetQ(Vd);
const IR::U128 m = v.ir.GetQ(Vm);
const IR::U128 n = v.ir.GetQ(Vn);
const u32 index = imm2.ZeroExtend();
const IR::U32 top_d = v.ir.VectorGetElement(32, d, 3);
const IR::U32 before_top_d = v.ir.VectorGetElement(32, d, 2);
const IR::U32 after_low_d = v.ir.VectorGetElement(32, d, 1);
const IR::U32 low_d = v.ir.VectorGetElement(32, d, 0);
const IR::U32 top_n = v.ir.VectorGetElement(32, n, 3);
const IR::U32 wj = v.ir.VectorGetElement(32, m, index);
const IR::U32 tt2 = [&] {
if (behavior == SM3TTVariant::A) {
return v.ir.Eor(after_low_d, v.ir.Eor(top_d, before_top_d));
}
const IR::U32 tmp1 = v.ir.And(top_d, before_top_d);
const IR::U32 tmp2 = v.ir.And(v.ir.Not(top_d), after_low_d);
return v.ir.Or(tmp1, tmp2);
}();
const IR::U32 final_tt2 = v.ir.Add(tt2, v.ir.Add(low_d, v.ir.Add(top_n, wj)));
const IR::U32 top_result = v.ir.Eor(final_tt2, v.ir.Eor(v.ir.RotateRight(final_tt2, v.ir.Imm8(23)),
v.ir.RotateRight(final_tt2, v.ir.Imm8(15))));
const IR::U128 zero_vector = v.ir.ZeroVector();
const IR::U128 tmp1 = v.ir.VectorSetElement(32, zero_vector, 0, after_low_d);
const IR::U128 tmp2 = v.ir.VectorSetElement(32, tmp1, 1, v.ir.RotateRight(before_top_d, v.ir.Imm8(13)));
const IR::U128 tmp3 = v.ir.VectorSetElement(32, tmp2, 2, top_d);
const IR::U128 result = v.ir.VectorSetElement(32, tmp3, 3, top_result);
v.ir.SetQ(Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::SM3TT1A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT1(*this, Vm, imm2, Vn, Vd, SM3TTVariant::A);
}
bool TranslatorVisitor::SM3TT1B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT1(*this, Vm, imm2, Vn, Vd, SM3TTVariant::B);
}
bool TranslatorVisitor::SM3TT2A(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT2(*this, Vm, imm2, Vn, Vd, SM3TTVariant::A);
}
bool TranslatorVisitor::SM3TT2B(Vec Vm, Imm<2> imm2, Vec Vn, Vec Vd) {
return SM3TT2(*this, Vm, imm2, Vn, Vd, SM3TTVariant::B);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,27 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::EXT(bool Q, Vec Vm, Imm<4> imm4, Vec Vn, Vec Vd) {
if (!Q && imm4.Bit<3>()) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t position = imm4.ZeroExtend<size_t>() << 3;
const IR::U128 lo = V(datasize, Vn);
const IR::U128 hi = V(datasize, Vm);
const IR::U128 result = datasize == 64 ? ir.VectorExtractLower(lo, hi, position) : ir.VectorExtract(lo, hi, position);
V(datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,109 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::MOVI(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<4> cmode, Imm<1> d, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
// MOVI
// also FMOV (vector, immediate) when cmode == 0b1111
const auto movi = [&]{
const u64 imm64 = AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
};
// MVNI
const auto mvni = [&]{
const u64 imm64 = ~AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
};
// ORR (vector, immediate)
const auto orr = [&]{
const u64 imm64 = AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
const IR::U128 operand = V(datasize, Vd);
const IR::U128 result = ir.VectorOr(operand, imm);
V(datasize, Vd, result);
return true;
};
// BIC (vector, immediate)
const auto bic = [&]{
const u64 imm64 = ~AdvSIMDExpandImm(op, cmode, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
const IR::U128 operand = V(datasize, Vd);
const IR::U128 result = ir.VectorAnd(operand, imm);
V(datasize, Vd, result);
return true;
};
switch (concatenate(cmode, Imm<1>{op}).ZeroExtend()) {
case 0b00000: case 0b00100: case 0b01000: case 0b01100:
case 0b10000: case 0b10100:
case 0b11000: case 0b11010:
case 0b11100: case 0b11101: case 0b11110:
return movi();
case 0b11111:
if (!Q) {
return UnallocatedEncoding();
}
return movi();
case 0b00001: case 0b00101: case 0b01001: case 0b01101:
case 0b10001: case 0b10101:
case 0b11001: case 0b11011:
return mvni();
case 0b00010: case 0b00110: case 0b01010: case 0b01110:
case 0b10010: case 0b10110:
return orr();
case 0b00011: case 0b00111: case 0b01011: case 0b01111:
case 0b10011: case 0b10111:
return bic();
}
UNREACHABLE();
}
bool TranslatorVisitor::FMOV_2(bool Q, bool op, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
if (op && !Q) {
return UnallocatedEncoding();
}
const u64 imm64 = AdvSIMDExpandImm(op, Imm<4>{0b1111}, concatenate(a, b, c, d, e, f, g, h));
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
}
bool TranslatorVisitor::FMOV_3(bool Q, Imm<1> a, Imm<1> b, Imm<1> c, Imm<1> d, Imm<1> e, Imm<1> f, Imm<1> g, Imm<1> h, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const Imm<8> imm8 = concatenate(a, b, c, d, e, f, g, h);
const u16 imm16 = [&imm8]{
u16 imm16 = 0;
imm16 |= imm8.Bit<7>() ? 0x8000 : 0;
imm16 |= imm8.Bit<6>() ? 0x3000 : 0x4000;
imm16 |= imm8.Bits<0, 5, u16>() << 6;
return imm16;
}();
const u64 imm64 = Common::Replicate<u64>(imm16, 16);
const IR::U128 imm = datasize == 64 ? ir.ZeroExtendToQuad(ir.Imm64(imm64)) : ir.VectorBroadcast(64, ir.Imm64(imm64));
V(128, Vd, imm);
return true;
}
} // namespace Dynarmic::A6

View File

@@ -0,0 +1,112 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <tuple>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class Transposition {
TRN1,
TRN2,
};
bool VectorTranspose(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Transposition type) {
if (!Q && size == 0b11) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const u8 esize = static_cast<u8>(8 << size.ZeroExtend());
const IR::U128 m = v.V(datasize, Vm);
const IR::U128 n = v.V(datasize, Vn);
const IR::U128 result = v.ir.VectorTranspose(esize, n, m, type == Transposition::TRN2);
v.V(datasize, Vd, result);
return true;
}
enum class UnzipType {
Even,
Odd,
};
bool VectorUnzip(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, UnzipType type) {
if (size == 0b11 && !Q) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 n = v.V(datasize, Vn);
const IR::U128 m = v.V(datasize, Vm);
const IR::U128 result = type == UnzipType::Even
? (Q ? v.ir.VectorDeinterleaveEven(esize, n, m) : v.ir.VectorDeinterleaveEvenLower(esize, n, m))
: (Q ? v.ir.VectorDeinterleaveOdd(esize, n, m) : v.ir.VectorDeinterleaveOddLower(esize, n, m));
v.V(datasize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::TRN1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorTranspose(*this, Q, size, Vm, Vn, Vd, Transposition::TRN1);
}
bool TranslatorVisitor::TRN2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorTranspose(*this, Q, size, Vm, Vn, Vd, Transposition::TRN2);
}
bool TranslatorVisitor::UZP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorUnzip(*this, Q, size, Vm, Vn, Vd, UnzipType::Even);
}
bool TranslatorVisitor::UZP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return VectorUnzip(*this, Q, size, Vm, Vn, Vd, UnzipType::Odd);
}
bool TranslatorVisitor::ZIP1(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
const IR::U128 result = ir.VectorInterleaveLower(esize, operand1, operand2);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::ZIP2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
const IR::U128 result = [&]{
if (Q) {
return ir.VectorInterleaveUpper(esize, operand1, operand2);
}
// TODO: Urgh.
const IR::U128 interleaved = ir.VectorInterleaveLower(esize, operand1, operand2);
return ir.VectorZeroUpper(ir.VectorShuffleWords(interleaved, 0b01001110));
}();
V(datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,80 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class MinMaxOperation {
Max,
MaxNumeric,
Min,
MinNumeric,
};
bool FPPairwiseMinMax(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, MinMaxOperation operation) {
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = v.V(128, Vn);
const IR::U32U64 element1 = v.ir.VectorGetElement(esize, operand, 0);
const IR::U32U64 element2 = v.ir.VectorGetElement(esize, operand, 1);
const IR::U32U64 result = [&] {
switch (operation) {
case MinMaxOperation::Max:
return v.ir.FPMax(element1, element2);
case MinMaxOperation::MaxNumeric:
return v.ir.FPMaxNumeric(element1, element2);
case MinMaxOperation::Min:
return v.ir.FPMin(element1, element2);
case MinMaxOperation::MinNumeric:
return v.ir.FPMinNumeric(element1, element2);
default:
UNREACHABLE();
}
}();
v.V(128, Vd, v.ir.ZeroExtendToQuad(result));
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::ADDP_pair(Imm<2> size, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const IR::U64 operand1 = ir.VectorGetElement(64, V(128, Vn), 0);
const IR::U64 operand2 = ir.VectorGetElement(64, V(128, Vn), 1);
const IR::U128 result = ir.ZeroExtendToQuad(ir.Add(operand1, operand2));
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::FADDP_pair_2(bool size, Vec Vn, Vec Vd) {
const size_t esize = size ? 64 : 32;
const IR::U32U64 operand1 = ir.VectorGetElement(esize, V(128, Vn), 0);
const IR::U32U64 operand2 = ir.VectorGetElement(esize, V(128, Vn), 1);
const IR::U128 result = ir.ZeroExtendToQuad(ir.FPAdd(operand1, operand2));
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::FMAXNMP_pair_2(bool sz, Vec Vn, Vec Vd) {
return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::MaxNumeric);
}
bool TranslatorVisitor::FMAXP_pair_2(bool sz, Vec Vn, Vec Vd) {
return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::Max);
}
bool TranslatorVisitor::FMINNMP_pair_2(bool sz, Vec Vn, Vec Vd) {
return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::MinNumeric);
}
bool TranslatorVisitor::FMINP_pair_2(bool sz, Vec Vn, Vec Vd) {
return FPPairwiseMinMax(*this, sz, Vn, Vd, MinMaxOperation::Min);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,356 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/rounding_mode.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class Narrowing {
Truncation,
SaturateToUnsigned,
SaturateToSigned,
};
enum class SaturatingShiftLeftType {
Signed,
Unsigned,
SignedWithUnsignedSaturation,
};
enum class ShiftExtraBehavior {
None,
Accumulate,
};
enum class Signedness {
Signed,
Unsigned,
};
enum class FloatConversionDirection {
FixedToFloat,
FloatToFixed,
};
bool SaturatingShiftLeft(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftType type) {
if (immh == 0b0000) {
return v.ReservedValue();
}
const size_t esize = 8U << Common::HighestSetBit(immh.ZeroExtend());
const size_t shift_amount = concatenate(immh, immb).ZeroExtend() - esize;
const IR::U128 operand = v.ir.ZeroExtendToQuad(v.V_scalar(esize, Vn));
const IR::U128 shift = v.ir.ZeroExtendToQuad(v.I(esize, shift_amount));
const IR::U128 result = [&v, esize, operand, shift, type] {
if (type == SaturatingShiftLeftType::Signed) {
return v.ir.VectorSignedSaturatedShiftLeft(esize, operand, shift);
}
if (type == SaturatingShiftLeftType::Unsigned) {
return v.ir.VectorUnsignedSaturatedShiftLeft(esize, operand, shift);
}
return v.ir.VectorSignedSaturatedShiftLeftUnsigned(esize, operand, shift);
}();
v.ir.SetQ(Vd, result);
return true;
}
bool ShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
ShiftExtraBehavior behavior, Signedness signedness) {
if (!immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 64;
const u8 shift_amount = static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend());
const IR::U64 operand = v.V_scalar(esize, Vn);
IR::U64 result = [&]() -> IR::U64 {
if (signedness == Signedness::Signed) {
return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount));
}
return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount));
}();
if (behavior == ShiftExtraBehavior::Accumulate) {
const IR::U64 addend = v.V_scalar(esize, Vd);
result = v.ir.Add(result, addend);
}
v.V_scalar(esize, Vd, result);
return true;
}
bool RoundingShiftRight(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
ShiftExtraBehavior behavior, Signedness signedness) {
if (!immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 64;
const u8 shift_amount = static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend());
const IR::U64 operand = v.V_scalar(esize, Vn);
const IR::U64 round_bit = v.ir.LogicalShiftRight(v.ir.LogicalShiftLeft(operand, v.ir.Imm8(64 - shift_amount)), v.ir.Imm8(63));
const IR::U64 result = [&] {
const IR::U64 shifted = [&]() -> IR::U64 {
if (signedness == Signedness::Signed) {
return v.ir.ArithmeticShiftRight(operand, v.ir.Imm8(shift_amount));
}
return v.ir.LogicalShiftRight(operand, v.ir.Imm8(shift_amount));
}();
IR::U64 tmp = v.ir.Add(shifted, round_bit);
if (behavior == ShiftExtraBehavior::Accumulate) {
tmp = v.ir.Add(tmp, v.V_scalar(esize, Vd));
}
return tmp;
}();
v.V_scalar(esize, Vd, result);
return true;
}
enum class ShiftDirection {
Left,
Right,
};
bool ShiftAndInsert(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
ShiftDirection direction) {
if (!immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 64;
const u8 shift_amount = [&] {
if (direction == ShiftDirection::Right) {
return static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend());
}
return static_cast<u8>(concatenate(immh, immb).ZeroExtend() - esize);
}();
const u64 mask = [&] {
if (direction == ShiftDirection::Right) {
return shift_amount == esize ? 0 : Common::Ones<u64>(esize) >> shift_amount;
}
return Common::Ones<u64>(esize) << shift_amount;
}();
const IR::U64 operand1 = v.V_scalar(esize, Vn);
const IR::U64 operand2 = v.V_scalar(esize, Vd);
const IR::U64 shifted = [&] {
if (direction == ShiftDirection::Right) {
return v.ir.LogicalShiftRight(operand1, v.ir.Imm8(shift_amount));
}
return v.ir.LogicalShiftLeft(operand1, v.ir.Imm8(shift_amount));
}();
const IR::U64 result = v.ir.Or(v.ir.And(operand2, v.ir.Not(v.ir.Imm64(mask))), shifted);
v.V_scalar(esize, Vd, result);
return true;
}
bool ShiftRightNarrowing(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
Narrowing narrowing, Signedness signedness) {
if (immh == 0b0000) {
return v.ReservedValue();
}
if (immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t source_esize = 2 * esize;
const u8 shift_amount = static_cast<u8>(source_esize - concatenate(immh, immb).ZeroExtend());
const IR::U128 operand = v.ir.ZeroExtendToQuad(v.ir.VectorGetElement(source_esize, v.V(128, Vn), 0));
IR::U128 wide_result = [&] {
if (signedness == Signedness::Signed) {
return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount);
}
return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount);
}();
const IR::U128 result = [&] {
switch (narrowing) {
case Narrowing::Truncation:
return v.ir.VectorNarrow(source_esize, wide_result);
case Narrowing::SaturateToUnsigned:
if (signedness == Signedness::Signed) {
return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result);
}
return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result);
case Narrowing::SaturateToSigned:
ASSERT(signedness == Signedness::Signed);
return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result);
}
UNREACHABLE();
}();
const IR::UAny segment = v.ir.VectorGetElement(esize, result, 0);
v.V_scalar(esize, Vd, segment);
return true;
}
bool ScalarFPConvertWithRound(TranslatorVisitor& v, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness sign, FloatConversionDirection direction, FP::RoundingMode rounding_mode) {
const u32 immh_value = immh.ZeroExtend();
if ((immh_value & 0b1110) == 0b0000) {
return v.ReservedValue();
}
// TODO: We currently don't handle FP16, so bail like the ARM reference manual allows.
if ((immh_value & 0b1110) == 0b0010) {
return v.ReservedValue();
}
const size_t esize = (immh_value & 0b1000) != 0 ? 64 : 32;
const size_t concat = concatenate(immh, immb).ZeroExtend();
const size_t fbits = (esize * 2) - concat;
const IR::U32U64 operand = v.V_scalar(esize, Vn);
const IR::U32U64 result = [&]() -> IR::U32U64 {
switch (direction) {
case FloatConversionDirection::FloatToFixed:
if (esize == 64) {
return sign == Signedness::Signed
? v.ir.FPToFixedS64(operand, fbits, rounding_mode)
: v.ir.FPToFixedU64(operand, fbits, rounding_mode);
}
return sign == Signedness::Signed
? v.ir.FPToFixedS32(operand, fbits, rounding_mode)
: v.ir.FPToFixedU32(operand, fbits, rounding_mode);
case FloatConversionDirection::FixedToFloat:
if (esize == 64) {
return sign == Signedness::Signed
? v.ir.FPSignedFixedToDouble(operand, fbits, rounding_mode)
: v.ir.FPUnsignedFixedToDouble(operand, fbits, rounding_mode);
}
return sign == Signedness::Signed
? v.ir.FPSignedFixedToSingle(operand, fbits, rounding_mode)
: v.ir.FPUnsignedFixedToSingle(operand, fbits, rounding_mode);
}
UNREACHABLE();
}();
v.V_scalar(esize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::FCVTZS_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTZU_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::SCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
}
bool TranslatorVisitor::UCVTF_fix_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
}
bool TranslatorVisitor::SLI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftAndInsert(*this, immh, immb, Vn, Vd, ShiftDirection::Left);
}
bool TranslatorVisitor::SRI_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftAndInsert(*this, immh, immb, Vn, Vd, ShiftDirection::Right);
}
bool TranslatorVisitor::SQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return SaturatingShiftLeft(*this, immh, immb, Vn, Vd, SaturatingShiftLeftType::Signed);
}
bool TranslatorVisitor::SQSHLU_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return SaturatingShiftLeft(*this, immh, immb, Vn, Vd, SaturatingShiftLeftType::SignedWithUnsignedSaturation);
}
bool TranslatorVisitor::SQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToSigned, Signedness::Signed);
}
bool TranslatorVisitor::SQSHRUN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Signed);
}
bool TranslatorVisitor::SRSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed);
}
bool TranslatorVisitor::SRSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Signed);
}
bool TranslatorVisitor::SSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SHL_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
if (!immh.Bit<3>()) {
return ReservedValue();
}
const size_t esize = 64;
const u8 shift_amount = static_cast<u8>(concatenate(immh, immb).ZeroExtend() - esize);
const IR::U64 operand = V_scalar(esize, Vn);
const IR::U64 result = ir.LogicalShiftLeft(operand, ir.Imm8(shift_amount));
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::UQSHL_imm_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return SaturatingShiftLeft(*this, immh, immb, Vn, Vd, SaturatingShiftLeftType::Unsigned);
}
bool TranslatorVisitor::UQSHRN_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, immh, immb, Vn, Vd, Narrowing::SaturateToUnsigned, Signedness::Unsigned);
}
bool TranslatorVisitor::URSHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned);
}
bool TranslatorVisitor::URSRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return RoundingShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned);
}
bool TranslatorVisitor::USHR_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::None, Signedness::Unsigned);
}
bool TranslatorVisitor::USRA_1(Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, immh, immb, Vn, Vd, ShiftExtraBehavior::Accumulate, Signedness::Unsigned);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,438 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <optional>
#include "common/bit_util.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class ComparisonType {
EQ,
GE,
GT,
HI,
HS,
LE,
LT,
};
enum class ComparisonVariant {
Register,
Zero,
};
enum class Signedness {
Signed,
Unsigned,
};
bool RoundingShiftLeft(TranslatorVisitor& v, Imm<2> size, Vec Vm, Vec Vn, Vec Vd, Signedness sign) {
if (size != 0b11) {
return v.ReservedValue();
}
const IR::U128 operand1 = v.V(64, Vn);
const IR::U128 operand2 = v.V(64, Vm);
const IR::U128 result = [&] {
if (sign == Signedness::Signed) {
return v.ir.VectorRoundingShiftLeftSigned(64, operand1, operand2);
}
return v.ir.VectorRoundingShiftLeftUnsigned(64, operand1, operand2);
}();
v.V(64, Vd, result);
return true;
}
bool ScalarCompare(TranslatorVisitor& v, Imm<2> size, std::optional<Vec> Vm, Vec Vn, Vec Vd,
ComparisonType type, ComparisonVariant variant) {
if (size != 0b11) {
return v.ReservedValue();
}
const size_t esize = 64;
const size_t datasize = 64;
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = variant == ComparisonVariant::Register ? v.V(datasize, *Vm) : v.ir.ZeroVector();
const IR::U128 result = [&] {
switch (type) {
case ComparisonType::EQ:
return v.ir.VectorEqual(esize, operand1, operand2);
case ComparisonType::GE:
return v.ir.VectorGreaterEqualSigned(esize, operand1, operand2);
case ComparisonType::GT:
return v.ir.VectorGreaterSigned(esize, operand1, operand2);
case ComparisonType::HI:
return v.ir.VectorGreaterUnsigned(esize, operand1, operand2);
case ComparisonType::HS:
return v.ir.VectorGreaterEqualUnsigned(esize, operand1, operand2);
case ComparisonType::LE:
return v.ir.VectorLessEqualSigned(esize, operand1, operand2);
case ComparisonType::LT:
default:
return v.ir.VectorLessSigned(esize, operand1, operand2);
}
}();
v.V_scalar(datasize, Vd, v.ir.VectorGetElement(esize, result, 0));
return true;
}
enum class FPComparisonType {
EQ,
GE,
AbsoluteGE,
GT,
AbsoluteGT
};
bool ScalarFPCompareRegister(TranslatorVisitor& v, bool sz, Vec Vm, Vec Vn, Vec Vd, FPComparisonType type) {
const size_t esize = sz ? 64 : 32;
const size_t datasize = esize;
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = v.V(datasize, Vm);
const IR::U128 result = [&] {
switch (type) {
case FPComparisonType::EQ:
return v.ir.FPVectorEqual(esize, operand1, operand2);
case FPComparisonType::GE:
return v.ir.FPVectorGreaterEqual(esize, operand1, operand2);
case FPComparisonType::AbsoluteGE:
return v.ir.FPVectorGreaterEqual(esize,
v.ir.FPVectorAbs(esize, operand1),
v.ir.FPVectorAbs(esize, operand2));
case FPComparisonType::GT:
return v.ir.FPVectorGreater(esize, operand1, operand2);
case FPComparisonType::AbsoluteGT:
return v.ir.FPVectorGreater(esize,
v.ir.FPVectorAbs(esize, operand1),
v.ir.FPVectorAbs(esize, operand2));
}
UNREACHABLE();
}();
v.V_scalar(datasize, Vd, v.ir.VectorGetElement(esize, result, 0));
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::SQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend<size_t>();
const IR::UAny operand1 = V_scalar(esize, Vn);
const IR::UAny operand2 = V_scalar(esize, Vm);
const auto result = ir.SignedSaturatedAdd(operand1, operand2);
ir.OrQC(result.overflow);
V_scalar(esize, Vd, result.result);
return true;
}
bool TranslatorVisitor::SQDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const IR::UAny operand1 = V_scalar(esize, Vn);
const IR::UAny operand2 = V_scalar(esize, Vm);
const auto result = ir.SignedSaturatedDoublingMultiplyReturnHigh(operand1, operand2);
ir.OrQC(result.overflow);
V_scalar(esize, Vd, result.result);
return true;
}
bool TranslatorVisitor::SQRDMULH_vec_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vm), 0));
const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, operand2);
const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));
V_scalar(esize, Vd, ir.VectorGetElement(esize, result, 0));
return true;
}
bool TranslatorVisitor::SQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend<size_t>();
const IR::UAny operand1 = V_scalar(esize, Vn);
const IR::UAny operand2 = V_scalar(esize, Vm);
const auto result = ir.SignedSaturatedSub(operand1, operand2);
ir.OrQC(result.overflow);
V_scalar(esize, Vd, result.result);
return true;
}
bool TranslatorVisitor::UQADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend();
const IR::UAny operand1 = V_scalar(esize, Vn);
const IR::UAny operand2 = V_scalar(esize, Vm);
const auto result = ir.UnsignedSaturatedAdd(operand1, operand2);
ir.OrQC(result.overflow);
V_scalar(esize, Vd, result.result);
return true;
}
bool TranslatorVisitor::UQSUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend();
const IR::UAny operand1 = V_scalar(esize, Vn);
const IR::UAny operand2 = V_scalar(esize, Vm);
const auto result = ir.UnsignedSaturatedSub(operand1, operand2);
ir.OrQC(result.overflow);
V_scalar(esize, Vd, result.result);
return true;
}
bool TranslatorVisitor::ADD_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = esize;
const IR::U64 operand1 = V_scalar(datasize, Vn);
const IR::U64 operand2 = V_scalar(datasize, Vm);
const IR::U64 result = ir.Add(operand1, operand2);
V_scalar(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::CMEQ_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::EQ, ComparisonVariant::Register);
}
bool TranslatorVisitor::CMEQ_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::EQ, ComparisonVariant::Zero);
}
bool TranslatorVisitor::CMGE_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::GE, ComparisonVariant::Register);
}
bool TranslatorVisitor::CMGE_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::GE, ComparisonVariant::Zero);
}
bool TranslatorVisitor::CMGT_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::GT, ComparisonVariant::Register);
}
bool TranslatorVisitor::CMGT_zero_1(Imm<2> size, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::GT, ComparisonVariant::Zero);
}
bool TranslatorVisitor::CMLE_1(Imm<2> size, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::LE, ComparisonVariant::Zero);
}
bool TranslatorVisitor::CMLT_1(Imm<2> size, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, {}, Vn, Vd, ComparisonType::LT, ComparisonVariant::Zero);
}
bool TranslatorVisitor::CMHI_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::HI, ComparisonVariant::Register);
}
bool TranslatorVisitor::CMHS_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return ScalarCompare(*this, size, Vm, Vn, Vd, ComparisonType::HS, ComparisonVariant::Register);
}
bool TranslatorVisitor::CMTST_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const IR::U128 operand1 = V(64, Vn);
const IR::U128 operand2 = V(64, Vm);
const IR::U128 anded = ir.VectorAnd(operand1, operand2);
const IR::U128 result = ir.VectorNot(ir.VectorEqual(64, anded, ir.ZeroVector()));
V(64, Vd, result);
return true;
}
bool TranslatorVisitor::FABD_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand1 = V_scalar(esize, Vn);
const IR::U32U64 operand2 = V_scalar(esize, Vm);
const IR::U32U64 result = ir.FPAbs(ir.FPSub(operand1, operand2));
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FMULX_vec_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand1 = V_scalar(esize, Vn);
const IR::U32U64 operand2 = V_scalar(esize, Vm);
const IR::U32U64 result = ir.FPMulX(operand1, operand2);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRECPS_1(Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 16;
const IR::U16 operand1 = V_scalar(esize, Vn);
const IR::U16 operand2 = V_scalar(esize, Vm);
const IR::U16 result = ir.FPRecipStepFused(operand1, operand2);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRECPS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand1 = V_scalar(esize, Vn);
const IR::U32U64 operand2 = V_scalar(esize, Vm);
const IR::U32U64 result = ir.FPRecipStepFused(operand1, operand2);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRSQRTS_1(Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 16;
const IR::U16 operand1 = V_scalar(esize, Vn);
const IR::U16 operand2 = V_scalar(esize, Vm);
const IR::U16 result = ir.FPRSqrtStepFused(operand1, operand2);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRSQRTS_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand1 = V_scalar(esize, Vn);
const IR::U32U64 operand2 = V_scalar(esize, Vm);
const IR::U32U64 result = ir.FPRSqrtStepFused(operand1, operand2);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FACGE_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::AbsoluteGE);
}
bool TranslatorVisitor::FACGT_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::AbsoluteGT);
}
bool TranslatorVisitor::FCMEQ_reg_1(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 lhs = V(128, Vn);
const IR::U128 rhs = V(128, Vm);
const IR::U128 result = ir.FPVectorEqual(16, lhs, rhs);
V_scalar(16, Vd, ir.VectorGetElement(16, result, 0));
return true;
}
bool TranslatorVisitor::FCMEQ_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::EQ);
}
bool TranslatorVisitor::FCMGE_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::GE);
}
bool TranslatorVisitor::FCMGT_reg_2(bool sz, Vec Vm, Vec Vn, Vec Vd) {
return ScalarFPCompareRegister(*this, sz, Vm, Vn, Vd, FPComparisonType::GT);
}
bool TranslatorVisitor::SQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 8U << size.ZeroExtend();
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vm), 0));
const IR::U128 result = ir.VectorSignedSaturatedShiftLeft(esize, operand1, operand2);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SRSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Signed);
}
bool TranslatorVisitor::SSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const IR::U128 operand1 = V(64, Vn);
const IR::U128 operand2 = V(64, Vm);
const IR::U128 result = ir.VectorArithmeticVShift(64, operand1, operand2);
V(64, Vd, result);
return true;
}
bool TranslatorVisitor::SUB_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = esize;
const IR::U64 operand1 = V_scalar(datasize, Vn);
const IR::U64 operand2 = V_scalar(datasize, Vm);
const IR::U64 result = ir.Sub(operand1, operand2);
V_scalar(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::UQSHL_reg_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
const size_t esize = 8U << size.ZeroExtend();
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vm), 0));
const IR::U128 result = ir.VectorUnsignedSaturatedShiftLeft(esize, operand1, operand2);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::URSHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return RoundingShiftLeft(*this, size, Vm, Vn, Vd, Signedness::Unsigned);
}
bool TranslatorVisitor::USHL_1(Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const IR::U128 operand1 = V(64, Vn);
const IR::U128 operand2 = V(64, Vm);
const IR::U128 result = ir.VectorLogicalVShift(64, operand1, operand2);
V(64, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,332 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class ComparisonType {
EQ,
GE,
GT,
LE,
LT
};
enum class Signedness {
Signed,
Unsigned
};
bool ScalarFPCompareAgainstZero(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd, ComparisonType type) {
const size_t esize = sz ? 64 : 32;
const size_t datasize = esize;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 zero = v.ir.ZeroVector();
const IR::U128 result = [&] {
switch (type) {
case ComparisonType::EQ:
return v.ir.FPVectorEqual(esize, operand, zero);
case ComparisonType::GE:
return v.ir.FPVectorGreaterEqual(esize, operand, zero);
case ComparisonType::GT:
return v.ir.FPVectorGreater(esize, operand, zero);
case ComparisonType::LE:
return v.ir.FPVectorGreaterEqual(esize, zero, operand);
case ComparisonType::LT:
return v.ir.FPVectorGreater(esize, zero, operand);
}
UNREACHABLE();
}();
v.V_scalar(datasize, Vd, v.ir.VectorGetElement(esize, result, 0));
return true;
}
bool ScalarFPConvertWithRound(TranslatorVisitor& v, bool sz, Vec Vn, Vec Vd,
FP::RoundingMode rmode, Signedness sign) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand = v.V_scalar(esize, Vn);
const IR::U32U64 result = [&]() -> IR::U32U64 {
if (sz) {
return sign == Signedness::Signed
? v.ir.FPToFixedS64(operand, 0, rmode)
: v.ir.FPToFixedU64(operand, 0, rmode);
}
return sign == Signedness::Signed
? v.ir.FPToFixedS32(operand, 0, rmode)
: v.ir.FPToFixedU32(operand, 0, rmode);
}();
v.V_scalar(esize, Vd, result);
return true;
}
using NarrowingFn = IR::U128 (IR::IREmitter::*)(size_t, const IR::U128&);
bool SaturatedNarrow(TranslatorVisitor& v, Imm<2> size, Vec Vn, Vec Vd, NarrowingFn fn) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 operand = v.ir.ZeroExtendToQuad(v.V_scalar(2 * esize, Vn));
const IR::U128 result = (v.ir.*fn)(2 * esize, operand);
v.V_scalar(64, Vd, v.ir.VectorGetElement(64, result, 0));
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::ABS_1(Imm<2> size, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const IR::U64 operand1 = V_scalar(64, Vn);
const IR::U64 operand2 = ir.ArithmeticShiftRight(operand1, ir.Imm8(63));
const IR::U64 result = ir.Sub(ir.Eor(operand1, operand2), operand2);
V_scalar(64, Vd, result);
return true;
}
bool TranslatorVisitor::FCMEQ_zero_1(Vec Vn, Vec Vd) {
const IR::U128 operand = ir.ZeroExtendToQuad(V_scalar(16, Vn));
const IR::U128 zero = ir.ZeroVector();
const IR::U128 result = ir.FPVectorEqual(16, operand, zero);
V_scalar(16, Vd, ir.VectorGetElement(16, result, 0));
return true;
}
bool TranslatorVisitor::FCMEQ_zero_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::EQ);
}
bool TranslatorVisitor::FCMGE_zero_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::GE);
}
bool TranslatorVisitor::FCMGT_zero_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::GT);
}
bool TranslatorVisitor::FCMLE_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::LE);
}
bool TranslatorVisitor::FCMLT_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPCompareAgainstZero(*this, sz, Vn, Vd, ComparisonType::LT);
}
bool TranslatorVisitor::FCVTAS_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, Signedness::Signed);
}
bool TranslatorVisitor::FCVTAU_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, Signedness::Unsigned);
}
bool TranslatorVisitor::FCVTMS_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, Signedness::Signed);
}
bool TranslatorVisitor::FCVTMU_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, Signedness::Unsigned);
}
bool TranslatorVisitor::FCVTNS_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, Signedness::Signed);
}
bool TranslatorVisitor::FCVTNU_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, Signedness::Unsigned);
}
bool TranslatorVisitor::FCVTPS_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Signed);
}
bool TranslatorVisitor::FCVTPU_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, Signedness::Unsigned);
}
bool TranslatorVisitor::FCVTXN_1(bool sz, Vec Vn, Vec Vd) {
if (!sz) {
return ReservedValue();
}
const IR::U64 element = V_scalar(64, Vn);
const IR::U32 result = ir.FPDoubleToSingle(element, FP::RoundingMode::ToOdd);
V_scalar(32, Vd, result);
return true;
}
bool TranslatorVisitor::FCVTZS_int_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Signed);
}
bool TranslatorVisitor::FCVTZU_int_2(bool sz, Vec Vn, Vec Vd) {
return ScalarFPConvertWithRound(*this, sz, Vn, Vd, FP::RoundingMode::TowardsZero, Signedness::Unsigned);
}
bool TranslatorVisitor::FRECPE_1(Vec Vn, Vec Vd) {
const size_t esize = 16;
const IR::U16 operand = V_scalar(esize, Vn);
const IR::U16 result = ir.FPRecipEstimate(operand);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRECPE_2(bool sz, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand = V_scalar(esize, Vn);
const IR::U32U64 result = ir.FPRecipEstimate(operand);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRECPX_1(Vec Vn, Vec Vd) {
const IR::U16 operand = V_scalar(16, Vn);
const IR::U16 result = ir.FPRecipExponent(operand);
V_scalar(16, Vd, result);
return true;
}
bool TranslatorVisitor::FRECPX_2(bool sz, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand = V_scalar(esize, Vn);
const IR::U32U64 result = ir.FPRecipExponent(operand);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRSQRTE_1(Vec Vn, Vec Vd) {
const size_t esize = 16;
const IR::U16 operand = V_scalar(esize, Vn);
const IR::U16 result = ir.FPRSqrtEstimate(operand);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::FRSQRTE_2(bool sz, Vec Vn, Vec Vd) {
const size_t esize = sz ? 64 : 32;
const IR::U32U64 operand = V_scalar(esize, Vn);
const IR::U32U64 result = ir.FPRSqrtEstimate(operand);
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::NEG_1(Imm<2> size, Vec Vn, Vec Vd) {
if (size != 0b11) {
return ReservedValue();
}
const IR::U64 operand = V_scalar(64, Vn);
const IR::U64 result = ir.Sub(ir.Imm64(0), operand);
V_scalar(64, Vd, result);
return true;
}
bool TranslatorVisitor::SCVTF_int_2(bool sz, Vec Vn, Vec Vd) {
const auto esize = sz ? 64 : 32;
const IR::U32U64 element = V_scalar(esize, Vn);
const IR::U32U64 result = esize == 32
? IR::U32U64(ir.FPSignedFixedToSingle(element, 0, ir.current_location->FPCR().RMode()))
: IR::U32U64(ir.FPSignedFixedToDouble(element, 0, ir.current_location->FPCR().RMode()));
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::SQABS_1(Imm<2> size, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 operand = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::U128 result = ir.VectorSignedSaturatedAbs(esize, operand);
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::SQNEG_1(Imm<2> size, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 operand = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::U128 result = ir.VectorSignedSaturatedNeg(esize, operand);
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::SQXTN_1(Imm<2> size, Vec Vn, Vec Vd) {
return SaturatedNarrow(*this, size, Vn, Vd, &IREmitter::VectorSignedSaturatedNarrowToSigned);
}
bool TranslatorVisitor::SQXTUN_1(Imm<2> size, Vec Vn, Vec Vd) {
return SaturatedNarrow(*this, size, Vn, Vd, &IREmitter::VectorSignedSaturatedNarrowToUnsigned);
}
bool TranslatorVisitor::SUQADD_1(Imm<2> size, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = 64;
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(datasize, Vn), 0));
const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(datasize, Vd), 0));
const IR::U128 result = ir.VectorSignedSaturatedAccumulateUnsigned(esize, operand1, operand2);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::UCVTF_int_2(bool sz, Vec Vn, Vec Vd) {
const auto esize = sz ? 64 : 32;
const IR::U32U64 element = V_scalar(esize, Vn);
const IR::U32U64 result = esize == 32
? IR::U32U64(ir.FPUnsignedFixedToSingle(element, 0, ir.current_location->FPCR().RMode()))
: IR::U32U64(ir.FPUnsignedFixedToDouble(element, 0, ir.current_location->FPCR().RMode()));
V_scalar(esize, Vd, result);
return true;
}
bool TranslatorVisitor::UQXTN_1(Imm<2> size, Vec Vn, Vec Vd) {
return SaturatedNarrow(*this, size, Vn, Vd, &IREmitter::VectorUnsignedSaturatedNarrow);
}
bool TranslatorVisitor::USQADD_1(Imm<2> size, Vec Vn, Vec Vd) {
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = 64;
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(datasize, Vn), 0));
const IR::U128 operand2 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(datasize, Vd), 0));
const IR::U128 result = ir.VectorUnsignedSaturatedAccumulateSigned(esize, operand1, operand2);
V(datasize, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,173 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <utility>
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
std::pair<size_t, Vec> Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) {
if (size == 0b01) {
return {concatenate(H, L, M).ZeroExtend(), Vmlo.ZeroExtend<Vec>()};
}
return {concatenate(H, L).ZeroExtend(), concatenate(M, Vmlo).ZeroExtend<Vec>()};
}
enum class ExtraBehavior {
None,
Accumulate,
Subtract,
MultiplyExtended,
};
bool MultiplyByElement(TranslatorVisitor& v, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
Vec Vn, Vec Vd, ExtraBehavior extra_behavior) {
if (sz && L == 1) {
return v.ReservedValue();
}
const size_t idxdsize = H == 1 ? 128 : 64;
const size_t index = sz ? H.ZeroExtend() : concatenate(H, L).ZeroExtend();
const Vec Vm = concatenate(M, Vmlo).ZeroExtend<Vec>();
const size_t esize = sz ? 64 : 32;
const IR::U32U64 element = v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index);
const IR::U32U64 result = [&]() -> IR::U32U64 {
IR::U32U64 operand1 = v.V_scalar(esize, Vn);
if (extra_behavior == ExtraBehavior::None) {
return v.ir.FPMul(operand1, element);
}
if (extra_behavior == ExtraBehavior::MultiplyExtended) {
return v.ir.FPMulX(operand1, element);
}
if (extra_behavior == ExtraBehavior::Subtract) {
operand1 = v.ir.FPNeg(operand1);
}
const IR::U32U64 operand2 = v.V_scalar(esize, Vd);
return v.ir.FPMulAdd(operand2, operand1, element);
}();
v.V_scalar(esize, Vd, result);
return true;
}
bool MultiplyByElementHalfPrecision(TranslatorVisitor& v, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
Vec Vn, Vec Vd, ExtraBehavior extra_behavior) {
const size_t esize = 16;
const size_t idxsize = H == 1 ? 128 : 64;
const size_t index = concatenate(H, L, M).ZeroExtend();
const auto Vm = Vmlo.ZeroExtend<Vec>();
const IR::U16 element = v.ir.VectorGetElement(esize, v.V(idxsize, Vm), index);
const IR::U16 result = [&]() -> IR::U16 {
IR::U16 operand1 = v.V_scalar(esize, Vn);
// TODO: Currently we don't implement half-precision paths
// for regular multiplication and extended multiplication.
if (extra_behavior == ExtraBehavior::None) {
ASSERT_FALSE("half-precision option unimplemented");
}
if (extra_behavior == ExtraBehavior::MultiplyExtended) {
ASSERT_FALSE("half-precision option unimplemented");
}
if (extra_behavior == ExtraBehavior::Subtract) {
operand1 = v.ir.FPNeg(operand1);
}
const IR::U16 operand2 = v.V_scalar(esize, Vd);
return v.ir.FPMulAdd(operand2, operand1, element);
}();
v.V_scalar(esize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::FMLA_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElementHalfPrecision(*this, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate);
}
bool TranslatorVisitor::FMLA_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate);
}
bool TranslatorVisitor::FMLS_elt_1(Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElementHalfPrecision(*this, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract);
}
bool TranslatorVisitor::FMLS_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract);
}
bool TranslatorVisitor::FMUL_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None);
}
bool TranslatorVisitor::FMULX_elt_2(bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::MultiplyExtended);
}
bool TranslatorVisitor::SQDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::UAny operand1 = V_scalar(esize, Vn);
const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index);
const auto result = ir.SignedSaturatedDoublingMultiplyReturnHigh(operand1, operand2);
ir.OrQC(result.overflow);
V_scalar(esize, Vd, result.result);
return true;
}
bool TranslatorVisitor::SQRDMULH_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index);
const IR::U128 broadcast = ir.VectorBroadcast(esize, operand2);
const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, broadcast);
const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::SQDMULL_elt_1(Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = ir.ZeroExtendToQuad(ir.VectorGetElement(esize, V(128, Vn), 0));
const IR::UAny operand2 = ir.VectorGetElement(esize, V(128, Vm), index);
const IR::U128 broadcast = ir.VectorBroadcast(esize, operand2);
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, broadcast);
V(128, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,261 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
IR::U32 SHAchoose(IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
return ir.Eor(ir.And(ir.Eor(y, z), x), z);
}
IR::U32 SHAmajority(IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
return ir.Or(ir.And(x, y), ir.And(ir.Or(x, y), z)) ;
}
IR::U32 SHAparity(IREmitter& ir, IR::U32 x, IR::U32 y, IR::U32 z) {
return ir.Eor(ir.Eor(y, z), x);
}
using SHA1HashUpdateFunction = IR::U32(IREmitter&, IR::U32, IR::U32, IR::U32);
IR::U128 SHA1HashUpdate(IREmitter& ir, Vec Vm, Vec Vn, Vec Vd, SHA1HashUpdateFunction fn) {
IR::U128 x = ir.GetQ(Vd);
IR::U32 y = ir.VectorGetElement(32, ir.GetQ(Vn), 0);
const IR::U128 w = ir.GetQ(Vm);
for (size_t i = 0; i < 4; i++) {
const IR::U32 low_x = ir.VectorGetElement(32, x, 0);
const IR::U32 after_low_x = ir.VectorGetElement(32, x, 1);
const IR::U32 before_high_x = ir.VectorGetElement(32, x, 2);
const IR::U32 high_x = ir.VectorGetElement(32, x, 3);
const IR::U32 t = fn(ir, after_low_x, before_high_x, high_x);
const IR::U32 w_segment = ir.VectorGetElement(32, w, i);
y = ir.Add(ir.Add(ir.Add(y, ir.RotateRight(low_x, ir.Imm8(27))), t), w_segment);
x = ir.VectorSetElement(32, x, 1, ir.RotateRight(after_low_x, ir.Imm8(2)));
// Move each 32-bit element to the left once
// e.g. [3, 2, 1, 0], becomes [2, 1, 0, 3]
const IR::U128 shuffled_x = ir.VectorShuffleWords(x, 0b10010011);
x = ir.VectorSetElement(32, shuffled_x, 0, y);
y = high_x;
}
return x;
}
IR::U32 SHAhashSIGMA0(IREmitter& ir, IR::U32 x) {
const IR::U32 tmp1 = ir.RotateRight(x, ir.Imm8(2));
const IR::U32 tmp2 = ir.RotateRight(x, ir.Imm8(13));
const IR::U32 tmp3 = ir.RotateRight(x, ir.Imm8(22));
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
}
IR::U32 SHAhashSIGMA1(IREmitter& ir, IR::U32 x) {
const IR::U32 tmp1 = ir.RotateRight(x, ir.Imm8(6));
const IR::U32 tmp2 = ir.RotateRight(x, ir.Imm8(11));
const IR::U32 tmp3 = ir.RotateRight(x, ir.Imm8(25));
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
}
enum class SHA256HashPart {
Part1,
Part2
};
IR::U128 SHA256hash(IREmitter& ir, IR::U128 x, IR::U128 y, IR::U128 w, SHA256HashPart part) {
for (size_t i = 0; i < 4; i++) {
const IR::U32 low_x = ir.VectorGetElement(32, x, 0);
const IR::U32 after_low_x = ir.VectorGetElement(32, x, 1);
const IR::U32 before_high_x = ir.VectorGetElement(32, x, 2);
const IR::U32 high_x = ir.VectorGetElement(32, x, 3);
const IR::U32 low_y = ir.VectorGetElement(32, y, 0);
const IR::U32 after_low_y = ir.VectorGetElement(32, y, 1);
const IR::U32 before_high_y = ir.VectorGetElement(32, y, 2);
const IR::U32 high_y = ir.VectorGetElement(32, y, 3);
const IR::U32 choice = SHAchoose(ir, low_y, after_low_y, before_high_y);
const IR::U32 majority = SHAmajority(ir, low_x, after_low_x, before_high_x);
const IR::U32 t = [&] {
const IR::U32 w_element = ir.VectorGetElement(32, w, i);
const IR::U32 sig = SHAhashSIGMA1(ir, low_y);
return ir.Add(high_y, ir.Add(sig, ir.Add(choice, w_element)));
}();
const IR::U32 new_low_x = ir.Add(t, ir.Add(SHAhashSIGMA0(ir, low_x), majority));
const IR::U32 new_low_y = ir.Add(t, high_x);
// Shuffle all words left by 1 element: [3, 2, 1, 0] -> [2, 1, 0, 3]
const IR::U128 shuffled_x = ir.VectorShuffleWords(x, 0b10010011);
const IR::U128 shuffled_y = ir.VectorShuffleWords(y, 0b10010011);
x = ir.VectorSetElement(32, shuffled_x, 0, new_low_x);
y = ir.VectorSetElement(32, shuffled_y, 0, new_low_y);
}
if (part == SHA256HashPart::Part1) {
return x;
}
return y;
}
} // Anonymous namespace
bool TranslatorVisitor::SHA1C(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA1HashUpdate(ir, Vm, Vn, Vd, SHAchoose);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA1M(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA1HashUpdate(ir, Vm, Vn, Vd, SHAmajority);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA1P(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA1HashUpdate(ir, Vm, Vn, Vd, SHAparity);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA1SU0(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 d = ir.GetQ(Vd);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
IR::U128 result = [&] {
const IR::U64 d_high = ir.VectorGetElement(64, d, 1);
const IR::U64 n_low = ir.VectorGetElement(64, n, 0);
const IR::U128 zero = ir.ZeroVector();
const IR::U128 tmp1 = ir.VectorSetElement(64, zero, 0, d_high);
return ir.VectorSetElement(64, tmp1, 1, n_low);
}();
result = ir.VectorEor(ir.VectorEor(result, d), m);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA1SU1(Vec Vn, Vec Vd) {
const IR::U128 d = ir.GetQ(Vd);
const IR::U128 n = ir.GetQ(Vn);
// Shuffle down the whole vector and zero out the top 32 bits
const IR::U128 shuffled_n = ir.VectorSetElement(32, ir.VectorShuffleWords(n, 0b00111001), 3, ir.Imm32(0));
const IR::U128 t = ir.VectorEor(d, shuffled_n);
const IR::U128 rotated_t = ir.VectorRotateLeft(32, t, 1);
const IR::U32 low_rotated_t = ir.RotateRight(ir.VectorGetElement(32, rotated_t, 0), ir.Imm8(31));
const IR::U32 high_t = ir.VectorGetElement(32, rotated_t, 3);
const IR::U128 result = ir.VectorSetElement(32, rotated_t, 3, ir.Eor(low_rotated_t, high_t));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA1H(Vec Vn, Vec Vd) {
const IR::U128 data = ir.GetS(Vn);
const IR::U128 result = ir.VectorOr(ir.VectorLogicalShiftLeft(32, data, 30),
ir.VectorLogicalShiftRight(32, data, 2));
ir.SetS(Vd, result);
return true;
}
bool TranslatorVisitor::SHA256SU0(Vec Vn, Vec Vd) {
const IR::U128 d = ir.GetQ(Vd);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 t = [&] {
// Shuffle the upper three elements down: [3, 2, 1, 0] -> [0, 3, 2, 1]
const IR::U128 shuffled = ir.VectorShuffleWords(d, 0b00111001);
return ir.VectorSetElement(32, shuffled, 3, ir.VectorGetElement(32, n, 0));
}();
IR::U128 result = ir.ZeroVector();
for (size_t i = 0; i < 4; i++) {
const IR::U32 modified_element = [&] {
const IR::U32 element = ir.VectorGetElement(32, t, i);
const IR::U32 tmp1 = ir.RotateRight(element, ir.Imm8(7));
const IR::U32 tmp2 = ir.RotateRight(element, ir.Imm8(18));
const IR::U32 tmp3 = ir.LogicalShiftRight(element, ir.Imm8(3));
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
}();
const IR::U32 d_element = ir.VectorGetElement(32, d, i);
result = ir.VectorSetElement(32, result, i, ir.Add(modified_element, d_element));
}
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA256SU1(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 d = ir.GetQ(Vd);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 T0 = [&] {
const IR::U32 low_m = ir.VectorGetElement(32, m, 0);
const IR::U128 shuffled_n = ir.VectorShuffleWords(n, 0b00111001);
return ir.VectorSetElement(32, shuffled_n, 3, low_m);
}();
const IR::U128 lower_half = [&] {
const IR::U128 T = ir.VectorShuffleWords(m, 0b01001110);
const IR::U128 tmp1 = ir.VectorRotateRight(32, T, 17);
const IR::U128 tmp2 = ir.VectorRotateRight(32, T, 19);
const IR::U128 tmp3 = ir.VectorLogicalShiftRight(32, T, 10);
const IR::U128 tmp4 = ir.VectorEor(tmp1, ir.VectorEor(tmp2, tmp3));
const IR::U128 tmp5 = ir.VectorAdd(32, tmp4, ir.VectorAdd(32, d, T0));
return ir.VectorZeroUpper(tmp5);
}();
const IR::U64 upper_half = [&] {
const IR::U128 tmp1 = ir.VectorRotateRight(32, lower_half, 17);
const IR::U128 tmp2 = ir.VectorRotateRight(32, lower_half, 19);
const IR::U128 tmp3 = ir.VectorLogicalShiftRight(32, lower_half, 10);
const IR::U128 tmp4 = ir.VectorEor(tmp1, ir.VectorEor(tmp2, tmp3));
// Shuffle the top two 32-bit elements downwards [3, 2, 1, 0] -> [1, 0, 3, 2]
const IR::U128 shuffled_d = ir.VectorShuffleWords(d, 0b01001110);
const IR::U128 shuffled_T0 = ir.VectorShuffleWords(T0, 0b01001110);
const IR::U128 tmp5 = ir.VectorAdd(32, tmp4, ir.VectorAdd(32, shuffled_d, shuffled_T0));
return ir.VectorGetElement(64, tmp5, 0);
}();
const IR::U128 result = ir.VectorSetElement(64, lower_half, 1, upper_half);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA256H(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA256hash(ir, ir.GetQ(Vd), ir.GetQ(Vn), ir.GetQ(Vm), SHA256HashPart::Part1);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA256H2(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA256hash(ir, ir.GetQ(Vn), ir.GetQ(Vd), ir.GetQ(Vm), SHA256HashPart::Part2);
ir.SetQ(Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,300 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
IR::U64 MakeSig(IREmitter& ir, IR::U64 data, u8 first_rot_amount, u8 second_rot_amount, u8 shift_amount) {
const IR::U64 tmp1 = ir.RotateRight(data, ir.Imm8(first_rot_amount));
const IR::U64 tmp2 = ir.RotateRight(data, ir.Imm8(second_rot_amount));
const IR::U64 tmp3 = ir.LogicalShiftRight(data, ir.Imm8(shift_amount));
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
}
IR::U64 MakeMNSig(IREmitter& ir, IR::U64 data, u8 first_rot_amount, u8 second_rot_amount, u8 third_rot_amount) {
const IR::U64 tmp1 = ir.RotateRight(data, ir.Imm8(first_rot_amount));
const IR::U64 tmp2 = ir.RotateRight(data, ir.Imm8(second_rot_amount));
const IR::U64 tmp3 = ir.RotateRight(data, ir.Imm8(third_rot_amount));
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
}
enum class SHA512HashPart {
Part1,
Part2,
};
IR::U128 SHA512Hash(IREmitter& ir, Vec Vm, Vec Vn, Vec Vd, SHA512HashPart part) {
const IR::U128 x = ir.GetQ(Vn);
const IR::U128 y = ir.GetQ(Vm);
const IR::U128 w = ir.GetQ(Vd);
const IR::U64 lower_x = ir.VectorGetElement(64, x, 0);
const IR::U64 upper_x = ir.VectorGetElement(64, x, 1);
const IR::U64 lower_y = ir.VectorGetElement(64, y, 0);
const IR::U64 upper_y = ir.VectorGetElement(64, y, 1);
const auto make_sigma = [&](IR::U64 data) {
if (part == SHA512HashPart::Part1) {
return MakeMNSig(ir, data, 14, 18, 41);
}
return MakeMNSig(ir, data, 28, 34, 39);
};
const auto make_partial_half = [&](IR::U64 a, IR::U64 b, IR::U64 c) {
const IR::U64 tmp1 = ir.And(a, b);
if (part == SHA512HashPart::Part1) {
const IR::U64 tmp2 = ir.And(ir.Not(a), c);
return ir.Eor(tmp1, tmp2);
}
const IR::U64 tmp2 = ir.And(a, c);
const IR::U64 tmp3 = ir.And(upper_y, lower_y);
return ir.Eor(tmp1, ir.Eor(tmp2, tmp3));
};
const IR::U64 Vtmp = [&] {
const IR::U64 partial = [&] {
if (part == SHA512HashPart::Part1) {
return make_partial_half(upper_y, lower_x, upper_x);
}
return make_partial_half(lower_x, upper_y, lower_y);
}();
const IR::U64 upper_w = ir.VectorGetElement(64, w, 1);
const IR::U64 sig = [&] {
if (part == SHA512HashPart::Part1) {
return make_sigma(upper_y);
}
return make_sigma(lower_y);
}();
return ir.Add(partial, ir.Add(sig, upper_w));
}();
const IR::U128 low_result = [&] {
const IR::U64 tmp = [&]() -> IR::U64 {
if (part == SHA512HashPart::Part1) {
return ir.Add(Vtmp, lower_y);
}
return Vtmp;
}();
const IR::U64 partial = [&] {
if (part == SHA512HashPart::Part1) {
return make_partial_half(tmp, upper_y, lower_x);
}
return make_partial_half(Vtmp, lower_y, upper_y);
}();
const IR::U64 sig = make_sigma(tmp);
const IR::U64 lower_w = ir.VectorGetElement(64, w, 0);
return ir.ZeroExtendToQuad(ir.Add(partial, ir.Add(sig, lower_w)));
}();
return ir.VectorSetElement(64, low_result, 1, Vtmp);
}
enum class SM4RotationType {
SM4E,
SM4EKEY
};
IR::U32 SM4Rotation(IREmitter& ir, IR::U32 intval, IR::U32 round_result_low_word, SM4RotationType type) {
if (type == SM4RotationType::SM4E) {
const IR::U32 tmp1 = ir.RotateRight(intval, ir.Imm8(30));
const IR::U32 tmp2 = ir.RotateRight(intval, ir.Imm8(22));
const IR::U32 tmp3 = ir.RotateRight(intval, ir.Imm8(14));
const IR::U32 tmp4 = ir.RotateRight(intval, ir.Imm8(8));
const IR::U32 tmp5 = ir.Eor(intval, ir.Eor(tmp1, ir.Eor(tmp2, ir.Eor(tmp3, tmp4))));
return ir.Eor(tmp5, round_result_low_word);
}
const IR::U32 tmp1 = ir.RotateRight(intval, ir.Imm8(19));
const IR::U32 tmp2 = ir.RotateRight(intval, ir.Imm8(9));
return ir.Eor(round_result_low_word, ir.Eor(intval, ir.Eor(tmp1, tmp2)));
}
IR::U128 SM4Hash(IREmitter& ir, Vec Vn, Vec Vd, SM4RotationType type) {
const IR::U128 n = ir.GetQ(Vn);
IR::U128 roundresult = ir.GetQ(Vd);
for (size_t i = 0; i < 4; i++) {
const IR::U32 round_key = ir.VectorGetElement(32, n, i);
const IR::U32 upper_round = ir.VectorGetElement(32, roundresult, 3);
const IR::U32 before_upper_round = ir.VectorGetElement(32, roundresult, 2);
const IR::U32 after_lower_round = ir.VectorGetElement(32, roundresult, 1);
IR::U128 intval_vec = ir.ZeroExtendToQuad(ir.Eor(upper_round, ir.Eor(before_upper_round, ir.Eor(after_lower_round, round_key))));
for (size_t j = 0; j < 4; j++) {
const IR::U8 byte_element = ir.VectorGetElement(8, intval_vec, j);
intval_vec = ir.VectorSetElement(8, intval_vec, j, ir.SM4AccessSubstitutionBox(byte_element));
}
const IR::U32 intval_low_word = ir.VectorGetElement(32, intval_vec, 0);
const IR::U32 round_result_low_word = ir.VectorGetElement(32, roundresult, 0);
const IR::U32 intval = SM4Rotation(ir, intval_low_word, round_result_low_word, type);
roundresult = ir.VectorShuffleWords(roundresult, 0b00111001);
roundresult = ir.VectorSetElement(32, roundresult, 3, intval);
}
return roundresult;
}
} // Anonymous namespace
bool TranslatorVisitor::SHA512SU0(Vec Vn, Vec Vd) {
const IR::U128 x = ir.GetQ(Vn);
const IR::U128 w = ir.GetQ(Vd);
const IR::U64 lower_x = ir.VectorGetElement(64, x, 0);
const IR::U64 lower_w = ir.VectorGetElement(64, w, 0);
const IR::U64 upper_w = ir.VectorGetElement(64, w, 1);
const auto make_sig0 = [&](IR::U64 data) {
return MakeSig(ir, data, 1, 8, 7);
};
const IR::U128 low_result = ir.ZeroExtendToQuad(ir.Add(lower_w, make_sig0(upper_w)));
const IR::U64 high_result = ir.Add(upper_w, make_sig0(lower_x));
const IR::U128 result = ir.VectorSetElement(64, low_result, 1, high_result);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA512SU1(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 x = ir.GetQ(Vn);
const IR::U128 y = ir.GetQ(Vm);
const IR::U128 w = ir.GetQ(Vd);
const auto make_sig1 = [&](IR::U64 data) {
return MakeSig(ir, data, 19, 61, 6);
};
const IR::U128 sig_vector = [&] {
const IR::U64 lower_x = ir.VectorGetElement(64, x, 0);
const IR::U64 upper_x = ir.VectorGetElement(64, x, 1);
const IR::U128 low_result = ir.ZeroExtendToQuad(make_sig1(lower_x));
return ir.VectorSetElement(64, low_result, 1, make_sig1(upper_x));
}();
const IR::U128 result = ir.VectorAdd(64, w, ir.VectorAdd(64, y, sig_vector));
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA512H(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA512Hash(ir, Vm, Vn, Vd, SHA512HashPart::Part1);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SHA512H2(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 result = SHA512Hash(ir, Vm, Vn, Vd, SHA512HashPart::Part2);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::RAX1(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 rotated_m = ir.VectorRotateLeft(64, m, 1);
const IR::U128 result = ir.VectorEor(n, rotated_m);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::XAR(Vec Vm, Imm<6> imm6, Vec Vn, Vec Vd) {
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 tmp = ir.VectorEor(m, n);
const IR::U128 result = ir.VectorRotateRight(64, tmp, imm6.ZeroExtend<u8>());
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SM3PARTW1(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 d = ir.GetQ(Vd);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 eor_d_n = ir.VectorEor(d, n);
const IR::U128 result_low_three_words = [&] {
// Move the top-most 3 words down one element (i.e. [3, 2, 1, 0] -> [0, 3, 2, 1])
const IR::U128 shuffled_m = ir.VectorShuffleWords(m, 0b00111001);
// We treat the uppermost word as junk data and don't touch/use it explicitly for now.
// Given we don't do anything with it yet, the fact we EOR into it doesn't matter.
return ir.VectorEor(eor_d_n, ir.VectorRotateLeft(32, shuffled_m, 15));
}();
IR::U128 result = result_low_three_words;
for (size_t i = 0; i < 4; i++) {
if (i == 3) {
const IR::U32 top_eor_d_n = ir.VectorGetElement(32, eor_d_n, 3);
const IR::U32 low_result_word = ir.VectorGetElement(32, result, 0);
const IR::U32 top_result_word = ir.Eor(top_eor_d_n, ir.RotateRight(low_result_word, ir.Imm8(17)));
// Now the uppermost word is well-defined data.
result = ir.VectorSetElement(32, result, 3, top_result_word);
}
const IR::U32 word = ir.VectorGetElement(32, result, i);
const IR::U32 modified = ir.Eor(word, ir.Eor(ir.RotateRight(word, ir.Imm8(17)),
ir.RotateRight(word, ir.Imm8(9))));
result = ir.VectorSetElement(32, result, i, modified);
}
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SM3PARTW2(Vec Vm, Vec Vn, Vec Vd) {
const IR::U128 d = ir.GetQ(Vd);
const IR::U128 m = ir.GetQ(Vm);
const IR::U128 n = ir.GetQ(Vn);
const IR::U128 temp = ir.VectorEor(n, ir.VectorRotateLeft(32, m, 7));
const IR::U128 temp_result = ir.VectorEor(d, temp);
const IR::U32 temp2 = [&] {
const IR::U32 rotate1 = ir.RotateRight(ir.VectorGetElement(32, temp, 0), ir.Imm8(17));
const IR::U32 rotate2 = ir.RotateRight(rotate1, ir.Imm8(17));
const IR::U32 rotate3 = ir.RotateRight(rotate1, ir.Imm8(9));
return ir.Eor(rotate1, ir.Eor(rotate2, rotate3));
}();
const IR::U32 high_temp_result = ir.VectorGetElement(32, temp_result, 3);
const IR::U32 replacement = ir.Eor(high_temp_result, temp2);
const IR::U128 result = ir.VectorSetElement(32, temp_result, 3, replacement);
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::SM4E(Vec Vn, Vec Vd) {
ir.SetQ(Vd, SM4Hash(ir, Vn, Vd, SM4RotationType::SM4E));
return true;
}
bool TranslatorVisitor::SM4EKEY(Vec Vm, Vec Vn, Vec Vd) {
ir.SetQ(Vd, SM4Hash(ir, Vm, Vn, SM4RotationType::SM4EKEY));
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,404 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/bit_util.h"
#include "common/fp/rounding_mode.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class Rounding {
None,
Round
};
enum class Accumulating {
None,
Accumulate
};
enum class Signedness {
Signed,
Unsigned
};
enum class Narrowing {
Truncation,
SaturateToUnsigned,
SaturateToSigned,
};
enum class SaturatingShiftLeftType {
Signed,
Unsigned,
SignedWithUnsignedSaturation,
};
enum class FloatConversionDirection {
FixedToFloat,
FloatToFixed,
};
IR::U128 PerformRoundingCorrection(TranslatorVisitor& v, size_t esize, u64 round_value, IR::U128 original, IR::U128 shifted) {
const IR::U128 round_const = v.ir.VectorBroadcast(esize, v.I(esize, round_value));
const IR::U128 round_correction = v.ir.VectorEqual(esize, v.ir.VectorAnd(original, round_const), round_const);
return v.ir.VectorSub(esize, shifted, round_correction);
}
bool ShiftRight(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
Rounding rounding, Accumulating accumulating, Signedness signedness) {
if (immh == 0b0000) {
return v.DecodeError();
}
if (immh.Bit<3>() && !Q) {
return v.ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = Q ? 128 : 64;
const u8 shift_amount = static_cast<u8>(2 * esize) - concatenate(immh, immb).ZeroExtend<u8>();
const IR::U128 operand = v.V(datasize, Vn);
IR::U128 result = [&] {
if (signedness == Signedness::Signed) {
return v.ir.VectorArithmeticShiftRight(esize, operand, shift_amount);
}
return v.ir.VectorLogicalShiftRight(esize, operand, shift_amount);
}();
if (rounding == Rounding::Round) {
const u64 round_value = 1ULL << (shift_amount - 1);
result = PerformRoundingCorrection(v, esize, round_value, operand, result);
}
if (accumulating == Accumulating::Accumulate) {
const IR::U128 accumulator = v.V(datasize, Vd);
result = v.ir.VectorAdd(esize, result, accumulator);
}
v.V(datasize, Vd, result);
return true;
}
bool ShiftRightNarrowing(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
Rounding rounding, Narrowing narrowing, Signedness signedness) {
if (immh == 0b0000) {
return v.DecodeError();
}
if (immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t source_esize = 2 * esize;
const size_t part = Q ? 1 : 0;
const u8 shift_amount = static_cast<u8>(source_esize - concatenate(immh, immb).ZeroExtend());
const IR::U128 operand = v.V(128, Vn);
IR::U128 wide_result = [&] {
if (signedness == Signedness::Signed) {
return v.ir.VectorArithmeticShiftRight(source_esize, operand, shift_amount);
}
return v.ir.VectorLogicalShiftRight(source_esize, operand, shift_amount);
}();
if (rounding == Rounding::Round) {
const u64 round_value = 1ULL << (shift_amount - 1);
wide_result = PerformRoundingCorrection(v, source_esize, round_value, operand, wide_result);
}
const IR::U128 result = [&] {
switch (narrowing) {
case Narrowing::Truncation:
return v.ir.VectorNarrow(source_esize, wide_result);
case Narrowing::SaturateToUnsigned:
if (signedness == Signedness::Signed) {
return v.ir.VectorSignedSaturatedNarrowToUnsigned(source_esize, wide_result);
}
return v.ir.VectorUnsignedSaturatedNarrow(source_esize, wide_result);
case Narrowing::SaturateToSigned:
ASSERT(signedness == Signedness::Signed);
return v.ir.VectorSignedSaturatedNarrowToSigned(source_esize, wide_result);
}
UNREACHABLE();
}();
v.Vpart(64, Vd, part, result);
return true;
}
bool ShiftLeftLong(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd,
Signedness signedness) {
if (immh == 0b0000) {
return v.DecodeError();
}
if (immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = 64;
const size_t part = Q ? 1 : 0;
const u8 shift_amount = concatenate(immh, immb).ZeroExtend<u8>() - static_cast<u8>(esize);
const IR::U128 operand = v.Vpart(datasize, Vn, part);
const IR::U128 expanded_operand = [&] {
if (signedness == Signedness::Signed) {
return v.ir.VectorSignExtend(esize, operand);
}
return v.ir.VectorZeroExtend(esize, operand);
}();
const IR::U128 result = v.ir.VectorLogicalShiftLeft(2 * esize, expanded_operand, shift_amount);
v.V(2 * datasize, Vd, result);
return true;
}
bool SaturatingShiftLeft(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, SaturatingShiftLeftType type) {
if (!Q && immh.Bit<3>()) {
return v.ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = Q ? 128 : 64;
const size_t shift = concatenate(immh, immb).ZeroExtend() - esize;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 shift_vec = v.ir.VectorBroadcast(esize, v.I(esize, shift));
const IR::U128 result = [&] {
if (type == SaturatingShiftLeftType::Signed) {
return v.ir.VectorSignedSaturatedShiftLeft(esize, operand, shift_vec);
}
if (type == SaturatingShiftLeftType::Unsigned) {
return v.ir.VectorUnsignedSaturatedShiftLeft(esize, operand, shift_vec);
}
return v.ir.VectorSignedSaturatedShiftLeftUnsigned(esize, operand, shift_vec);
}();
v.V(datasize, Vd, result);
return true;
}
bool ConvertFloat(TranslatorVisitor& v, bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd, Signedness signedness, FloatConversionDirection direction, FP::RoundingMode rounding_mode) {
if (immh == 0b0000) {
return v.DecodeError();
}
if (immh == 0b0001 || immh == 0b0010 || immh == 0b0011) {
return v.ReservedValue();
}
if (immh.Bit<3>() && !Q) {
return v.ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = Q ? 128 : 64;
const u8 fbits = static_cast<u8>(esize * 2) - concatenate(immh, immb).ZeroExtend<u8>();
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 result = [&] {
switch (direction) {
case FloatConversionDirection::FixedToFloat:
return signedness == Signedness::Signed
? v.ir.FPVectorFromSignedFixed(esize, operand, fbits, rounding_mode)
: v.ir.FPVectorFromUnsignedFixed(esize, operand, fbits, rounding_mode);
case FloatConversionDirection::FloatToFixed:
return signedness == Signedness::Signed
? v.ir.FPVectorToSignedFixed(esize, operand, fbits, rounding_mode)
: v.ir.FPVectorToUnsignedFixed(esize, operand, fbits, rounding_mode);
}
UNREACHABLE();
}();
v.V(datasize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::SSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Signed);
}
bool TranslatorVisitor::SRSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Signed);
}
bool TranslatorVisitor::SRSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SHL_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
if (immh == 0b0000) {
return DecodeError();
}
if (immh.Bit<3>() && !Q) {
return ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = Q ? 128 : 64;
const u8 shift_amount = concatenate(immh, immb).ZeroExtend<u8>() - static_cast<u8>(esize);
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.VectorLogicalShiftLeft(esize, operand, shift_amount);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::Truncation, Signedness::Unsigned);
}
bool TranslatorVisitor::RSHRN(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::Truncation, Signedness::Unsigned);
}
bool TranslatorVisitor::SQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Signed);
}
bool TranslatorVisitor::SQSHLU_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::SignedWithUnsignedSaturation);
}
bool TranslatorVisitor::SQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToSigned, Signedness::Signed);
}
bool TranslatorVisitor::SQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToSigned, Signedness::Signed);
}
bool TranslatorVisitor::SQSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Signed);
}
bool TranslatorVisitor::SQRSHRUN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Signed);
}
bool TranslatorVisitor::UQSHL_imm_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return SaturatingShiftLeft(*this, Q, immh, immb, Vn, Vd, SaturatingShiftLeftType::Unsigned);
}
bool TranslatorVisitor::UQSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::None, Narrowing::SaturateToUnsigned, Signedness::Unsigned);
}
bool TranslatorVisitor::UQRSHRN_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRightNarrowing(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Narrowing::SaturateToUnsigned, Signedness::Unsigned);
}
bool TranslatorVisitor::SSHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Signed);
}
bool TranslatorVisitor::URSHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::None, Signedness::Unsigned);
}
bool TranslatorVisitor::URSRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::Round, Accumulating::Accumulate, Signedness::Unsigned);
}
bool TranslatorVisitor::USHR_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::None, Signedness::Unsigned);
}
bool TranslatorVisitor::USRA_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftRight(*this, Q, immh, immb, Vn, Vd, Rounding::None, Accumulating::Accumulate, Signedness::Unsigned);
}
bool TranslatorVisitor::USHLL(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ShiftLeftLong(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned);
}
bool TranslatorVisitor::SRI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
if (immh == 0b0000) {
return DecodeError();
}
if (!Q && immh.Bit<3>()) {
return ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = Q ? 128 : 64;
const u8 shift_amount = static_cast<u8>((esize * 2) - concatenate(immh, immb).ZeroExtend<u8>());
const u64 mask = shift_amount == esize ? 0 : Common::Ones<u64>(esize) >> shift_amount;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vd);
const IR::U128 shifted = ir.VectorLogicalShiftRight(esize, operand1, shift_amount);
const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const IR::U128 result = ir.VectorOr(ir.VectorAnd(operand2, ir.VectorNot(mask_vec)), shifted);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SLI_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
if (immh == 0b0000) {
return DecodeError();
}
if (!Q && immh.Bit<3>()) {
return ReservedValue();
}
const size_t esize = 8 << Common::HighestSetBit(immh.ZeroExtend());
const size_t datasize = Q ? 128 : 64;
const u8 shift_amount = concatenate(immh, immb).ZeroExtend<u8>() - static_cast<u8>(esize);
const u64 mask = Common::Ones<u64>(esize) << shift_amount;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vd);
const IR::U128 shifted = ir.VectorLogicalShiftLeft(esize, operand1, shift_amount);
const IR::U128 mask_vec = ir.VectorBroadcast(esize, I(esize, mask));
const IR::U128 result = ir.VectorOr(ir.VectorAnd(operand2, ir.VectorNot(mask_vec)), shifted);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
}
bool TranslatorVisitor::UCVTF_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FixedToFloat, ir.current_location->FPCR().RMode());
}
bool TranslatorVisitor::FCVTZS_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Signed, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTZU_fix_2(bool Q, Imm<4> immh, Imm<3> immb, Vec Vn, Vec Vd) {
return ConvertFloat(*this, Q, immh, immb, Vn, Vd, Signedness::Unsigned, FloatConversionDirection::FloatToFixed, FP::RoundingMode::TowardsZero);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,38 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool TableLookup(TranslatorVisitor& v, bool Q, Vec Vm, Imm<2> len, bool is_tbl, size_t Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const IR::Table table = v.ir.VectorTable([&]{
std::vector<IR::U128> result;
for (size_t i = 0; i < len.ZeroExtend<size_t>() + 1; ++i) {
result.emplace_back(v.ir.GetQ(static_cast<Vec>((Vn + i) % 32)));
}
return result;
}());
const IR::U128 indicies = v.ir.GetQ(Vm);
const IR::U128 defaults = is_tbl ? v.ir.ZeroVector() : v.ir.GetQ(Vd);
const IR::U128 result = v.ir.VectorTableLookup(defaults, table, indicies);
v.V(datasize, Vd, datasize == 128 ? result : v.ir.VectorZeroUpper(result));
return true;
}
bool TranslatorVisitor::TBL(bool Q, Vec Vm, Imm<2> len, size_t Vn, Vec Vd) {
return TableLookup(*this, Q, Vm, len, true, Vn, Vd);
}
bool TranslatorVisitor::TBX(bool Q, Vec Vm, Imm<2> len, size_t Vn, Vec Vd) {
return TableLookup(*this, Q, Vm, len, false, Vn, Vd);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,267 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class AbsoluteDifferenceBehavior {
None,
Accumulate
};
enum class Signedness {
Signed,
Unsigned
};
bool AbsoluteDifferenceLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd,
AbsoluteDifferenceBehavior behavior, Signedness sign) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = 64;
const IR::U128 operand1 = v.ir.VectorZeroExtend(esize, v.Vpart(datasize, Vn, Q));
const IR::U128 operand2 = v.ir.VectorZeroExtend(esize, v.Vpart(datasize, Vm, Q));
IR::U128 result = sign == Signedness::Signed ? v.ir.VectorSignedAbsoluteDifference(esize, operand1, operand2)
: v.ir.VectorUnsignedAbsoluteDifference(esize, operand1, operand2);
if (behavior == AbsoluteDifferenceBehavior::Accumulate) {
const IR::U128 data = v.V(2 * datasize, Vd);
result = v.ir.VectorAdd(2 * esize, result, data);
}
v.V(2 * datasize, Vd, result);
return true;
}
enum class MultiplyLongBehavior {
None,
Accumulate,
Subtract
};
bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd,
MultiplyLongBehavior behavior, Signedness sign) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t doubled_esize = 2 * esize;
const size_t datasize = 64;
const size_t doubled_datasize = datasize * 2;
const auto get_operands = [&] {
const auto p1 = v.Vpart(datasize, Vn, Q);
const auto p2 = v.Vpart(datasize, Vm, Q);
if (sign == Signedness::Signed) {
return std::make_pair(v.ir.VectorSignExtend(esize, p1),
v.ir.VectorSignExtend(esize, p2));
}
return std::make_pair(v.ir.VectorZeroExtend(esize, p1),
v.ir.VectorZeroExtend(esize, p2));
};
const auto [operand1, operand2] = get_operands();
IR::U128 result = v.ir.VectorMultiply(doubled_esize, operand1, operand2);
if (behavior == MultiplyLongBehavior::Accumulate) {
const IR::U128 addend = v.V(doubled_datasize, Vd);
result = v.ir.VectorAdd(doubled_esize, addend, result);
} else if (behavior == MultiplyLongBehavior::Subtract) {
const IR::U128 minuend = v.V(doubled_datasize, Vd);
result = v.ir.VectorSub(doubled_esize, minuend, result);
}
v.V(doubled_datasize, Vd, result);
return true;
}
enum class LongOperationBehavior {
Addition,
Subtraction
};
bool LongOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd,
LongOperationBehavior behavior, Signedness sign) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t part = Q ? 1 : 0;
const auto get_operand = [&](Vec vec) {
const IR::U128 tmp = v.Vpart(64, vec, part);
if (sign == Signedness::Signed) {
return v.ir.VectorSignExtend(esize, tmp);
}
return v.ir.VectorZeroExtend(esize, tmp);
};
const IR::U128 operand1 = get_operand(Vn);
const IR::U128 operand2 = get_operand(Vm);
const IR::U128 result = [&] {
if (behavior == LongOperationBehavior::Addition) {
return v.ir.VectorAdd(esize * 2, operand1, operand2);
}
return v.ir.VectorSub(esize * 2, operand1, operand2);
}();
v.V(128, Vd, result);
return true;
}
enum class WideOperationBehavior {
Addition,
Subtraction
};
bool WideOperation(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd,
WideOperationBehavior behavior, Signedness sign) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t part = Q ? 1 : 0;
const IR::U128 operand1 = v.V(128, Vn);
const IR::U128 operand2 = [&] {
const IR::U128 tmp = v.Vpart(64, Vm, part);
if (sign == Signedness::Signed) {
return v.ir.VectorSignExtend(esize, tmp);
}
return v.ir.VectorZeroExtend(esize, tmp);
}();
const IR::U128 result = [&] {
if (behavior == WideOperationBehavior::Addition) {
return v.ir.VectorAdd(esize * 2, operand1, operand2);
}
return v.ir.VectorSub(esize * 2, operand1, operand2);
}();
v.V(128, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::PMULL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b01 || size == 0b10) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = 64;
const IR::U128 operand1 = Vpart(datasize, Vn, Q);
const IR::U128 operand2 = Vpart(datasize, Vm, Q);
const IR::U128 result = ir.VectorPolynomialMultiplyLong(esize, operand1, operand2);
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::SABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, Signedness::Signed);
}
bool TranslatorVisitor::SADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, Signedness::Signed);
}
bool TranslatorVisitor::SADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, Signedness::Signed);
}
bool TranslatorVisitor::SMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, Signedness::Signed);
}
bool TranslatorVisitor::SMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, Signedness::Signed);
}
bool TranslatorVisitor::SSUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, Signedness::Signed);
}
bool TranslatorVisitor::SSUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, Signedness::Signed);
}
bool TranslatorVisitor::UADDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Addition, Signedness::Unsigned);
}
bool TranslatorVisitor::UABAL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::Accumulate, Signedness::Unsigned);
}
bool TranslatorVisitor::UABDL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return AbsoluteDifferenceLong(*this, Q, size, Vm, Vn, Vd, AbsoluteDifferenceBehavior::None, Signedness::Unsigned);
}
bool TranslatorVisitor::UADDW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Addition, Signedness::Unsigned);
}
bool TranslatorVisitor::UMLAL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Accumulate, Signedness::Unsigned);
}
bool TranslatorVisitor::UMLSL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::Subtract, Signedness::Unsigned);
}
bool TranslatorVisitor::UMULL_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, Vm, Vn, Vd, MultiplyLongBehavior::None, Signedness::Unsigned);
}
bool TranslatorVisitor::USUBW(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return WideOperation(*this, Q, size, Vm, Vn, Vd, WideOperationBehavior::Subtraction, Signedness::Unsigned);
}
bool TranslatorVisitor::USUBL(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return LongOperation(*this, Q, size, Vm, Vn, Vd, LongOperationBehavior::Subtraction, Signedness::Unsigned);
}
bool TranslatorVisitor::SQDMULL_vec_2(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t part = Q ? 1 : 0;
const IR::U128 operand1 = Vpart(64, Vn, part);
const IR::U128 operand2 = Vpart(64, Vm, part);
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, operand2);
V(128, Vd, result);
return true;
}
} // namespace Dynarmic::A64

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
using ExtensionFunction = IR::U32 (IREmitter::*)(const IR::UAny&);
bool DotProduct(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd,
ExtensionFunction extension) {
if (size != 0b10) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = v.V(datasize, Vm);
IR::U128 result = v.V(datasize, Vd);
for (size_t i = 0; i < elements; i++) {
IR::U32 res_element = v.ir.Imm32(0);
for (size_t j = 0; j < 4; j++) {
const IR::U32 elem1 = (v.ir.*extension)(v.ir.VectorGetElement(8, operand1, 4 * i + j));
const IR::U32 elem2 = (v.ir.*extension)(v.ir.VectorGetElement(8, operand2, 4 * i + j));
res_element = v.ir.Add(res_element, v.ir.Mul(elem1, elem2));
}
res_element = v.ir.Add(v.ir.VectorGetElement(32, result, i), res_element);
result = v.ir.VectorSetElement(32, result, i, res_element);
}
v.V(datasize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::SDOT_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return DotProduct(*this, Q, size, Vm, Vn, Vd, &IREmitter::SignExtendToWord);
}
bool TranslatorVisitor::UDOT_vec(bool Q, Imm<2> size, Vec Vm, Vec Vn, Vec Vd) {
return DotProduct(*this, Q, size, Vm, Vn, Vd, &IREmitter::ZeroExtendToWord);
}
bool TranslatorVisitor::FCMLA_vec(bool Q, Imm<2> size, Vec Vm, Imm<2> rot, Vec Vn, Vec Vd) {
if (size == 0) {
return ReservedValue();
}
if (!Q && size == 0b11) {
return ReservedValue();
}
const size_t esize = 8U << size.ZeroExtend();
// TODO: Currently we don't support half-precision floating point
if (esize == 16) {
return InterpretThisInstruction();
}
const size_t datasize = Q ? 128 : 64;
const size_t num_elements = datasize / esize;
const size_t num_iterations = num_elements / 2;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
const IR::U128 operand3 = V(datasize, Vd);
IR::U128 result = ir.ZeroVector();
IR::U32U64 element1;
IR::U32U64 element2;
IR::U32U64 element3;
IR::U32U64 element4;
for (size_t e = 0; e < num_iterations; ++e) {
const size_t first = e * 2;
const size_t second = first + 1;
switch (rot.ZeroExtend()) {
case 0b00: // 0 degrees
element1 = ir.VectorGetElement(esize, operand2, first);
element2 = ir.VectorGetElement(esize, operand1, first);
element3 = ir.VectorGetElement(esize, operand2, second);
element4 = ir.VectorGetElement(esize, operand1, first);
break;
case 0b01: // 90 degrees
element1 = ir.FPNeg(ir.VectorGetElement(esize, operand2, second));
element2 = ir.VectorGetElement(esize, operand1, second);
element3 = ir.VectorGetElement(esize, operand2, first);
element4 = ir.VectorGetElement(esize, operand1, second);
break;
case 0b10: // 180 degrees
element1 = ir.FPNeg(ir.VectorGetElement(esize, operand2, first));
element2 = ir.VectorGetElement(esize, operand1, first);
element3 = ir.FPNeg(ir.VectorGetElement(esize, operand2, second));
element4 = ir.VectorGetElement(esize, operand1, first);
break;
case 0b11: // 270 degrees
element1 = ir.VectorGetElement(esize, operand2, second);
element2 = ir.VectorGetElement(esize, operand1, second);
element3 = ir.FPNeg(ir.VectorGetElement(esize, operand2, first));
element4 = ir.VectorGetElement(esize, operand1, second);
break;
}
const IR::U32U64 operand3_elem1 = ir.VectorGetElement(esize, operand3, first);
const IR::U32U64 operand3_elem2 = ir.VectorGetElement(esize, operand3, second);
result = ir.VectorSetElement(esize, result, first, ir.FPMulAdd(operand3_elem1, element2, element1));
result = ir.VectorSetElement(esize, result, second, ir.FPMulAdd(operand3_elem2, element4, element3));
}
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::FCADD_vec(bool Q, Imm<2> size, Vec Vm, Imm<1> rot, Vec Vn, Vec Vd) {
if (size == 0) {
return ReservedValue();
}
if (!Q && size == 0b11) {
return ReservedValue();
}
const size_t esize = 8U << size.ZeroExtend();
// TODO: Currently we don't support half-precision floating point
if (esize == 16) {
return InterpretThisInstruction();
}
const size_t datasize = Q ? 128 : 64;
const size_t num_elements = datasize / esize;
const size_t num_iterations = num_elements / 2;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
IR::U128 result = ir.ZeroVector();
IR::U32U64 element1;
IR::U32U64 element3;
for (size_t e = 0; e < num_iterations; ++e) {
const size_t first = e * 2;
const size_t second = first + 1;
if (rot == 0) {
element1 = ir.FPNeg(ir.VectorGetElement(esize, operand2, second));
element3 = ir.VectorGetElement(esize, operand2, first);
} else if (rot == 1) {
element1 = ir.VectorGetElement(esize, operand2, second);
element3 = ir.FPNeg(ir.VectorGetElement(esize, operand2, first));
}
const IR::U32U64 operand1_elem1 = ir.VectorGetElement(esize, operand1, first);
const IR::U32U64 operand1_elem3 = ir.VectorGetElement(esize, operand1, second);
result = ir.VectorSetElement(esize, result, first, ir.FPAdd(operand1_elem1, element1));
result = ir.VectorSetElement(esize, result, second, ir.FPAdd(operand1_elem3, element3));
}
ir.SetQ(Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,885 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "common/fp/rounding_mode.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
enum class ComparisonType {
EQ,
GE,
GT,
LE,
LT,
};
bool CompareAgainstZero(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, ComparisonType type) {
if (size == 0b11 && !Q) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 zero = v.ir.ZeroVector();
IR::U128 result = [&] {
switch (type) {
case ComparisonType::EQ:
return v.ir.VectorEqual(esize, operand, zero);
case ComparisonType::GE:
return v.ir.VectorGreaterEqualSigned(esize, operand, zero);
case ComparisonType::GT:
return v.ir.VectorGreaterSigned(esize, operand, zero);
case ComparisonType::LE:
return v.ir.VectorLessEqualSigned(esize, operand, zero);
case ComparisonType::LT:
default:
return v.ir.VectorLessSigned(esize, operand, zero);
}
}();
if (datasize == 64) {
result = v.ir.VectorZeroUpper(result);
}
v.V(datasize, Vd, result);
return true;
}
bool FPCompareAgainstZero(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, ComparisonType type) {
if (sz && !Q) {
return v.ReservedValue();
}
const size_t esize = sz ? 64 : 32;
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 zero = v.ir.ZeroVector();
const IR::U128 result = [&] {
switch (type) {
case ComparisonType::EQ:
return v.ir.FPVectorEqual(esize, operand, zero);
case ComparisonType::GE:
return v.ir.FPVectorGreaterEqual(esize, operand, zero);
case ComparisonType::GT:
return v.ir.FPVectorGreater(esize, operand, zero);
case ComparisonType::LE:
return v.ir.FPVectorGreaterEqual(esize, zero, operand);
case ComparisonType::LT:
return v.ir.FPVectorGreater(esize, zero, operand);
}
UNREACHABLE();
}();
v.V(datasize, Vd, result);
return true;
}
enum class Signedness {
Signed,
Unsigned
};
bool IntegerConvertToFloat(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, Signedness signedness) {
if (sz && !Q) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const FP::RoundingMode rounding_mode = v.ir.current_location->FPCR().RMode();
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 result = signedness == Signedness::Signed
? v.ir.FPVectorFromSignedFixed(esize, operand, 0, rounding_mode)
: v.ir.FPVectorFromUnsignedFixed(esize, operand, 0, rounding_mode);
v.V(datasize, Vd, result);
return true;
}
bool FloatConvertToInteger(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, Signedness signedness, FP::RoundingMode rounding_mode) {
if (sz && !Q) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 result = signedness == Signedness::Signed
? v.ir.FPVectorToSignedFixed(esize, operand, 0, rounding_mode)
: v.ir.FPVectorToUnsignedFixed(esize, operand, 0, rounding_mode);
v.V(datasize, Vd, result);
return true;
}
bool FloatRoundToIntegral(TranslatorVisitor& v, bool Q, bool sz, Vec Vn, Vec Vd, FP::RoundingMode rounding_mode, bool exact) {
if (sz && !Q) {
return v.ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 result = v.ir.FPVectorRoundInt(esize, operand, rounding_mode, exact);
v.V(datasize, Vd, result);
return true;
}
bool FloatRoundToIntegralHalfPrecision(TranslatorVisitor& v, bool Q, Vec Vn, Vec Vd, FP::RoundingMode rounding_mode, bool exact) {
const size_t datasize = Q ? 128 : 64;
const size_t esize = 16;
const IR::U128 operand = v.V(datasize, Vn);
const IR::U128 result = v.ir.FPVectorRoundInt(esize, operand, rounding_mode, exact);
v.V(datasize, Vd, result);
return true;
}
bool SaturatedNarrow(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, IR::U128 (IR::IREmitter::*fn)(size_t, const IR::U128&)) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = 64;
const size_t part = Q ? 1 : 0;
const IR::U128 operand = v.V(2 * datasize, Vn);
const IR::U128 result = (v.ir.*fn)(2 * esize, operand);
v.Vpart(datasize, Vd, part, result);
return true;
}
enum class PairedAddLongExtraBehavior {
None,
Accumulate,
};
bool PairedAddLong(TranslatorVisitor& v, bool Q, Imm<2> size, Vec Vn, Vec Vd, Signedness sign,
PairedAddLongExtraBehavior behavior) {
if (size == 0b11) {
return v.ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = v.V(datasize, Vn);
IR::U128 result = [&] {
if (sign == Signedness::Signed) {
return v.ir.VectorPairedAddSignedWiden(esize, operand);
}
return v.ir.VectorPairedAddUnsignedWiden(esize, operand);
}();
if (behavior == PairedAddLongExtraBehavior::Accumulate) {
result = v.ir.VectorAdd(esize * 2, v.V(datasize, Vd), result);
}
if (datasize == 64) {
result = v.ir.VectorZeroUpper(result);
}
v.V(datasize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::CLS_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 shifted = ir.VectorArithmeticShiftRight(esize, operand, static_cast<u8>(esize));
const IR::U128 xored = ir.VectorEor(operand, shifted);
const IR::U128 clz = ir.VectorCountLeadingZeros(esize, xored);
IR::U128 result = ir.VectorSub(esize, clz, ir.VectorBroadcast(esize, I(esize, 1)));
if (datasize == 64) {
result = ir.VectorZeroUpper(result);
}
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::CLZ_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
IR::U128 result = ir.VectorCountLeadingZeros(esize, operand);
if (datasize == 64) {
result = ir.VectorZeroUpper(result);
}
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::CNT(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size != 0b00) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.VectorPopulationCount(operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::CMGE_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::GE);
}
bool TranslatorVisitor::CMGT_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::GT);
}
bool TranslatorVisitor::CMEQ_zero_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::EQ);
}
bool TranslatorVisitor::CMLE_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::LE);
}
bool TranslatorVisitor::CMLT_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return CompareAgainstZero(*this, Q, size, Vn, Vd, ComparisonType::LT);
}
bool TranslatorVisitor::ABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (!Q && size == 0b11) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 data = V(datasize, Vn);
const IR::U128 result = ir.VectorAbs(esize, data);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::XTN(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = 64;
const size_t part = Q ? 1 : 0;
const IR::U128 operand = V(2 * datasize, Vn);
const IR::U128 result = ir.VectorNarrow(2 * esize, operand);
Vpart(datasize, Vd, part, result);
return true;
}
bool TranslatorVisitor::FABS_1(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const size_t esize = 16;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorAbs(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FABS_2(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz && !Q) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorAbs(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FCMEQ_zero_3(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 zero = ir.ZeroVector();
const IR::U128 result = ir.FPVectorEqual(16, operand, zero);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FCMEQ_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::EQ);
}
bool TranslatorVisitor::FCMGE_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::GE);
}
bool TranslatorVisitor::FCMGT_zero_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::GT);
}
bool TranslatorVisitor::FCMLE_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::LE);
}
bool TranslatorVisitor::FCMLT_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FPCompareAgainstZero(*this, Q, sz, Vn, Vd, ComparisonType::LT);
}
bool TranslatorVisitor::FCVTL(bool Q, bool sz, Vec Vn, Vec Vd) {
const size_t esize = sz ? 32 : 16;
const size_t datasize = 64;
const size_t num_elements = datasize / esize;
const IR::U128 part = Vpart(64, Vn, Q);
const auto rounding_mode = ir.current_location->FPCR().RMode();
IR::U128 result = ir.ZeroVector();
for (size_t i = 0; i < num_elements; i++) {
IR::U16U32U64 element = ir.VectorGetElement(esize, part, i);
if (esize == 16) {
element = ir.FPHalfToSingle(element, rounding_mode);
} else if (esize == 32) {
element = ir.FPSingleToDouble(element, rounding_mode);
}
result = ir.VectorSetElement(2 * esize, result, i, element);
}
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::FCVTN(bool Q, bool sz, Vec Vn, Vec Vd) {
const size_t datasize = 64;
const size_t esize = sz ? 32 : 16;
const size_t num_elements = datasize / esize;
const IR::U128 operand = V(128, Vn);
const auto rounding_mode = ir.current_location->FPCR().RMode();
IR::U128 result = ir.ZeroVector();
for (size_t i = 0; i < num_elements; i++) {
IR::U16U32U64 element = ir.VectorGetElement(2 * esize, operand, i);
if (esize == 16) {
element = ir.FPSingleToHalf(element, rounding_mode);
} else if (esize == 32) {
element = ir.FPDoubleToSingle(element, rounding_mode);
}
result = ir.VectorSetElement(esize, result, i, element);
}
Vpart(datasize, Vd, Q, result);
return true;
}
bool TranslatorVisitor::FCVTNS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::ToNearest_TieEven);
}
bool TranslatorVisitor::FCVTMS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsMinusInfinity);
}
bool TranslatorVisitor::FCVTAS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::ToNearest_TieAwayFromZero);
}
bool TranslatorVisitor::FCVTPS_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsPlusInfinity);
}
bool TranslatorVisitor::FCVTXN_2(bool Q, bool sz, Vec Vn, Vec Vd) {
if (!sz) {
return UnallocatedEncoding();
}
const size_t part = Q ? 1 : 0;
const auto operand = ir.GetQ(Vn);
auto result = ir.ZeroVector();
for (size_t e = 0; e < 2; ++e) {
const IR::U64 element = ir.VectorGetElement(64, operand, e);
const IR::U32 converted = ir.FPDoubleToSingle(element, FP::RoundingMode::ToOdd);
result = ir.VectorSetElement(32, result, e, converted);
}
Vpart(64, Vd, part, result);
return true;
}
bool TranslatorVisitor::FCVTZS_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Signed, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FCVTNU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::ToNearest_TieEven);
}
bool TranslatorVisitor::FCVTMU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsMinusInfinity);
}
bool TranslatorVisitor::FCVTAU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::ToNearest_TieAwayFromZero);
}
bool TranslatorVisitor::FCVTPU_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsPlusInfinity);
}
bool TranslatorVisitor::FCVTZU_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatConvertToInteger(*this, Q, sz, Vn, Vd, Signedness::Unsigned, FP::RoundingMode::TowardsZero);
}
bool TranslatorVisitor::FRINTN_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, false);
}
bool TranslatorVisitor::FRINTN_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieEven, false);
}
bool TranslatorVisitor::FRINTM_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, false);
}
bool TranslatorVisitor::FRINTM_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd, FP::RoundingMode::TowardsMinusInfinity, false);
}
bool TranslatorVisitor::FRINTP_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, false);
}
bool TranslatorVisitor::FRINTP_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd, FP::RoundingMode::TowardsPlusInfinity, false);
}
bool TranslatorVisitor::FRINTZ_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, FP::RoundingMode::TowardsZero, false);
}
bool TranslatorVisitor::FRINTZ_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd, FP::RoundingMode::TowardsZero, false);
}
bool TranslatorVisitor::FRINTA_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, false);
}
bool TranslatorVisitor::FRINTA_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd, FP::RoundingMode::ToNearest_TieAwayFromZero, false);
}
bool TranslatorVisitor::FRINTX_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, ir.current_location->FPCR().RMode(), true);
}
bool TranslatorVisitor::FRINTX_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd, ir.current_location->FPCR().RMode(), true);
}
bool TranslatorVisitor::FRINTI_1(bool Q, Vec Vn, Vec Vd) {
return FloatRoundToIntegralHalfPrecision(*this, Q, Vn, Vd, ir.current_location->FPCR().RMode(), false);
}
bool TranslatorVisitor::FRINTI_2(bool Q, bool sz, Vec Vn, Vec Vd) {
return FloatRoundToIntegral(*this, Q, sz, Vn, Vd,ir.current_location->FPCR().RMode(), false);
}
bool TranslatorVisitor::FRECPE_3(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const size_t esize = 16;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorRecipEstimate(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FRECPE_4(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz && !Q) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorRecipEstimate(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FSQRT_2(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz && !Q) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorSqrt(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FRSQRTE_3(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const size_t esize = 16;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorRSqrtEstimate(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FRSQRTE_4(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz && !Q) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.FPVectorRSqrtEstimate(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNEG_1(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 mask = ir.VectorBroadcast(64, I(64, 0x8000800080008000));
const IR::U128 result = ir.VectorEor(operand, mask);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::FNEG_2(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz && !Q) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = sz ? 64 : 32;
const size_t mask_value = esize == 64 ? 0x8000000000000000 : 0x8000000080000000;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 mask = Q ? ir.VectorBroadcast(esize, I(esize, mask_value)) : ir.VectorBroadcastLower(esize, I(esize, mask_value));
const IR::U128 result = ir.VectorEor(operand, mask);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::NEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend<size_t>();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 zero = ir.ZeroVector();
const IR::U128 result = ir.VectorSub(esize, zero, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SQXTUN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return SaturatedNarrow(*this, Q, size, Vn, Vd, &IR::IREmitter::VectorSignedSaturatedNarrowToUnsigned);
}
bool TranslatorVisitor::SQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return SaturatedNarrow(*this, Q, size, Vn, Vd, &IR::IREmitter::VectorSignedSaturatedNarrowToSigned);
}
bool TranslatorVisitor::UQXTN_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return SaturatedNarrow(*this, Q, size, Vn, Vd, &IR::IREmitter::VectorUnsignedSaturatedNarrow);
}
bool TranslatorVisitor::NOT(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
IR::U128 result = ir.VectorNot(operand);
if (datasize == 64) {
result = ir.VectorZeroUpper(result);
}
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::RBIT_asimd(bool Q, Vec Vn, Vec Vd) {
const size_t datasize = Q ? 128 : 64;
const IR::U128 data = V(datasize, Vn);
const IR::U128 result = ir.VectorReverseBits(data);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::REV16_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size != 0) {
return UnallocatedEncoding();
}
const size_t datasize = Q ? 128 : 64;
constexpr size_t esize = 16;
const IR::U128 data = V(datasize, Vn);
const IR::U128 result = ir.VectorOr(ir.VectorLogicalShiftRight(esize, data, 8),
ir.VectorLogicalShiftLeft(esize, data, 8));
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::REV32_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
const u32 zext_size = size.ZeroExtend();
if (zext_size > 1) {
return UnallocatedEncoding();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 16 << zext_size;
const u8 shift = static_cast<u8>(8 << zext_size);
const IR::U128 data = V(datasize, Vn);
// TODO: Consider factoring byte swapping code out into its own opcode.
// Technically the rest of the following code can be a PSHUFB
// in the presence of SSSE3.
IR::U128 result = ir.VectorOr(ir.VectorLogicalShiftRight(esize, data, shift),
ir.VectorLogicalShiftLeft(esize, data, shift));
// If dealing with 8-bit elements we'll need to shuffle the bytes in each halfword
// e.g. Assume the following numbers point out bytes in a 32-bit word, we're essentially
// changing [3, 2, 1, 0] to [2, 3, 0, 1]
if (zext_size == 0) {
result = ir.VectorShuffleLowHalfwords(result, 0b10110001);
result = ir.VectorShuffleHighHalfwords(result, 0b10110001);
}
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::REV64_asimd(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
const u32 zext_size = size.ZeroExtend();
if (zext_size >= 3) {
return UnallocatedEncoding();
}
const size_t datasize = Q ? 128 : 64;
const size_t esize = 16 << zext_size;
const u8 shift = static_cast<u8>(8 << zext_size);
const IR::U128 data = V(datasize, Vn);
// TODO: Consider factoring byte swapping code out into its own opcode.
// Technically the rest of the following code can be a PSHUFB
// in the presence of SSSE3.
IR::U128 result = ir.VectorOr(ir.VectorLogicalShiftRight(esize, data, shift),
ir.VectorLogicalShiftLeft(esize, data, shift));
switch (zext_size) {
case 0: // 8-bit elements
result = ir.VectorShuffleLowHalfwords(result, 0b00011011);
result = ir.VectorShuffleHighHalfwords(result, 0b00011011);
break;
case 1: // 16-bit elements
result = ir.VectorShuffleLowHalfwords(result, 0b01001110);
result = ir.VectorShuffleHighHalfwords(result, 0b01001110);
break;
}
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SQABS_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.VectorSignedSaturatedAbs(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SQNEG_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.VectorSignedSaturatedNeg(esize, operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SUQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vd);
const IR::U128 result = ir.VectorSignedSaturatedAccumulateUnsigned(esize, operand1, operand2);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::USQADD_2(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11 && !Q) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vd);
const IR::U128 result = ir.VectorUnsignedSaturatedAccumulateSigned(esize, operand1, operand2);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Signed, PairedAddLongExtraBehavior::Accumulate);
}
bool TranslatorVisitor::SADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Signed, PairedAddLongExtraBehavior::None);
}
bool TranslatorVisitor::UADALP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Unsigned, PairedAddLongExtraBehavior::Accumulate);
}
bool TranslatorVisitor::UADDLP(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
return PairedAddLong(*this, Q, size, Vn, Vd, Signedness::Unsigned, PairedAddLongExtraBehavior::None);
}
bool TranslatorVisitor::URECPE(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.VectorUnsignedRecipEstimate(operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::URSQRTE(bool Q, bool sz, Vec Vn, Vec Vd) {
if (sz) {
return ReservedValue();
}
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand = V(datasize, Vn);
const IR::U128 result = ir.VectorUnsignedRecipSqrtEstimate(operand);
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, Signedness::Signed);
}
bool TranslatorVisitor::UCVTF_int_4(bool Q, bool sz, Vec Vn, Vec Vd) {
return IntegerConvertToFloat(*this, Q, sz, Vn, Vd, Signedness::Unsigned);
}
bool TranslatorVisitor::SHLL(bool Q, Imm<2> size, Vec Vn, Vec Vd) {
if (size == 0b11) {
return ReservedValue();
}
const size_t esize = 8 << size.ZeroExtend();
const IR::U128 operand = ir.VectorZeroExtend(esize, Vpart(64, Vn, Q));
const IR::U128 result = ir.VectorLogicalShiftLeft(esize * 2, operand, static_cast<u8>(esize));
V(128, Vd, result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,422 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <utility>
#include "common/assert.h"
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
namespace {
std::pair<size_t, Vec> Combine(Imm<2> size, Imm<1> H, Imm<1> L, Imm<1> M, Imm<4> Vmlo) {
if (size == 0b01) {
return {concatenate(H, L, M).ZeroExtend(), Vmlo.ZeroExtend<Vec>()};
}
return {concatenate(H, L).ZeroExtend(), concatenate(M, Vmlo).ZeroExtend<Vec>()};
}
enum class ExtraBehavior {
None,
Extended,
Accumulate,
Subtract,
};
bool MultiplyByElement(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd,
ExtraBehavior extra_behavior) {
if (size != 0b01 && size != 0b10) {
return v.ReservedValue();
}
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const size_t idxdsize = H == 1 ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = v.ir.VectorBroadcast(esize, v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index));
const IR::U128 operand3 = v.V(datasize, Vd);
IR::U128 result = v.ir.VectorMultiply(esize, operand1, operand2);
if (extra_behavior == ExtraBehavior::Accumulate) {
result = v.ir.VectorAdd(esize, operand3, result);
} else if (extra_behavior == ExtraBehavior::Subtract) {
result = v.ir.VectorSub(esize, operand3, result);
}
v.V(datasize, Vd, result);
return true;
}
bool FPMultiplyByElement(TranslatorVisitor& v, bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd,
ExtraBehavior extra_behavior) {
if (sz && L == 1) {
return v.ReservedValue();
}
if (sz && !Q) {
return v.ReservedValue();
}
const size_t idxdsize = H == 1 ? 128 : 64;
const size_t index = sz ? H.ZeroExtend() : concatenate(H, L).ZeroExtend();
const Vec Vm = concatenate(M, Vmlo).ZeroExtend<Vec>();
const size_t esize = sz ? 64 : 32;
const size_t datasize = Q ? 128 : 64;
const IR::UAny element2 = v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = Q ? v.ir.VectorBroadcast(esize, element2) : v.ir.VectorBroadcastLower(esize, element2);
const IR::U128 operand3 = v.V(datasize, Vd);
const IR::U128 result = [&]{
switch (extra_behavior) {
case ExtraBehavior::None:
return v.ir.FPVectorMul(esize, operand1, operand2);
case ExtraBehavior::Extended:
return v.ir.FPVectorMulX(esize, operand1, operand2);
case ExtraBehavior::Accumulate:
return v.ir.FPVectorMulAdd(esize, operand3, operand1, operand2);
case ExtraBehavior::Subtract:
return v.ir.FPVectorMulAdd(esize, operand3, v.ir.FPVectorNeg(esize, operand1), operand2);
}
UNREACHABLE();
}();
v.V(datasize, Vd, result);
return true;
}
bool FPMultiplyByElementHalfPrecision(TranslatorVisitor& v, bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
Vec Vn, Vec Vd, ExtraBehavior extra_behavior) {
const size_t idxdsize = H == 1 ? 128 : 64;
const size_t index = concatenate(H, L, M).ZeroExtend();
const Vec Vm = Vmlo.ZeroExtend<Vec>();
const size_t esize = 16;
const size_t datasize = Q ? 128 : 64;
const IR::UAny element2 = v.ir.VectorGetElement(esize, v.V(idxdsize, Vm), index);
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = Q ? v.ir.VectorBroadcast(esize, element2) : v.ir.VectorBroadcastLower(esize, element2);
const IR::U128 operand3 = v.V(datasize, Vd);
// TODO: We currently don't implement half-precision paths for
// regular multiplies and extended multiplies.
const IR::U128 result = [&]{
switch (extra_behavior) {
case ExtraBehavior::None:
break;
case ExtraBehavior::Extended:
break;
case ExtraBehavior::Accumulate:
return v.ir.FPVectorMulAdd(esize, operand3, operand1, operand2);
case ExtraBehavior::Subtract:
return v.ir.FPVectorMulAdd(esize, operand3, v.ir.FPVectorNeg(esize, operand1), operand2);
}
UNREACHABLE();
}();
v.V(datasize, Vd, result);
return true;
}
using ExtensionFunction = IR::U32 (IREmitter::*)(const IR::UAny&);
bool DotProduct(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H,
Vec Vn, Vec Vd, ExtensionFunction extension) {
if (size != 0b10) {
return v.ReservedValue();
}
const Vec Vm = concatenate(M, Vmlo).ZeroExtend<Vec>();
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const size_t elements = datasize / esize;
const size_t index = concatenate(H, L).ZeroExtend();
const IR::U128 operand1 = v.V(datasize, Vn);
const IR::U128 operand2 = v.V(128, Vm);
IR::U128 result = v.V(datasize, Vd);
for (size_t i = 0; i < elements; i++) {
IR::U32 res_element = v.ir.Imm32(0);
for (size_t j = 0; j < 4; j++) {
const IR::U32 elem1 = (v.ir.*extension)(v.ir.VectorGetElement(8, operand1, 4 * i + j));
const IR::U32 elem2 = (v.ir.*extension)(v.ir.VectorGetElement(8, operand2, 4 * index + j));
res_element = v.ir.Add(res_element, v.ir.Mul(elem1, elem2));
}
res_element = v.ir.Add(v.ir.VectorGetElement(32, result, i), res_element);
result = v.ir.VectorSetElement(32, result, i, res_element);
}
v.V(datasize, Vd, result);
return true;
}
enum class Signedness {
Signed,
Unsigned
};
bool MultiplyLong(TranslatorVisitor& v, bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo,
Imm<1> H, Vec Vn, Vec Vd, ExtraBehavior extra_behavior, Signedness sign) {
if (size == 0b00 || size == 0b11) {
return v.ReservedValue();
}
const size_t idxsize = H == 1 ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = 64;
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const auto extend_operands = [&](const IR::U128& lhs, const IR::U128& rhs) {
if (sign == Signedness::Signed) {
return std::make_pair(v.ir.VectorSignExtend(esize, lhs),
v.ir.VectorSignExtend(esize, rhs));
}
return std::make_pair(v.ir.VectorZeroExtend(esize, lhs),
v.ir.VectorZeroExtend(esize, rhs));
};
const IR::U128 operand1 = v.Vpart(datasize, Vn, Q);
const IR::U128 operand2 = v.V(idxsize, Vm);
const IR::U128 index_vector = v.ir.VectorBroadcast(esize, v.ir.VectorGetElement(esize, operand2, index));
const IR::U128 result = [&] {
const auto [extended_op1, extended_index] = extend_operands(operand1, index_vector);
const IR::U128 product = v.ir.VectorMultiply(2 * esize, extended_op1, extended_index);
if (extra_behavior == ExtraBehavior::None) {
return product;
}
const IR::U128 operand3 = v.V(2 * datasize, Vd);
if (extra_behavior == ExtraBehavior::Accumulate) {
return v.ir.VectorAdd(2 * esize, operand3, product);
}
return v.ir.VectorSub(2 * esize, operand3, product);
}();
v.V(2 * datasize, Vd, result);
return true;
}
} // Anonymous namespace
bool TranslatorVisitor::MLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate);
}
bool TranslatorVisitor::MLS_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract);
}
bool TranslatorVisitor::MUL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyByElement(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None);
}
bool TranslatorVisitor::FCMLA_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<2> rot, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
if (size == 0b01 && H == 1 && Q == 0) {
return ReservedValue();
}
if (size == 0b10 && (L == 1 || Q == 0)) {
return ReservedValue();
}
const size_t esize = 8U << size.ZeroExtend();
// TODO: We don't support the half-precision floating point variant yet.
if (esize == 16) {
return InterpretThisInstruction();
}
const size_t index = [=] {
if (size == 0b01) {
return concatenate(H, L).ZeroExtend();
}
return H.ZeroExtend();
}();
const Vec Vm = concatenate(M, Vmlo).ZeroExtend<Vec>();
const size_t datasize = Q ? 128 : 64;
const size_t num_elements = datasize / esize;
const size_t num_iterations = num_elements / 2;
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(datasize, Vm);
const IR::U128 operand3 = V(datasize, Vd);
IR::U128 result = ir.ZeroVector();
IR::U32U64 element1;
IR::U32U64 element2;
IR::U32U64 element3;
IR::U32U64 element4;
for (size_t e = 0; e < num_iterations; ++e) {
const size_t first = e * 2;
const size_t second = first + 1;
const size_t index_first = index * 2;
const size_t index_second = index_first + 1;
switch (rot.ZeroExtend()) {
case 0b00: // 0 degrees
element1 = ir.VectorGetElement(esize, operand2, index_first);
element2 = ir.VectorGetElement(esize, operand1, first);
element3 = ir.VectorGetElement(esize, operand2, index_second);
element4 = ir.VectorGetElement(esize, operand1, first);
break;
case 0b01: // 90 degrees
element1 = ir.FPNeg(ir.VectorGetElement(esize, operand2, index_second));
element2 = ir.VectorGetElement(esize, operand1, second);
element3 = ir.VectorGetElement(esize, operand2, index_first);
element4 = ir.VectorGetElement(esize, operand1, second);
break;
case 0b10: // 180 degrees
element1 = ir.FPNeg(ir.VectorGetElement(esize, operand2, index_first));
element2 = ir.VectorGetElement(esize, operand1, first);
element3 = ir.FPNeg(ir.VectorGetElement(esize, operand2, index_second));
element4 = ir.VectorGetElement(esize, operand1, first);
break;
case 0b11: // 270 degrees
element1 = ir.VectorGetElement(esize, operand2, index_second);
element2 = ir.VectorGetElement(esize, operand1, second);
element3 = ir.FPNeg(ir.VectorGetElement(esize, operand2, index_first));
element4 = ir.VectorGetElement(esize, operand1, second);
break;
}
const IR::U32U64 operand3_elem1 = ir.VectorGetElement(esize, operand3, first);
const IR::U32U64 operand3_elem2 = ir.VectorGetElement(esize, operand3, second);
result = ir.VectorSetElement(esize, result, first, ir.FPMulAdd(operand3_elem1, element2, element1));
result = ir.VectorSetElement(esize, result, second, ir.FPMulAdd(operand3_elem2, element4, element3));
}
ir.SetQ(Vd, result);
return true;
}
bool TranslatorVisitor::FMLA_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate);
}
bool TranslatorVisitor::FMLA_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate);
}
bool TranslatorVisitor::FMLS_elt_3(bool Q, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return FPMultiplyByElementHalfPrecision(*this, Q, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract);
}
bool TranslatorVisitor::FMLS_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract);
}
bool TranslatorVisitor::FMUL_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None);
}
bool TranslatorVisitor::FMULX_elt_4(bool Q, bool sz, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return FPMultiplyByElement(*this, Q, sz, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Extended);
}
bool TranslatorVisitor::SMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate, Signedness::Signed);
}
bool TranslatorVisitor::SMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract, Signedness::Signed);
}
bool TranslatorVisitor::SMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None, Signedness::Signed);
}
bool TranslatorVisitor::SQDMULL_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t part = Q ? 1 : 0;
const size_t idxsize = H == 1 ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = 64;
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = Vpart(datasize, Vn, part);
const IR::U128 operand2 = V(idxsize, Vm);
const IR::U128 index_vector = ir.VectorBroadcast(esize, ir.VectorGetElement(esize, operand2, index));
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiplyLong(esize, operand1, index_vector);
V(128, Vd, result);
return true;
}
bool TranslatorVisitor::SQDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t idxsize = H == 1 ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(idxsize, Vm);
const IR::U128 index_vector = ir.VectorBroadcast(esize, ir.VectorGetElement(esize, operand2, index));
const IR::U128 result = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, index_vector).upper;
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SQRDMULH_elt_2(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
if (size == 0b00 || size == 0b11) {
return ReservedValue();
}
const size_t idxsize = H == 1 ? 128 : 64;
const size_t esize = 8 << size.ZeroExtend();
const size_t datasize = Q ? 128 : 64;
const auto [index, Vm] = Combine(size, H, L, M, Vmlo);
const IR::U128 operand1 = V(datasize, Vn);
const IR::U128 operand2 = V(idxsize, Vm);
const IR::U128 index_vector = ir.VectorBroadcast(esize, ir.VectorGetElement(esize, operand2, index));
const IR::UpperAndLower multiply = ir.VectorSignedSaturatedDoublingMultiply(esize, operand1, index_vector);
const IR::U128 result = ir.VectorAdd(esize, multiply.upper, ir.VectorLogicalShiftRight(esize, multiply.lower, static_cast<u8>(esize - 1)));
V(datasize, Vd, result);
return true;
}
bool TranslatorVisitor::SDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return DotProduct(*this, Q, size, L, M, Vmlo, H, Vn, Vd, &IREmitter::SignExtendToWord);
}
bool TranslatorVisitor::UDOT_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return DotProduct(*this, Q, size, L, M, Vmlo, H, Vn, Vd, &IREmitter::ZeroExtendToWord);
}
bool TranslatorVisitor::UMLAL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Accumulate, Signedness::Unsigned);
}
bool TranslatorVisitor::UMLSL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::Subtract, Signedness::Unsigned);
}
bool TranslatorVisitor::UMULL_elt(bool Q, Imm<2> size, Imm<1> L, Imm<1> M, Imm<4> Vmlo, Imm<1> H, Vec Vn, Vec Vd) {
return MultiplyLong(*this, Q, size, L, M, Vmlo, H, Vn, Vd, ExtraBehavior::None, Signedness::Unsigned);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,51 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
static bool DataCacheInstruction(TranslatorVisitor& v, DataCacheOperation op, const Reg Rt) {
v.ir.DataCacheOperationRaised(op, v.X(64, Rt));
return true;
}
bool TranslatorVisitor::DC_IVAC(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::InvalidateByVAToPoC, Rt);
}
bool TranslatorVisitor::DC_ISW(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::InvalidateBySetWay, Rt);
}
bool TranslatorVisitor::DC_CSW(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::CleanBySetWay, Rt);
}
bool TranslatorVisitor::DC_CISW(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::CleanAndInvalidateBySetWay, Rt);
}
bool TranslatorVisitor::DC_ZVA(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::ZeroByVA, Rt);
}
bool TranslatorVisitor::DC_CVAC(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::CleanByVAToPoC, Rt);
}
bool TranslatorVisitor::DC_CVAU(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::CleanByVAToPoU, Rt);
}
bool TranslatorVisitor::DC_CVAP(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::CleanByVAToPoP, Rt);
}
bool TranslatorVisitor::DC_CIVAC(Reg Rt) {
return DataCacheInstruction(*this, DataCacheOperation::CleanAndInvalidateByVAToPoC, Rt);
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,153 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
// Register encodings used by MRS and MSR.
enum class SystemRegisterEncoding : u32 {
// Counter-timer Frequency register
CNTFRQ_EL0 = 0b11'011'1110'0000'000,
// Counter-timer Physical Count register
CNTPCT_EL0 = 0b11'011'1110'0000'001,
// Cache Type Register
CTR_EL0 = 0b11'011'0000'0000'001,
// Data Cache Zero ID register
DCZID_EL0 = 0b11'011'0000'0000'111,
// Floating-point Control Register
FPCR = 0b11'011'0100'0100'000,
// Floating-point Status Register
FPSR = 0b11'011'0100'0100'001,
// Read/Write Software Thread ID Register
TPIDR_EL0 = 0b11'011'1101'0000'010,
// Read-Only Software Thread ID Register
TPIDRRO_EL0 = 0b11'011'1101'0000'011,
};
bool TranslatorVisitor::HINT([[maybe_unused]] Imm<4> CRm, [[maybe_unused]] Imm<3> op2) {
return true;
}
bool TranslatorVisitor::NOP() {
return true;
}
bool TranslatorVisitor::YIELD() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::Yield);
}
bool TranslatorVisitor::WFE() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::WaitForEvent);
}
bool TranslatorVisitor::WFI() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::WaitForInterrupt);
}
bool TranslatorVisitor::SEV() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::SendEvent);
}
bool TranslatorVisitor::SEVL() {
if (!options.hook_hint_instructions) {
return true;
}
return RaiseException(Exception::SendEventLocal);
}
bool TranslatorVisitor::CLREX(Imm<4> /*CRm*/) {
ir.ClearExclusive();
return true;
}
bool TranslatorVisitor::DSB(Imm<4> /*CRm*/) {
ir.DataSynchronizationBarrier();
return true;
}
bool TranslatorVisitor::DMB(Imm<4> /*CRm*/) {
ir.DataMemoryBarrier();
return true;
}
bool TranslatorVisitor::ISB(Imm<4> /*CRm*/) {
ir.InstructionSynchronizationBarrier();
ir.SetPC(ir.Imm64(ir.current_location->PC() + 4));
ir.SetTerm(IR::Term::ReturnToDispatch{});
return false;
}
bool TranslatorVisitor::MSR_reg(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<SystemRegisterEncoding>();
switch (sys_reg) {
case SystemRegisterEncoding::FPCR:
ir.SetFPCR(X(32, Rt));
ir.SetPC(ir.Imm64(ir.current_location->PC() + 4));
ir.SetTerm(IR::Term::FastDispatchHint{});
return false;
case SystemRegisterEncoding::FPSR:
ir.SetFPSR(X(32, Rt));
return true;
case SystemRegisterEncoding::TPIDR_EL0:
ir.SetTPIDR(X(64, Rt));
return true;
default:
break;
}
return InterpretThisInstruction();
}
bool TranslatorVisitor::MRS(Imm<1> o0, Imm<3> op1, Imm<4> CRn, Imm<4> CRm, Imm<3> op2, Reg Rt) {
const auto sys_reg = concatenate(Imm<1>{1}, o0, op1, CRn, CRm, op2).ZeroExtend<SystemRegisterEncoding>();
switch (sys_reg) {
case SystemRegisterEncoding::CNTFRQ_EL0:
X(32, Rt, ir.GetCNTFRQ());
return true;
case SystemRegisterEncoding::CNTPCT_EL0:
// HACK: Ensure that this is the first instruction in the block it's emitted in, so the cycle count is most up-to-date.
if (!ir.block.empty() && !options.wall_clock_cntpct) {
ir.block.CycleCount()--;
ir.SetTerm(IR::Term::LinkBlock{*ir.current_location});
return false;
}
X(64, Rt, ir.GetCNTPCT());
return true;
case SystemRegisterEncoding::CTR_EL0:
X(32, Rt, ir.GetCTR());
return true;
case SystemRegisterEncoding::DCZID_EL0:
X(32, Rt, ir.GetDCZID());
return true;
case SystemRegisterEncoding::FPCR:
X(32, Rt, ir.GetFPCR());
return true;
case SystemRegisterEncoding::FPSR:
X(32, Rt, ir.GetFPSR());
return true;
case SystemRegisterEncoding::TPIDR_EL0:
X(64, Rt, ir.GetTPIDR());
return true;
case SystemRegisterEncoding::TPIDRRO_EL0:
X(64, Rt, ir.GetTPIDRRO());
return true;
}
return InterpretThisInstruction();
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,46 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::AXFlag() {
const IR::U32 nzcv = ir.GetNZCVRaw();
const IR::U32 z = ir.And(nzcv, ir.Imm32(0x40000000));
const IR::U32 c = ir.And(nzcv, ir.Imm32(0x20000000));
const IR::U32 v = ir.And(nzcv, ir.Imm32(0x10000000));
const IR::U32 new_z = ir.Or(ir.LogicalShiftLeft(v, ir.Imm8(2)), z);
const IR::U32 new_c = ir.And(ir.And(c, ir.Not(ir.LogicalShiftLeft(v, ir.Imm8(1)))), ir.Imm32(0x20000000));
ir.SetNZCVRaw(ir.Or(new_z, new_c));
return true;
}
bool TranslatorVisitor::XAFlag() {
const IR::U32 nzcv = ir.GetNZCVRaw();
const IR::U32 z = ir.And(nzcv, ir.Imm32(0x40000000));
const IR::U32 c = ir.And(nzcv, ir.Imm32(0x20000000));
const IR::U32 not_z = ir.And(ir.Not(z), ir.Imm32(0x40000000));
const IR::U32 not_c = ir.And(ir.Not(c), ir.Imm32(0x20000000));
const IR::U32 new_n = ir.And(ir.LogicalShiftLeft(not_c, ir.Imm8(2)),
ir.LogicalShiftLeft(not_z, ir.Imm8(1)));
const IR::U32 new_z = ir.And(z, ir.LogicalShiftLeft(c, ir.Imm8(1)));
const IR::U32 new_c = ir.Or(c, ir.LogicalShiftRight(z, ir.Imm8(1)));
const IR::U32 new_v = ir.And(ir.LogicalShiftRight(not_c, ir.Imm8(1)),
ir.LogicalShiftRight(z, ir.Imm8(2)));
const IR::U32 result = ir.Or(ir.Or(ir.Or(new_n, new_z), new_c), new_v);
ir.SetNZCVRaw(result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,62 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2019 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/translate/impl/impl.h"
namespace Dynarmic::A64 {
bool TranslatorVisitor::CFINV() {
const IR::U32 nzcv = ir.GetNZCVRaw();
const IR::U32 result = ir.Eor(nzcv, ir.Imm32(0x20000000));
ir.SetNZCVRaw(result);
return true;
}
bool TranslatorVisitor::RMIF(Imm<6> lsb, Reg Rn, Imm<4> mask) {
const u32 mask_value = mask.ZeroExtend();
// If no bits are to be moved into the NZCV bits, then we
// just preserve the bits and do no extra work.
if (mask_value == 0) {
ir.SetNZCVRaw(ir.GetNZCVRaw());
return true;
}
const IR::U64 tmp_reg = ir.GetX(Rn);
const IR::U64 rotated = ir.RotateRight(tmp_reg, ir.Imm8(lsb.ZeroExtend<u8>()));
const IR::U32 shifted = ir.LeastSignificantWord(ir.LogicalShiftLeft(rotated, ir.Imm8(28)));
// On the other hand, if all mask bits are set, then we move all four
// relevant bits in the source register to the NZCV bits.
if (mask_value == 0b1111) {
ir.SetNZCVRaw(shifted);
return true;
}
// Determine which bits from the PSTATE will be preserved during the operation.
u32 preservation_mask = 0;
if ((mask_value & 0b1000) == 0) {
preservation_mask |= 1U << 31;
}
if ((mask_value & 0b0100) == 0) {
preservation_mask |= 1U << 30;
}
if ((mask_value & 0b0010) == 0) {
preservation_mask |= 1U << 29;
}
if ((mask_value & 0b0001) == 0) {
preservation_mask |= 1U << 28;
}
const IR::U32 masked = ir.And(shifted, ir.Imm32(~preservation_mask));
const IR::U32 nzcv = ir.And(ir.GetNZCVRaw(), ir.Imm32(preservation_mask));
const IR::U32 result = ir.Or(nzcv, masked);
ir.SetNZCVRaw(result);
return true;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,65 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "frontend/A64/decoder/a64.h"
#include "frontend/A64/location_descriptor.h"
#include "frontend/A64/translate/impl/impl.h"
#include "frontend/A64/translate/translate.h"
#include "frontend/ir/basic_block.h"
#include "frontend/ir/terminal.h"
namespace Dynarmic::A64 {
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options) {
const bool single_step = descriptor.SingleStepping();
IR::Block block{descriptor};
TranslatorVisitor visitor{block, descriptor, std::move(options)};
bool should_continue = true;
do {
const u64 pc = visitor.ir.current_location->PC();
const u32 instruction = memory_read_code(pc);
if (auto decoder = Decode<TranslatorVisitor>(instruction)) {
should_continue = decoder->get().call(visitor, instruction);
} else {
should_continue = visitor.InterpretThisInstruction();
}
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
block.CycleCount()++;
} while (should_continue && !single_step);
if (single_step && should_continue) {
visitor.ir.SetTerm(IR::Term::LinkBlock{*visitor.ir.current_location});
}
ASSERT_MSG(block.HasTerminal(), "Terminal has not been set");
block.SetEndLocation(*visitor.ir.current_location);
return block;
}
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction) {
TranslatorVisitor visitor{block, descriptor, {}};
bool should_continue = true;
if (auto decoder = Decode<TranslatorVisitor>(instruction)) {
should_continue = decoder->get().call(visitor, instruction);
} else {
should_continue = visitor.InterpretThisInstruction();
}
visitor.ir.current_location = visitor.ir.current_location->AdvancePC(4);
block.CycleCount()++;
block.SetEndLocation(*visitor.ir.current_location);
return should_continue;
}
} // namespace Dynarmic::A64

View File

@@ -0,0 +1,58 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <functional>
#include "common/common_types.h"
namespace Dynarmic {
namespace IR {
class Block;
} // namespace IR
namespace A64 {
class LocationDescriptor;
using MemoryReadCodeFuncType = std::function<u32(u64 vaddr)>;
struct TranslationOptions {
/// This changes what IR we emit when we translate an unpredictable instruction.
/// If this is false, the ExceptionRaised IR instruction is emitted.
/// If this is true, we define some behaviour for some instructions.
bool define_unpredictable_behaviour = false;
/// This tells the translator a wall clock will be used, thus allowing it
/// to avoid writting certain unnecessary code only needed for cycle timers.
bool wall_clock_cntpct = false;
/// This changes what IR we emit when we translate a hint instruction.
/// If this is false, we treat the instruction as a NOP.
/// If this is true, we emit an ExceptionRaised instruction.
bool hook_hint_instructions = true;
};
/**
* This function translates instructions in memory into our intermediate representation.
* @param descriptor The starting location of the basic block. Includes information like PC, FPCR state, &c.
* @param memory_read_code The function we should use to read emulated memory.
* @param options Configures how certain instructions are translated.
* @return A translated basic block in the intermediate representation.
*/
IR::Block Translate(LocationDescriptor descriptor, MemoryReadCodeFuncType memory_read_code, TranslationOptions options);
/**
* This function translates a single provided instruction into our intermediate representation.
* @param block The block to append the IR for the instruction to.
* @param descriptor The location of the instruction. Includes information like PC, FPCR state, &c.
* @param instruction The instruction to translate.
* @return The translated instruction translated to the intermediate representation.
*/
bool TranslateSingleInstruction(IR::Block& block, LocationDescriptor descriptor, u32 instruction);
} // namespace A64
} // namespace Dynarmic

44
externals/dynarmic/src/frontend/A64/types.cpp vendored Executable file
View File

@@ -0,0 +1,44 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <array>
#include <ostream>
#include <fmt/format.h>
#include "frontend/A64/types.h"
namespace Dynarmic::A64 {
const char* CondToString(Cond cond) {
static constexpr std::array cond_strs = {
"eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv"
};
return cond_strs.at(static_cast<size_t>(cond));
}
std::string RegToString(Reg reg) {
if (reg == Reg::R31) {
return "sp|zr";
}
return fmt::format("r{}", static_cast<size_t>(reg));
}
std::string VecToString(Vec vec) {
return fmt::format("v{}", static_cast<size_t>(vec));
}
std::ostream& operator<<(std::ostream& o, Reg reg) {
o << RegToString(reg);
return o;
}
std::ostream& operator<<(std::ostream& o, Vec vec) {
o << VecToString(vec);
return o;
}
} // namespace Dynarmic::A64

71
externals/dynarmic/src/frontend/A64/types.h vendored Executable file
View File

@@ -0,0 +1,71 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#pragma once
#include <iosfwd>
#include <string>
#include "common/assert.h"
#include "common/common_types.h"
#include "frontend/ir/cond.h"
namespace Dynarmic::A64 {
using Cond = IR::Cond;
enum class Reg {
R0, R1, R2, R3, R4, R5, R6, R7,
R8, R9, R10, R11, R12, R13, R14, R15,
R16, R17, R18, R19, R20, R21, R22, R23,
R24, R25, R26, R27, R28, R29, R30, R31,
LR = R30,
SP = R31, ZR = R31,
};
enum class Vec {
V0, V1, V2, V3, V4, V5, V6, V7,
V8, V9, V10, V11, V12, V13, V14, V15,
V16, V17, V18, V19, V20, V21, V22, V23,
V24, V25, V26, V27, V28, V29, V30, V31,
};
enum class ShiftType {
LSL,
LSR,
ASR,
ROR,
};
const char* CondToString(Cond cond);
std::string RegToString(Reg reg);
std::string VecToString(Vec vec);
std::ostream& operator<<(std::ostream& o, Reg reg);
std::ostream& operator<<(std::ostream& o, Vec vec);
constexpr size_t RegNumber(Reg reg) {
return static_cast<size_t>(reg);
}
constexpr size_t VecNumber(Vec vec) {
return static_cast<size_t>(vec);
}
inline Reg operator+(Reg reg, size_t number) {
const size_t new_reg = RegNumber(reg) + number;
ASSERT(new_reg <= 31);
return static_cast<Reg>(new_reg);
}
inline Vec operator+(Vec vec, size_t number) {
const size_t new_vec = VecNumber(vec) + number;
ASSERT(new_vec <= 31);
return static_cast<Vec>(new_vec);
}
} // namespace Dynarmic::A64