backend/arm64: Toy implementation of enough to execute LSLS
This commit is contained in:
parent
7e046357ff
commit
77436bbbbb
6 changed files with 270 additions and 4 deletions
|
@ -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()
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
76
src/dynarmic/backend/arm64/emit_arm64_a32.cpp
Normal file
76
src/dynarmic/backend/arm64/emit_arm64_a32.cpp
Normal 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
|
137
src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp
Normal file
137
src/dynarmic/backend/arm64/emit_arm64_data_processing.cpp
Normal 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
|
26
src/dynarmic/backend/arm64/emit_context.h
Normal file
26
src/dynarmic/backend/arm64/emit_context.h
Normal 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
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue