From 77436bbbbb847d9a9759dc928e1d97c4d77ed906 Mon Sep 17 00:00:00 2001 From: Merry Date: Sat, 16 Jul 2022 20:27:56 +0100 Subject: [PATCH] backend/arm64: Toy implementation of enough to execute LSLS --- src/dynarmic/CMakeLists.txt | 3 + src/dynarmic/backend/arm64/emit_arm64.cpp | 25 ++++ src/dynarmic/backend/arm64/emit_arm64_a32.cpp | 76 ++++++++++ .../arm64/emit_arm64_data_processing.cpp | 137 ++++++++++++++++++ src/dynarmic/backend/arm64/emit_context.h | 26 ++++ src/dynarmic/backend/arm64/reg_alloc.h | 7 +- 6 files changed, 270 insertions(+), 4 deletions(-) create mode 100644 src/dynarmic/backend/arm64/emit_arm64_a32.cpp create mode 100644 src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp create mode 100644 src/dynarmic/backend/arm64/emit_context.h diff --git a/src/dynarmic/CMakeLists.txt b/src/dynarmic/CMakeLists.txt index ec843f74..c6891ec9 100644 --- a/src/dynarmic/CMakeLists.txt +++ b/src/dynarmic/CMakeLists.txt @@ -372,6 +372,8 @@ elseif(ARCHITECTURE STREQUAL "arm64") backend/arm64/abi.h backend/arm64/emit_arm64.cpp backend/arm64/emit_arm64.h + backend/arm64/emit_arm64_data_processing.cpp + backend/arm64/emit_context.h backend/arm64/reg_alloc.cpp backend/arm64/reg_alloc.h backend/arm64/stack_layout.h @@ -385,6 +387,7 @@ elseif(ARCHITECTURE STREQUAL "arm64") backend/arm64/a32_interface.cpp backend/arm64/a32_jitstate.cpp backend/arm64/a32_jitstate.h + backend/arm64/emit_arm64_a32.cpp ) endif() diff --git a/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/backend/arm64/emit_arm64.cpp index 2a433ed3..7f138541 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -25,11 +25,36 @@ void EmitIR(oaknut::CodeGenerator&, EmitContext&, IR::Inst*) { ASSERT_FALSE("Unimplemented opcode {}", op); } +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); + template<> void EmitIR(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) { ASSERT(ctx.reg_alloc.IsValueLive(inst)); } +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + auto Wvalue = ctx.reg_alloc.ReadW(args[0]); + auto Wnz = ctx.reg_alloc.WriteW(inst); + RegAlloc::Realize(Wvalue, Wnz); + + code.CMP(*Wnz, WZR); + code.MRS(Wnz->toX(), static_cast(0b11'011'0100'0010'000)); +} + EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf) { EmittedBlockInfo ebi; diff --git a/src/dynarmic/backend/arm64/emit_arm64_a32.cpp b/src/dynarmic/backend/arm64/emit_arm64_a32.cpp new file mode 100644 index 00000000..19ce31cc --- /dev/null +++ b/src/dynarmic/backend/arm64/emit_arm64_a32.cpp @@ -0,0 +1,76 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include +#include + +#include "dynarmic/backend/arm64/a32_jitstate.h" +#include "dynarmic/backend/arm64/abi.h" +#include "dynarmic/backend/arm64/emit_arm64.h" +#include "dynarmic/backend/arm64/emit_context.h" +#include "dynarmic/backend/arm64/reg_alloc.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/opcodes.h" + +namespace Dynarmic::Backend::Arm64 { + +using namespace oaknut::util; + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + const A32::Reg reg = inst->GetArg(0).GetA32RegRef(); + + auto Wresult = ctx.reg_alloc.WriteW(inst); + RegAlloc::Realize(Wresult); + + // TODO: Detect if Gpr vs Fpr is more appropriate + + code.LDR(Wresult, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * static_cast(reg)); +} + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + const A32::Reg reg = inst->GetArg(0).GetA32RegRef(); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + auto Wvalue = ctx.reg_alloc.ReadW(args[1]); + RegAlloc::Realize(Wvalue); + + // TODO: Detect if Gpr vs Fpr is more appropriate + + code.STR(Wvalue, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * static_cast(reg)); +} + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + auto Wflag = ctx.reg_alloc.WriteW(inst); + RegAlloc::Realize(Wflag); + + // TODO: Store in Flags + + code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv)); + code.LSR(Wflag, Wscratch0, 29); + code.AND(Wflag, Wflag, 1); +} + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + auto Wnz = ctx.reg_alloc.ReadW(args[0]); + auto Wc = ctx.reg_alloc.ReadW(args[1]); + RegAlloc::Realize(Wnz, Wc); + + // TODO: Store in Flags + code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv)); + code.AND(Wscratch0, Wscratch0, 0x10000000); + code.ORR(Wscratch0, Wscratch0, Wnz); + code.SBFX(Wscratch0, Wc, 29, 1); + code.STR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv)); +} + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp b/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp new file mode 100644 index 00000000..4bda0d91 --- /dev/null +++ b/src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp @@ -0,0 +1,137 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#include +#include + +#include "dynarmic/backend/arm64/a32_jitstate.h" +#include "dynarmic/backend/arm64/abi.h" +#include "dynarmic/backend/arm64/emit_arm64.h" +#include "dynarmic/backend/arm64/emit_context.h" +#include "dynarmic/backend/arm64/reg_alloc.h" +#include "dynarmic/ir/basic_block.h" +#include "dynarmic/ir/microinstruction.h" +#include "dynarmic/ir/opcodes.h" + +namespace Dynarmic::Backend::Arm64 { + +using namespace oaknut::util; + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + // TODO: Use host flags + const auto carry_inst = inst->GetAssociatedPseudoOperation(IR::Opcode::GetCarryFromOp); + + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + auto& operand_arg = args[0]; + auto& shift_arg = args[1]; + auto& carry_arg = args[2]; + + if (!carry_inst) { + if (shift_arg.IsImmediate()) { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + RegAlloc::Realize(Wresult, Woperand); + + const u8 shift = shift_arg.GetImmediateU8(); + + if (shift <= 31) { + code.LSL(Wresult, Woperand, shift); + } else { + code.MOV(Wresult, WZR); + } + } else { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + auto Wshift = ctx.reg_alloc.ReadW(shift_arg); + RegAlloc::Realize(Wresult, Woperand, Wshift); + + code.AND(Wscratch0, Wshift, 0xff); + code.LSL(Wresult, Woperand, Wscratch0); + code.CMP(Wscratch0, 32); + code.CSEL(Wresult, Wresult, WZR, LT); + } + } else { + if (shift_arg.IsImmediate()) { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + auto Wcarry_in = ctx.reg_alloc.ReadW(carry_arg); + RegAlloc::Realize(Wresult, Wcarry_out, Woperand, Wcarry_in); + + const u8 shift = shift_arg.GetImmediateU8(); + + if (shift == 0) { + code.MOV(*Wresult, Woperand); + code.MOV(*Wcarry_out, Wcarry_in); + } else if (shift < 32) { + code.UBFX(Wcarry_out, Woperand, 32 - shift, 1); + code.LSL(Wresult, Woperand, shift); + } else if (shift > 32) { + code.MOV(Wresult, WZR); + code.MOV(Wcarry_out, WZR); + } else { + code.AND(Wcarry_out, Wresult, 1); + code.MOV(Wresult, WZR); + } + } else { + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Wcarry_out = ctx.reg_alloc.WriteW(carry_inst); + auto Woperand = ctx.reg_alloc.ReadW(operand_arg); + auto Wshift = ctx.reg_alloc.ReadW(shift_arg); + auto Wcarry_in = ctx.reg_alloc.ReadW(carry_arg); + RegAlloc::Realize(Wresult, Wcarry_out, Woperand, Wshift, Wcarry_in); + + // TODO: Use RMIF + + oaknut::Label zero, end; + + code.ANDS(Wscratch1, Wshift, 0xff); + code.B(EQ, zero); + + code.NEG(Wscratch0, Wshift); + code.LSR(Wcarry_out, Woperand, Wscratch0); + code.LSL(Wresult, Woperand, Wshift); + code.AND(Wcarry_out, Wcarry_out, 1); + code.CMP(Wscratch1, 32); + code.CSEL(Wresult, Wresult, WZR, LT); + code.CSEL(Wcarry_out, Wcarry_out, WZR, LE); + code.B(end); + + code.l(zero); + code.MOV(*Wresult, Woperand); + code.MOV(*Wcarry_out, Wcarry_in); + + code.l(end); + } + } +} + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + // TODO: Use host flags + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(args[0]); + RegAlloc::Realize(Wresult, Woperand); + + code.LSR(Wresult, Woperand, 31); +} + +template<> +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { + // TODO: Use host flags + auto args = ctx.reg_alloc.GetArgumentInfo(inst); + + auto Wresult = ctx.reg_alloc.WriteW(inst); + auto Woperand = ctx.reg_alloc.ReadW(args[0]); + RegAlloc::Realize(Wresult, Woperand); + + code.CMP(Woperand, 0); + code.CSET(Wresult, EQ); +} + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/emit_context.h b/src/dynarmic/backend/arm64/emit_context.h new file mode 100644 index 00000000..268f9c73 --- /dev/null +++ b/src/dynarmic/backend/arm64/emit_context.h @@ -0,0 +1,26 @@ +/* This file is part of the dynarmic project. + * Copyright (c) 2022 MerryMage + * SPDX-License-Identifier: 0BSD + */ + +#pragma once + +#include "dynarmic/backend/arm64/emit_arm64.h" +#include "dynarmic/backend/arm64/reg_alloc.h" + +namespace Dynarmic::IR { +class Block; +} // namespace Dynarmic::IR + +namespace Dynarmic::Backend::Arm64 { + +struct EmitConfig; + +struct EmitContext { + IR::Block& block; + RegAlloc& reg_alloc; + const EmitConfig& emit_conf; + EmittedBlockInfo& ebi; +}; + +} // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/reg_alloc.h b/src/dynarmic/backend/arm64/reg_alloc.h index 4eac92f5..35bf35fb 100644 --- a/src/dynarmic/backend/arm64/reg_alloc.h +++ b/src/dynarmic/backend/arm64/reg_alloc.h @@ -67,17 +67,16 @@ public: operator T() const { return *reg; } - template && std::is_same_v>> - operator oaknut::WRegWsp() const { + operator oaknut::WRegWsp() const requires(std::is_same_v) { return *reg; } - template && std::is_same_v>> - operator oaknut::XRegSp() const { + operator oaknut::XRegSp() const requires(std::is_same_v) { return *reg; } T operator*() const { return *reg; } + const T* operator->() const { return &*reg; } ~RAReg();