backend/arm64: Toy implementation of enough to execute LSLS

This commit is contained in:
Merry 2022-07-16 20:27:56 +01:00 committed by merry
parent 7e046357ff
commit 77436bbbbb
6 changed files with 270 additions and 4 deletions

View file

@ -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()

View file

@ -25,11 +25,36 @@ void EmitIR(oaknut::CodeGenerator&, EmitContext&, IR::Inst*) {
ASSERT_FALSE("Unimplemented opcode {}", op);
}
template<>
void EmitIR<IR::Opcode::A32GetRegister>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::A32SetRegister>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::A32GetCFlag>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::A32SetCpsrNZC>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::LogicalShiftLeft32>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::MostSignificantBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst);
template<>
void EmitIR<IR::Opcode::GetCarryFromOp>(oaknut::CodeGenerator&, EmitContext& ctx, IR::Inst* inst) {
ASSERT(ctx.reg_alloc.IsValueLive(inst));
}
template<>
void EmitIR<IR::Opcode::GetNZFromOp>(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<oaknut::SystemReg>(0b11'011'0100'0010'000));
}
EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf) {
EmittedBlockInfo ebi;

View file

@ -0,0 +1,76 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <fmt/ostream.h>
#include <oaknut/oaknut.hpp>
#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<IR::Opcode::A32GetRegister>(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<size_t>(reg));
}
template<>
void EmitIR<IR::Opcode::A32SetRegister>(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<size_t>(reg));
}
template<>
void EmitIR<IR::Opcode::A32GetCFlag>(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<IR::Opcode::A32SetCpsrNZC>(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

View file

@ -0,0 +1,137 @@
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include <fmt/ostream.h>
#include <oaknut/oaknut.hpp>
#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<IR::Opcode::LogicalShiftLeft32>(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<IR::Opcode::MostSignificantBit>(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<IR::Opcode::IsZero32>(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

View file

@ -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

View file

@ -67,17 +67,16 @@ public:
operator T() const { return *reg; }
template<typename U = T, typename = std::enable_if_t<std::is_same_v<U, oaknut::WReg> && std::is_same_v<T, U>>>
operator oaknut::WRegWsp() const {
operator oaknut::WRegWsp() const requires(std::is_same_v<T, oaknut::WReg>) {
return *reg;
}
template<typename U = T, typename = std::enable_if_t<std::is_same_v<U, oaknut::XReg> && std::is_same_v<T, U>>>
operator oaknut::XRegSp() const {
operator oaknut::XRegSp() const requires(std::is_same_v<T, oaknut::XReg>) {
return *reg;
}
T operator*() const { return *reg; }
const T* operator->() const { return &*reg; }
~RAReg();