emit_arm64_a32_coprocessor: Implement coprocessor IR instructions
This commit is contained in:
parent
0f0744cb78
commit
94f5ae4f37
5 changed files with 264 additions and 40 deletions
|
@ -150,6 +150,7 @@ EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) {
|
|||
.descriptor_to_fpcr = [](const IR::LocationDescriptor& location) { return FP::FPCR{A32::LocationDescriptor{location}.FPSCR().Value()}; },
|
||||
.state_nzcv_offset = offsetof(A32JitState, cpsr_nzcv),
|
||||
.state_fpsr_offset = offsetof(A32JitState, fpsr),
|
||||
.coprocessors = conf.coprocessors,
|
||||
};
|
||||
EmittedBlockInfo block_info = EmitArm64(code, std::move(block), emit_conf);
|
||||
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <mcl/stdint.hpp>
|
||||
|
||||
#include "dynarmic/interface/A32/coprocessor.h"
|
||||
|
||||
namespace oaknut {
|
||||
struct PointerCodeGeneratorPolicy;
|
||||
template<typename>
|
||||
|
@ -71,6 +75,8 @@ struct EmitConfig {
|
|||
|
||||
size_t state_nzcv_offset;
|
||||
size_t state_fpsr_offset;
|
||||
|
||||
std::array<std::shared_ptr<A32::Coprocessor>, 16> coprocessors{};
|
||||
};
|
||||
|
||||
struct EmitContext;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||
#include "dynarmic/backend/arm64/emit_context.h"
|
||||
#include "dynarmic/backend/arm64/reg_alloc.h"
|
||||
#include "dynarmic/interface/A32/coprocessor.h"
|
||||
#include "dynarmic/ir/basic_block.h"
|
||||
#include "dynarmic/ir/microinstruction.h"
|
||||
#include "dynarmic/ir/opcodes.h"
|
||||
|
@ -18,60 +19,277 @@ namespace Dynarmic::Backend::Arm64 {
|
|||
|
||||
using namespace oaknut::util;
|
||||
|
||||
static void EmitCoprocessorException() {
|
||||
ASSERT_FALSE("Should raise coproc exception here");
|
||||
}
|
||||
|
||||
static void CallCoprocCallback(oaknut::CodeGenerator& code, EmitContext& ctx, A32::Coprocessor::Callback callback, IR::Inst* inst = nullptr, std::optional<Argument::copyable_reference> arg0 = {}, std::optional<Argument::copyable_reference> arg1 = {}) {
|
||||
ctx.reg_alloc.PrepareForCall(inst, {}, arg0, arg1);
|
||||
|
||||
if (callback.user_arg) {
|
||||
code.MOV(X0, reinterpret_cast<u64>(*callback.user_arg));
|
||||
}
|
||||
|
||||
code.MOV(Xscratch0, reinterpret_cast<u64>(callback.function));
|
||||
code.BLR(Xscratch0);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocInternalOperation>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const auto opc1 = static_cast<unsigned>(coproc_info[2]);
|
||||
const auto CRd = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
const auto CRn = static_cast<A32::CoprocReg>(coproc_info[4]);
|
||||
const auto CRm = static_cast<A32::CoprocReg>(coproc_info[5]);
|
||||
const auto opc2 = static_cast<unsigned>(coproc_info[6]);
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = coproc->CompileInternalOperation(two, opc1, CRd, CRn, CRm, opc2);
|
||||
if (!action) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
CallCoprocCallback(code, ctx, *action);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendOneWord>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const auto opc1 = static_cast<unsigned>(coproc_info[2]);
|
||||
const auto CRn = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
const auto CRm = static_cast<A32::CoprocReg>(coproc_info[4]);
|
||||
const auto opc2 = static_cast<unsigned>(coproc_info[5]);
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = coproc->CompileSendOneWord(two, opc1, CRn, CRm, opc2);
|
||||
|
||||
if (std::holds_alternative<std::monostate>(action)) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx, *cb, nullptr, args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto destination_ptr = std::get_if<u32*>(&action)) {
|
||||
auto Wvalue = ctx.reg_alloc.ReadW(args[1]);
|
||||
RegAlloc::Realize(Wvalue);
|
||||
|
||||
code.MOV(Xscratch0, reinterpret_cast<u64>(*destination_ptr));
|
||||
code.STR(Wvalue, Xscratch0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocSendTwoWords>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const auto opc = static_cast<unsigned>(coproc_info[2]);
|
||||
const auto CRm = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = coproc->CompileSendTwoWords(two, opc, CRm);
|
||||
|
||||
if (std::holds_alternative<std::monostate>(action)) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx, *cb, nullptr, args[1], args[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto destination_ptrs = std::get_if<std::array<u32*, 2>>(&action)) {
|
||||
auto Wvalue1 = ctx.reg_alloc.ReadW(args[1]);
|
||||
auto Wvalue2 = ctx.reg_alloc.ReadW(args[2]);
|
||||
RegAlloc::Realize(Wvalue1, Wvalue2);
|
||||
|
||||
code.MOV(Xscratch0, reinterpret_cast<u64>((*destination_ptrs)[0]));
|
||||
code.MOV(Xscratch1, reinterpret_cast<u64>((*destination_ptrs)[1]));
|
||||
code.STR(Wvalue1, Xscratch0);
|
||||
code.STR(Wvalue2, Xscratch1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetOneWord>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const auto opc1 = static_cast<unsigned>(coproc_info[2]);
|
||||
const auto CRn = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
const auto CRm = static_cast<A32::CoprocReg>(coproc_info[4]);
|
||||
const auto opc2 = static_cast<unsigned>(coproc_info[5]);
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = coproc->CompileGetOneWord(two, opc1, CRn, CRm, opc2);
|
||||
|
||||
if (std::holds_alternative<std::monostate>(action)) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx, *cb, inst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto source_ptr = std::get_if<u32*>(&action)) {
|
||||
auto Wvalue = ctx.reg_alloc.WriteW(inst);
|
||||
RegAlloc::Realize(Wvalue);
|
||||
|
||||
code.MOV(Xscratch0, reinterpret_cast<u64>(*source_ptr));
|
||||
code.LDR(Wvalue, Xscratch0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocGetTwoWords>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const unsigned opc = coproc_info[2];
|
||||
const auto CRm = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = coproc->CompileGetTwoWords(two, opc, CRm);
|
||||
|
||||
if (std::holds_alternative<std::monostate>(action)) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx, *cb, inst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto source_ptrs = std::get_if<std::array<u32*, 2>>(&action)) {
|
||||
auto Xvalue = ctx.reg_alloc.WriteX(inst);
|
||||
RegAlloc::Realize(Xvalue);
|
||||
|
||||
code.MOV(Xscratch0, reinterpret_cast<u64>((*source_ptrs)[0]));
|
||||
code.MOV(Xscratch1, reinterpret_cast<u64>((*source_ptrs)[1]));
|
||||
code.LDR(Xvalue, Xscratch0);
|
||||
code.LDR(Wscratch1, Xscratch1);
|
||||
code.BFI(Xvalue, Xscratch1, 32, 32);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocLoadWords>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const bool long_transfer = coproc_info[2] != 0;
|
||||
const auto CRd = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
const bool has_option = coproc_info[4] != 0;
|
||||
|
||||
std::optional<u8> option = std::nullopt;
|
||||
if (has_option) {
|
||||
option = coproc_info[5];
|
||||
}
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = coproc->CompileLoadWords(two, long_transfer, CRd, option);
|
||||
if (!action) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
CallCoprocCallback(code, ctx, *action, nullptr, args[1]);
|
||||
}
|
||||
|
||||
template<>
|
||||
void EmitIR<IR::Opcode::A32CoprocStoreWords>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||
(void)code;
|
||||
(void)ctx;
|
||||
(void)inst;
|
||||
ASSERT_FALSE("Unimplemented");
|
||||
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||
|
||||
const auto coproc_info = inst->GetArg(0).GetCoprocInfo();
|
||||
const size_t coproc_num = coproc_info[0];
|
||||
const bool two = coproc_info[1] != 0;
|
||||
const bool long_transfer = coproc_info[2] != 0;
|
||||
const auto CRd = static_cast<A32::CoprocReg>(coproc_info[3]);
|
||||
const bool has_option = coproc_info[4] != 0;
|
||||
|
||||
std::optional<u8> option = std::nullopt;
|
||||
if (has_option) {
|
||||
option = coproc_info[5];
|
||||
}
|
||||
|
||||
std::shared_ptr<A32::Coprocessor> coproc = ctx.conf.coprocessors[coproc_num];
|
||||
if (!coproc) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto action = coproc->CompileStoreWords(two, long_transfer, CRd, option);
|
||||
if (!action) {
|
||||
EmitCoprocessorException();
|
||||
return;
|
||||
}
|
||||
|
||||
CallCoprocCallback(code, ctx, *action, nullptr, args[1]);
|
||||
}
|
||||
|
||||
} // namespace Dynarmic::Backend::Arm64
|
||||
|
|
|
@ -835,12 +835,11 @@ static void EmitCoprocessorException() {
|
|||
ASSERT_FALSE("Should raise coproc exception here");
|
||||
}
|
||||
|
||||
static void CallCoprocCallback(BlockOfCode& code, RegAlloc& reg_alloc, A32::Jit* jit_interface, A32::Coprocessor::Callback callback, IR::Inst* inst = nullptr, std::optional<Argument::copyable_reference> arg0 = {}, std::optional<Argument::copyable_reference> arg1 = {}) {
|
||||
reg_alloc.HostCall(inst, {}, {}, arg0, arg1);
|
||||
static void CallCoprocCallback(BlockOfCode& code, RegAlloc& reg_alloc, A32::Coprocessor::Callback callback, IR::Inst* inst = nullptr, std::optional<Argument::copyable_reference> arg0 = {}, std::optional<Argument::copyable_reference> arg1 = {}) {
|
||||
reg_alloc.HostCall(inst, {}, arg0, arg1);
|
||||
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(jit_interface));
|
||||
if (callback.user_arg) {
|
||||
code.mov(code.ABI_PARAM2, reinterpret_cast<u64>(*callback.user_arg));
|
||||
code.mov(code.ABI_PARAM1, reinterpret_cast<u64>(*callback.user_arg));
|
||||
}
|
||||
|
||||
code.CallFunction(callback.function);
|
||||
|
@ -868,7 +867,7 @@ void A32EmitX64::EmitA32CoprocInternalOperation(A32EmitContext& ctx, IR::Inst* i
|
|||
return;
|
||||
}
|
||||
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *action);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *action);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32CoprocSendOneWord(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
|
@ -895,7 +894,7 @@ void A32EmitX64::EmitA32CoprocSendOneWord(A32EmitContext& ctx, IR::Inst* inst) {
|
|||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *cb, nullptr, args[1]);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *cb, nullptr, args[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -935,7 +934,7 @@ void A32EmitX64::EmitA32CoprocSendTwoWords(A32EmitContext& ctx, IR::Inst* inst)
|
|||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *cb, nullptr, args[1], args[2]);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *cb, nullptr, args[1], args[2]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -979,7 +978,7 @@ void A32EmitX64::EmitA32CoprocGetOneWord(A32EmitContext& ctx, IR::Inst* inst) {
|
|||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *cb, inst);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *cb, inst);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1018,7 @@ void A32EmitX64::EmitA32CoprocGetTwoWords(A32EmitContext& ctx, IR::Inst* inst) {
|
|||
}
|
||||
|
||||
if (const auto cb = std::get_if<A32::Coprocessor::Callback>(&action)) {
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *cb, inst);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *cb, inst);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1070,7 +1069,7 @@ void A32EmitX64::EmitA32CoprocLoadWords(A32EmitContext& ctx, IR::Inst* inst) {
|
|||
return;
|
||||
}
|
||||
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *action, nullptr, args[1]);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *action, nullptr, args[1]);
|
||||
}
|
||||
|
||||
void A32EmitX64::EmitA32CoprocStoreWords(A32EmitContext& ctx, IR::Inst* inst) {
|
||||
|
@ -1100,7 +1099,7 @@ void A32EmitX64::EmitA32CoprocStoreWords(A32EmitContext& ctx, IR::Inst* inst) {
|
|||
return;
|
||||
}
|
||||
|
||||
CallCoprocCallback(code, ctx.reg_alloc, jit_interface, *action, nullptr, args[1]);
|
||||
CallCoprocCallback(code, ctx.reg_alloc, *action, nullptr, args[1]);
|
||||
}
|
||||
|
||||
std::string A32EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescriptor& ir_descriptor) const {
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
* @param arg1 Purpose of this argument depends on type of callback.
|
||||
* @return Purpose of return value depends on type of callback.
|
||||
*/
|
||||
std::uint64_t (*function)(Jit* jit, void* user_arg, std::uint32_t arg0, std::uint32_t arg1);
|
||||
std::uint64_t (*function)(void* user_arg, std::uint32_t arg0, std::uint32_t arg1);
|
||||
/// If std::nullopt, function will be called with a user_arg parameter containing garbage.
|
||||
std::optional<void*> user_arg;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue