Add emit_arm64_a64
This commit is contained in:
parent
a8cb2c33f6
commit
803743488a
6 changed files with 241 additions and 150 deletions
|
@ -303,6 +303,11 @@ EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) {
|
||||||
mem.unprotect();
|
mem.unprotect();
|
||||||
|
|
||||||
const EmitConfig emit_conf{
|
const EmitConfig emit_conf{
|
||||||
|
.tpidr_el0{},
|
||||||
|
.tpidrro_el0{},
|
||||||
|
.cntfreq_el0{},
|
||||||
|
.dczid_el0{},
|
||||||
|
.ctr_el0{},
|
||||||
.hook_isb = conf.hook_isb,
|
.hook_isb = conf.hook_isb,
|
||||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||||
.always_little_endian = conf.always_little_endian,
|
.always_little_endian = conf.always_little_endian,
|
||||||
|
|
|
@ -182,6 +182,7 @@ void A64AddressSpace::EmitPrelude() {
|
||||||
prelude_info.isb_raised = EmitCallTrampoline<&A64::UserCallbacks::InstructionSynchronizationBarrierRaised>(code, conf.callbacks);
|
prelude_info.isb_raised = EmitCallTrampoline<&A64::UserCallbacks::InstructionSynchronizationBarrierRaised>(code, conf.callbacks);
|
||||||
prelude_info.ic_raised = EmitCallTrampoline<&A64::UserCallbacks::InstructionCacheOperationRaised>(code, conf.callbacks);
|
prelude_info.ic_raised = EmitCallTrampoline<&A64::UserCallbacks::InstructionCacheOperationRaised>(code, conf.callbacks);
|
||||||
prelude_info.dc_raised = EmitCallTrampoline<&A64::UserCallbacks::DataCacheOperationRaised>(code, conf.callbacks);
|
prelude_info.dc_raised = EmitCallTrampoline<&A64::UserCallbacks::DataCacheOperationRaised>(code, conf.callbacks);
|
||||||
|
prelude_info.get_cntpct = EmitCallTrampoline<&A64::UserCallbacks::GetCNTPCT>(code, conf.callbacks);
|
||||||
prelude_info.add_ticks = EmitCallTrampoline<&A64::UserCallbacks::AddTicks>(code, conf.callbacks);
|
prelude_info.add_ticks = EmitCallTrampoline<&A64::UserCallbacks::AddTicks>(code, conf.callbacks);
|
||||||
prelude_info.get_ticks_remaining = EmitCallTrampoline<&A64::UserCallbacks::GetTicksRemaining>(code, conf.callbacks);
|
prelude_info.get_ticks_remaining = EmitCallTrampoline<&A64::UserCallbacks::GetTicksRemaining>(code, conf.callbacks);
|
||||||
|
|
||||||
|
@ -201,10 +202,12 @@ void A64AddressSpace::EmitPrelude() {
|
||||||
code.STR(Xticks, SP, offsetof(StackLayout, cycles_to_run));
|
code.STR(Xticks, SP, offsetof(StackLayout, cycles_to_run));
|
||||||
}
|
}
|
||||||
|
|
||||||
code.LDR(Wscratch0, Xstate, offsetof(A64JitState, fpcr));
|
|
||||||
code.MRS(Xscratch1, oaknut::SystemReg::FPCR);
|
code.MRS(Xscratch1, oaknut::SystemReg::FPCR);
|
||||||
code.STR(Wscratch1, SP, offsetof(StackLayout, save_host_fpcr));
|
code.STR(Wscratch1, SP, offsetof(StackLayout, save_host_fpcr));
|
||||||
|
code.LDR(Wscratch0, Xstate, offsetof(A64JitState, fpcr));
|
||||||
|
code.LDR(Wscratch1, Xstate, offsetof(A64JitState, fpsr));
|
||||||
code.MSR(oaknut::SystemReg::FPCR, Xscratch0);
|
code.MSR(oaknut::SystemReg::FPCR, Xscratch0);
|
||||||
|
code.MSR(oaknut::SystemReg::FPSR, Xscratch1);
|
||||||
|
|
||||||
code.LDAR(Wscratch0, Xhalt);
|
code.LDAR(Wscratch0, Xhalt);
|
||||||
code.CBNZ(Wscratch0, return_from_run_code);
|
code.CBNZ(Wscratch0, return_from_run_code);
|
||||||
|
@ -225,10 +228,12 @@ void A64AddressSpace::EmitPrelude() {
|
||||||
code.STR(Xticks, SP, offsetof(StackLayout, cycles_to_run));
|
code.STR(Xticks, SP, offsetof(StackLayout, cycles_to_run));
|
||||||
}
|
}
|
||||||
|
|
||||||
code.LDR(Wscratch0, Xstate, offsetof(A64JitState, fpcr));
|
|
||||||
code.MRS(Xscratch1, oaknut::SystemReg::FPCR);
|
code.MRS(Xscratch1, oaknut::SystemReg::FPCR);
|
||||||
code.STR(Wscratch1, SP, offsetof(StackLayout, save_host_fpcr));
|
code.STR(Wscratch1, SP, offsetof(StackLayout, save_host_fpcr));
|
||||||
|
code.LDR(Wscratch0, Xstate, offsetof(A64JitState, fpcr));
|
||||||
|
code.LDR(Wscratch1, Xstate, offsetof(A64JitState, fpsr));
|
||||||
code.MSR(oaknut::SystemReg::FPCR, Xscratch0);
|
code.MSR(oaknut::SystemReg::FPCR, Xscratch0);
|
||||||
|
code.MSR(oaknut::SystemReg::FPSR, Xscratch1);
|
||||||
|
|
||||||
oaknut::Label step_hr_loop;
|
oaknut::Label step_hr_loop;
|
||||||
code.l(step_hr_loop);
|
code.l(step_hr_loop);
|
||||||
|
@ -311,6 +316,11 @@ EmittedBlockInfo A64AddressSpace::Emit(IR::Block block) {
|
||||||
mem.unprotect();
|
mem.unprotect();
|
||||||
|
|
||||||
const EmitConfig emit_conf{
|
const EmitConfig emit_conf{
|
||||||
|
.tpidr_el0 = conf.tpidr_el0,
|
||||||
|
.tpidrro_el0 = conf.tpidrro_el0,
|
||||||
|
.cntfreq_el0 = conf.cntfrq_el0,
|
||||||
|
.dczid_el0 = conf.dczid_el0,
|
||||||
|
.ctr_el0 = conf.ctr_el0,
|
||||||
.hook_isb = conf.hook_isb,
|
.hook_isb = conf.hook_isb,
|
||||||
.enable_cycle_counting = conf.enable_cycle_counting,
|
.enable_cycle_counting = conf.enable_cycle_counting,
|
||||||
.always_little_endian = true,
|
.always_little_endian = true,
|
||||||
|
@ -437,6 +447,9 @@ void A64AddressSpace::Link(IR::LocationDescriptor block_descriptor, EmittedBlock
|
||||||
case LinkTarget::DataCacheOperationRaised:
|
case LinkTarget::DataCacheOperationRaised:
|
||||||
c.BL(prelude_info.dc_raised);
|
c.BL(prelude_info.dc_raised);
|
||||||
break;
|
break;
|
||||||
|
case LinkTarget::GetCNTPCT:
|
||||||
|
c.BL(prelude_info.get_cntpct);
|
||||||
|
break;
|
||||||
case LinkTarget::AddTicks:
|
case LinkTarget::AddTicks:
|
||||||
c.BL(prelude_info.add_ticks);
|
c.BL(prelude_info.add_ticks);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -86,6 +86,7 @@ private:
|
||||||
void* dc_raised;
|
void* dc_raised;
|
||||||
void* ic_raised;
|
void* ic_raised;
|
||||||
void* isb_raised;
|
void* isb_raised;
|
||||||
|
void* get_cntpct;
|
||||||
void* add_ticks;
|
void* add_ticks;
|
||||||
void* get_ticks_remaining;
|
void* get_ticks_remaining;
|
||||||
} prelude_info;
|
} prelude_info;
|
||||||
|
|
|
@ -68,6 +68,7 @@ enum class LinkTarget {
|
||||||
InstructionSynchronizationBarrierRaised,
|
InstructionSynchronizationBarrierRaised,
|
||||||
InstructionCacheOperationRaised,
|
InstructionCacheOperationRaised,
|
||||||
DataCacheOperationRaised,
|
DataCacheOperationRaised,
|
||||||
|
GetCNTPCT,
|
||||||
AddTicks,
|
AddTicks,
|
||||||
GetTicksRemaining,
|
GetTicksRemaining,
|
||||||
};
|
};
|
||||||
|
@ -89,6 +90,11 @@ struct EmittedBlockInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmitConfig {
|
struct EmitConfig {
|
||||||
|
u64* tpidr_el0;
|
||||||
|
const u64* tpidrro_el0;
|
||||||
|
u64 cntfreq_el0;
|
||||||
|
u32 dczid_el0;
|
||||||
|
u32 ctr_el0;
|
||||||
bool hook_isb;
|
bool hook_isb;
|
||||||
bool enable_cycle_counting;
|
bool enable_cycle_counting;
|
||||||
bool always_little_endian;
|
bool always_little_endian;
|
||||||
|
|
|
@ -4,8 +4,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <oaknut/oaknut.hpp>
|
#include <oaknut/oaknut.hpp>
|
||||||
|
#include <mcl/bit_cast.hpp>
|
||||||
|
|
||||||
#include "dynarmic/backend/arm64/a32_jitstate.h"
|
#include "dynarmic/backend/arm64/a64_jitstate.h"
|
||||||
#include "dynarmic/backend/arm64/abi.h"
|
#include "dynarmic/backend/arm64/abi.h"
|
||||||
#include "dynarmic/backend/arm64/emit_arm64.h"
|
#include "dynarmic/backend/arm64/emit_arm64.h"
|
||||||
#include "dynarmic/backend/arm64/emit_context.h"
|
#include "dynarmic/backend/arm64/emit_context.h"
|
||||||
|
@ -18,292 +19,357 @@ namespace Dynarmic::Backend::Arm64 {
|
||||||
|
|
||||||
using namespace oaknut::util;
|
using namespace oaknut::util;
|
||||||
|
|
||||||
|
static constexpr int nzcv_c_flag_shift = 29;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetCheckBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetCheckBit>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
if (args[0].IsImmediate()) {
|
||||||
ASSERT_FALSE("Unimplemented");
|
if (args[0].GetImmediateU1()) {
|
||||||
|
code.MOV(Wscratch0, 1);
|
||||||
|
code.STRB(Wscratch0, SP, offsetof(StackLayout, check_bit));
|
||||||
|
} else {
|
||||||
|
code.STRB(WZR, SP, offsetof(StackLayout, check_bit));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto Wbit = ctx.reg_alloc.ReadW(args[0]);
|
||||||
|
RegAlloc::Realize(Wbit);
|
||||||
|
code.STRB(Wbit, SP, offsetof(StackLayout, check_bit));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetCFlag>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetCFlag>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
(void)inst;
|
RegAlloc::Realize(Wresult);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.LDR(Wresult, Xstate, offsetof(A64JitState, cpsr_nzcv));
|
||||||
|
code.UBFX(Wresult, Wresult, nzcv_c_flag_shift, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetNZCVRaw>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetNZCVRaw>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Wnzcv = ctx.reg_alloc.WriteW(inst);
|
||||||
(void)inst;
|
RegAlloc::Realize(Wnzcv);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
code.LDR(Wnzcv, Xstate, offsetof(A64JitState, cpsr_nzcv));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetNZCVRaw>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetNZCVRaw>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Wnzcv = ctx.reg_alloc.ReadW(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Wnzcv);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
code.STR(Wnzcv, Xstate, offsetof(A64JitState, cpsr_nzcv));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetNZCV>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetNZCV>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Wnzcv = ctx.reg_alloc.ReadW(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Wnzcv);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
code.STR(Wnzcv, Xstate, offsetof(A64JitState, cpsr_nzcv));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetW>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetW>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Wresult);
|
||||||
|
|
||||||
|
// TODO: Detect if Gpr vs Fpr is more appropriate
|
||||||
|
|
||||||
|
code.LDR(Wresult, Xstate, offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetX>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetX>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Xresult);
|
||||||
|
|
||||||
|
// TODO: Detect if Gpr vs Fpr is more appropriate
|
||||||
|
|
||||||
|
code.LDR(Xresult, Xstate, offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetS>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetS>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
(void)ctx;
|
auto Sresult = ctx.reg_alloc.WriteS(inst);
|
||||||
(void)inst;
|
RegAlloc::Realize(Sresult);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.LDR(Sresult, Xstate, offsetof(A64JitState, vec) + sizeof(u64)*2 * static_cast<size_t>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetD>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetD>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
(void)ctx;
|
auto Dresult = ctx.reg_alloc.WriteD(inst);
|
||||||
(void)inst;
|
RegAlloc::Realize(Dresult);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.LDR(Dresult, Xstate, offsetof(A64JitState, vec) + sizeof(u64)*2 * static_cast<size_t>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetQ>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetQ>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
(void)ctx;
|
auto Qresult = ctx.reg_alloc.WriteQ(inst);
|
||||||
(void)inst;
|
RegAlloc::Realize(Qresult);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.LDR(Qresult, Xstate, offsetof(A64JitState, vec) + sizeof(u64)*2 * static_cast<size_t>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetSP>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetSP>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto Xresult = ctx.reg_alloc.WriteX(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Xresult);
|
||||||
|
|
||||||
|
code.LDR(Xresult, Xstate, offsetof(A64JitState, sp));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetFPCR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetFPCR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Wresult);
|
||||||
|
|
||||||
|
code.LDR(Wresult, Xstate, offsetof(A64JitState, fpcr));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetFPSR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetFPSR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto Wresult = ctx.reg_alloc.WriteW(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Wresult);
|
||||||
|
|
||||||
|
code.LDR(Wresult, Xstate, offsetof(A64JitState, fpsr));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetW>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetW>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
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(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetX>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetX>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
const A64::Reg reg = inst->GetArg(0).GetA64RegRef();
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
auto Xvalue = ctx.reg_alloc.ReadX(args[1]);
|
||||||
|
RegAlloc::Realize(Xvalue);
|
||||||
|
|
||||||
|
// TODO: Detect if Gpr vs Fpr is more appropriate
|
||||||
|
|
||||||
|
code.STR(Xvalue, Xstate, offsetof(A64JitState, reg) + sizeof(u64) * static_cast<size_t>(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetS>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetS>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
(void)inst;
|
auto Svalue = ctx.reg_alloc.ReadS(args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Svalue);
|
||||||
|
code.STR(Svalue, Xstate, offsetof(A64JitState, vec) + sizeof(u64)*2*static_cast<size_t>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetD>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetD>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
(void)inst;
|
auto Dvalue = ctx.reg_alloc.ReadD(args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Dvalue);
|
||||||
|
code.STR(Dvalue, Xstate, offsetof(A64JitState, vec) + sizeof(u64)*2*static_cast<size_t>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetQ>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetQ>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
const A64::Vec vec = inst->GetArg(0).GetA64VecRef();
|
||||||
(void)inst;
|
auto Qvalue = ctx.reg_alloc.ReadQ(args[1]);
|
||||||
ASSERT_FALSE("Unimplemented");
|
RegAlloc::Realize(Qvalue);
|
||||||
|
code.STR(Qvalue, Xstate, offsetof(A64JitState, vec) + sizeof(u64)*2*static_cast<size_t>(vec));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetSP>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetSP>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Xvalue);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.STR(Xvalue, Xstate, offsetof(A64JitState, sp));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetFPCR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetFPCR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Wvalue = ctx.reg_alloc.ReadW(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Wvalue);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.STR(Wvalue, Xstate, offsetof(A64JitState, fpcr));
|
||||||
|
code.MSR(oaknut::SystemReg::FPCR, Wvalue->toX());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetFPSR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetFPSR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Wvalue = ctx.reg_alloc.ReadW(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Wvalue);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.STR(Wvalue, Xstate, offsetof(A64JitState, fpsr));
|
||||||
|
code.MSR(oaknut::SystemReg::FPSR, Wvalue->toX());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetPC>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetPC>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Xvalue);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.STR(Xvalue, Xstate, offsetof(A64JitState, pc));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64CallSupervisor>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64CallSupervisor>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
ctx.reg_alloc.PrepareForCall(nullptr);
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
if (ctx.conf.enable_cycle_counting) {
|
||||||
|
code.LDR(Xscratch0, SP, offsetof(StackLayout, cycles_to_run));
|
||||||
|
code.SUB(Xscratch0, Xscratch0, Xticks);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::AddTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.MOV(W1, args[0].GetImmediateU32());
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::CallSVC);
|
||||||
|
|
||||||
|
if (ctx.conf.enable_cycle_counting) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::GetTicksRemaining);
|
||||||
|
code.STR(X0, SP, offsetof(StackLayout, cycles_to_run));
|
||||||
|
code.MOV(Xticks, X0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64ExceptionRaised>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64ExceptionRaised>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
ctx.reg_alloc.PrepareForCall(nullptr);
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
if (ctx.conf.enable_cycle_counting) {
|
||||||
|
code.LDR(Xscratch0, SP, offsetof(StackLayout, cycles_to_run));
|
||||||
|
code.SUB(Xscratch0, Xscratch0, Xticks);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::AddTicks);
|
||||||
|
}
|
||||||
|
|
||||||
|
code.MOV(X1, args[0].GetImmediateU64());
|
||||||
|
code.MOV(X2, args[1].GetImmediateU64());
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::ExceptionRaised);
|
||||||
|
|
||||||
|
if (ctx.conf.enable_cycle_counting) {
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::GetTicksRemaining);
|
||||||
|
code.STR(X0, SP, offsetof(StackLayout, cycles_to_run));
|
||||||
|
code.MOV(Xticks, X0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64DataCacheOperationRaised>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64DataCacheOperationRaised>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
ctx.reg_alloc.PrepareForCall(nullptr, args[1], args[2]);
|
||||||
(void)inst;
|
EmitRelocation(code, ctx, LinkTarget::DataCacheOperationRaised);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64InstructionCacheOperationRaised>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64InstructionCacheOperationRaised>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
ctx.reg_alloc.PrepareForCall(nullptr, args[1], args[2]);
|
||||||
(void)inst;
|
EmitRelocation(code, ctx, LinkTarget::InstructionCacheOperationRaised);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64DataSynchronizationBarrier>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64DataSynchronizationBarrier>(oaknut::CodeGenerator& code, EmitContext&, IR::Inst*) {
|
||||||
(void)code;
|
code.DSB(oaknut::BarrierOp::SY);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64DataMemoryBarrier>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64DataMemoryBarrier>(oaknut::CodeGenerator& code, EmitContext&, IR::Inst*) {
|
||||||
(void)code;
|
code.DMB(oaknut::BarrierOp::SY);
|
||||||
(void)ctx;
|
|
||||||
(void)inst;
|
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64InstructionSynchronizationBarrier>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64InstructionSynchronizationBarrier>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
if (!ctx.conf.hook_isb) {
|
||||||
(void)ctx;
|
return;
|
||||||
(void)inst;
|
}
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
|
ctx.reg_alloc.PrepareForCall(nullptr);
|
||||||
|
EmitRelocation(code, ctx, LinkTarget::InstructionSynchronizationBarrierRaised);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetCNTFRQ>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetCNTFRQ>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto Xvalue = ctx.reg_alloc.WriteX(inst);
|
||||||
(void)ctx;
|
RegAlloc::Realize(Xvalue);
|
||||||
(void)inst;
|
code.MOV(Xvalue, ctx.conf.cntfreq_el0);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetCNTPCT>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetCNTPCT>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
// FIXME: AddTicks / GetTicksRemaining
|
||||||
(void)ctx;
|
ctx.reg_alloc.PrepareForCall(inst);
|
||||||
(void)inst;
|
EmitRelocation(code, ctx, LinkTarget::GetCNTPCT);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetCTR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetCTR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto Wvalue = ctx.reg_alloc.WriteW(inst);
|
||||||
(void)ctx;
|
RegAlloc::Realize(Wvalue);
|
||||||
(void)inst;
|
code.MOV(Wvalue, ctx.conf.ctr_el0);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetDCZID>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetDCZID>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto Wvalue = ctx.reg_alloc.WriteW(inst);
|
||||||
(void)ctx;
|
RegAlloc::Realize(Wvalue);
|
||||||
(void)inst;
|
code.MOV(Wvalue, ctx.conf.dczid_el0);
|
||||||
ASSERT_FALSE("Unimplemented");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetTPIDR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetTPIDR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto Xvalue = ctx.reg_alloc.WriteX(inst);
|
||||||
(void)ctx;
|
RegAlloc::Realize(Xvalue);
|
||||||
(void)inst;
|
code.MOV(Xscratch0, mcl::bit_cast<u64>(ctx.conf.tpidr_el0));
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.LDR(Xvalue, Xscratch0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64GetTPIDRRO>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64GetTPIDRRO>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto Xvalue = ctx.reg_alloc.WriteX(inst);
|
||||||
(void)ctx;
|
RegAlloc::Realize(Xvalue);
|
||||||
(void)inst;
|
code.MOV(Xscratch0, mcl::bit_cast<u64>(ctx.conf.tpidrro_el0));
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.LDR(Xvalue, Xscratch0);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void EmitIR<IR::Opcode::A64SetTPIDR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
void EmitIR<IR::Opcode::A64SetTPIDR>(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) {
|
||||||
(void)code;
|
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
|
||||||
(void)ctx;
|
auto Xvalue = ctx.reg_alloc.ReadX(args[0]);
|
||||||
(void)inst;
|
RegAlloc::Realize(Xvalue);
|
||||||
ASSERT_FALSE("Unimplemented");
|
code.MOV(Xscratch0, mcl::bit_cast<u64>(ctx.conf.tpidrro_el0));
|
||||||
|
code.STR(Xvalue, Xscratch0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Dynarmic::Backend::Arm64
|
} // namespace Dynarmic::Backend::Arm64
|
||||||
|
|
|
@ -198,7 +198,7 @@ struct UserConfig {
|
||||||
|
|
||||||
/// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into
|
/// Pointer to where TPIDR_EL0 is stored. This pointer will be inserted into
|
||||||
/// emitted code.
|
/// emitted code.
|
||||||
const std::uint64_t* tpidr_el0 = nullptr;
|
std::uint64_t* tpidr_el0 = nullptr;
|
||||||
|
|
||||||
/// Pointer to the page table which we can use for direct page table access.
|
/// Pointer to the page table which we can use for direct page table access.
|
||||||
/// If an entry in page_table is null, the relevant memory callback will be called.
|
/// If an entry in page_table is null, the relevant memory callback will be called.
|
||||||
|
|
Loading…
Reference in a new issue