diff --git a/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/backend/arm64/a32_address_space.cpp index b17a54b9..2ea167ac 100644 --- a/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -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); diff --git a/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/backend/arm64/emit_arm64.h index 55f4a19f..0665ae87 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.h +++ b/src/dynarmic/backend/arm64/emit_arm64.h @@ -5,11 +5,15 @@ #pragma once +#include #include +#include #include #include +#include "dynarmic/interface/A32/coprocessor.h" + namespace oaknut { struct PointerCodeGeneratorPolicy; template @@ -71,6 +75,8 @@ struct EmitConfig { size_t state_nzcv_offset; size_t state_fpsr_offset; + + std::array, 16> coprocessors{}; }; struct EmitContext; diff --git a/src/dynarmic/backend/arm64/emit_arm64_a32_coprocessor.cpp b/src/dynarmic/backend/arm64/emit_arm64_a32_coprocessor.cpp index a359e2c0..6f2f75c7 100644 --- a/src/dynarmic/backend/arm64/emit_arm64_a32_coprocessor.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64_a32_coprocessor.cpp @@ -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 arg0 = {}, std::optional arg1 = {}) { + ctx.reg_alloc.PrepareForCall(inst, {}, arg0, arg1); + + if (callback.user_arg) { + code.MOV(X0, reinterpret_cast(*callback.user_arg)); + } + + code.MOV(Xscratch0, reinterpret_cast(callback.function)); + code.BLR(Xscratch0); +} + template<> void EmitIR(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(coproc_info[2]); + const auto CRd = static_cast(coproc_info[3]); + const auto CRn = static_cast(coproc_info[4]); + const auto CRm = static_cast(coproc_info[5]); + const auto opc2 = static_cast(coproc_info[6]); + + std::shared_ptr 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(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(coproc_info[2]); + const auto CRn = static_cast(coproc_info[3]); + const auto CRm = static_cast(coproc_info[4]); + const auto opc2 = static_cast(coproc_info[5]); + + std::shared_ptr coproc = ctx.conf.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + const auto action = coproc->CompileSendOneWord(two, opc1, CRn, CRm, opc2); + + if (std::holds_alternative(action)) { + EmitCoprocessorException(); + return; + } + + if (const auto cb = std::get_if(&action)) { + CallCoprocCallback(code, ctx, *cb, nullptr, args[1]); + return; + } + + if (const auto destination_ptr = std::get_if(&action)) { + auto Wvalue = ctx.reg_alloc.ReadW(args[1]); + RegAlloc::Realize(Wvalue); + + code.MOV(Xscratch0, reinterpret_cast(*destination_ptr)); + code.STR(Wvalue, Xscratch0); + + return; + } + + UNREACHABLE(); } template<> void EmitIR(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(coproc_info[2]); + const auto CRm = static_cast(coproc_info[3]); + + std::shared_ptr coproc = ctx.conf.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + const auto action = coproc->CompileSendTwoWords(two, opc, CRm); + + if (std::holds_alternative(action)) { + EmitCoprocessorException(); + return; + } + + if (const auto cb = std::get_if(&action)) { + CallCoprocCallback(code, ctx, *cb, nullptr, args[1], args[2]); + return; + } + + if (const auto destination_ptrs = std::get_if>(&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((*destination_ptrs)[0])); + code.MOV(Xscratch1, reinterpret_cast((*destination_ptrs)[1])); + code.STR(Wvalue1, Xscratch0); + code.STR(Wvalue2, Xscratch1); + + return; + } + + UNREACHABLE(); } template<> void EmitIR(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(coproc_info[2]); + const auto CRn = static_cast(coproc_info[3]); + const auto CRm = static_cast(coproc_info[4]); + const auto opc2 = static_cast(coproc_info[5]); + + std::shared_ptr coproc = ctx.conf.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + const auto action = coproc->CompileGetOneWord(two, opc1, CRn, CRm, opc2); + + if (std::holds_alternative(action)) { + EmitCoprocessorException(); + return; + } + + if (const auto cb = std::get_if(&action)) { + CallCoprocCallback(code, ctx, *cb, inst); + return; + } + + if (const auto source_ptr = std::get_if(&action)) { + auto Wvalue = ctx.reg_alloc.WriteW(inst); + RegAlloc::Realize(Wvalue); + + code.MOV(Xscratch0, reinterpret_cast(*source_ptr)); + code.LDR(Wvalue, Xscratch0); + + return; + } + + UNREACHABLE(); } template<> void EmitIR(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(coproc_info[3]); + + std::shared_ptr coproc = ctx.conf.coprocessors[coproc_num]; + if (!coproc) { + EmitCoprocessorException(); + return; + } + + auto action = coproc->CompileGetTwoWords(two, opc, CRm); + + if (std::holds_alternative(action)) { + EmitCoprocessorException(); + return; + } + + if (const auto cb = std::get_if(&action)) { + CallCoprocCallback(code, ctx, *cb, inst); + return; + } + + if (const auto source_ptrs = std::get_if>(&action)) { + auto Xvalue = ctx.reg_alloc.WriteX(inst); + RegAlloc::Realize(Xvalue); + + code.MOV(Xscratch0, reinterpret_cast((*source_ptrs)[0])); + code.MOV(Xscratch1, reinterpret_cast((*source_ptrs)[1])); + code.LDR(Xvalue, Xscratch0); + code.LDR(Wscratch1, Xscratch1); + code.BFI(Xvalue, Xscratch1, 32, 32); + + return; + } + + UNREACHABLE(); } template<> void EmitIR(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(coproc_info[3]); + const bool has_option = coproc_info[4] != 0; + + std::optional option = std::nullopt; + if (has_option) { + option = coproc_info[5]; + } + + std::shared_ptr 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(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(coproc_info[3]); + const bool has_option = coproc_info[4] != 0; + + std::optional option = std::nullopt; + if (has_option) { + option = coproc_info[5]; + } + + std::shared_ptr 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 diff --git a/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/backend/x64/a32_emit_x64.cpp index 3703c5e0..2930f3cc 100644 --- a/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -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 arg0 = {}, std::optional 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 arg0 = {}, std::optional arg1 = {}) { + reg_alloc.HostCall(inst, {}, arg0, arg1); - code.mov(code.ABI_PARAM1, reinterpret_cast(jit_interface)); if (callback.user_arg) { - code.mov(code.ABI_PARAM2, reinterpret_cast(*callback.user_arg)); + code.mov(code.ABI_PARAM1, reinterpret_cast(*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(&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(&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(&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(&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 { diff --git a/src/dynarmic/interface/A32/coprocessor.h b/src/dynarmic/interface/A32/coprocessor.h index 71b3f5a4..7ee2e96e 100644 --- a/src/dynarmic/interface/A32/coprocessor.h +++ b/src/dynarmic/interface/A32/coprocessor.h @@ -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 user_arg; };