diff --git a/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/backend/arm64/a32_address_space.cpp index 306db097..59c9193e 100644 --- a/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -308,6 +308,7 @@ EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) { .cntfreq_el0{}, .dczid_el0{}, .ctr_el0{}, + .is_a64 = false, .hook_isb = conf.hook_isb, .enable_cycle_counting = conf.enable_cycle_counting, .always_little_endian = conf.always_little_endian, diff --git a/src/dynarmic/backend/arm64/a64_address_space.cpp b/src/dynarmic/backend/arm64/a64_address_space.cpp index b2232aeb..fe063f5a 100644 --- a/src/dynarmic/backend/arm64/a64_address_space.cpp +++ b/src/dynarmic/backend/arm64/a64_address_space.cpp @@ -321,6 +321,7 @@ EmittedBlockInfo A64AddressSpace::Emit(IR::Block block) { .cntfreq_el0 = conf.cntfrq_el0, .dczid_el0 = conf.dczid_el0, .ctr_el0 = conf.ctr_el0, + .is_a64 = true, .hook_isb = conf.hook_isb, .enable_cycle_counting = conf.enable_cycle_counting, .always_little_endian = true, diff --git a/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/backend/arm64/emit_arm64.cpp index 104d173d..ffd07954 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -164,10 +164,18 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E ASSERT(!ctx.block.HasConditionFailedLocation()); } else { ASSERT(ctx.block.HasConditionFailedLocation()); + oaknut::Label pass; + + if (conf.is_a64) { + pass = EmitA64Cond(code, ctx, ctx.block.GetCondition()); + EmitAddCycles(code, ctx, ctx.block.ConditionFailedCycleCount()); + EmitA64ConditionFailedTerminal(code, ctx); + } else { + pass = EmitA32Cond(code, ctx, ctx.block.GetCondition()); + EmitAddCycles(code, ctx, ctx.block.ConditionFailedCycleCount()); + EmitA32ConditionFailedTerminal(code, ctx); + } - oaknut::Label pass = EmitA32Cond(code, ctx, ctx.block.GetCondition()); - EmitAddCycles(code, ctx, ctx.block.ConditionFailedCycleCount()); - EmitA32ConditionFailedTerminal(code, ctx); code.l(pass); } @@ -205,7 +213,11 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E reg_alloc.AssertNoMoreUses(); EmitAddCycles(code, ctx, block.CycleCount()); - EmitA32Terminal(code, ctx); + if (conf.is_a64) { + EmitA64Terminal(code, ctx); + } else { + EmitA32Terminal(code, ctx); + } ebi.size = code.ptr() - ebi.entry_point; return ebi; diff --git a/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/backend/arm64/emit_arm64.h index 290230ac..b22bf810 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.h +++ b/src/dynarmic/backend/arm64/emit_arm64.h @@ -95,6 +95,7 @@ struct EmitConfig { u64 cntfreq_el0; u32 dczid_el0; u32 ctr_el0; + bool is_a64; bool hook_isb; bool enable_cycle_counting; bool always_little_endian; @@ -120,7 +121,10 @@ void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target); void EmitBlockLinkRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, const IR::LocationDescriptor& descriptor); oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond); +oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond); void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx); +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx); void EmitA32ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx); +void EmitA64ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx); } // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/emit_arm64_a32.cpp b/src/dynarmic/backend/arm64/emit_arm64_a32.cpp index 493d75eb..f8fc5eee 100644 --- a/src/dynarmic/backend/arm64/emit_arm64_a32.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64_a32.cpp @@ -41,7 +41,7 @@ void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Re EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); } -void EmitSetUpperLocationDescriptor(oaknut::CodeGenerator& code, EmitContext& ctx, IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) { +static void EmitSetUpperLocationDescriptor(oaknut::CodeGenerator& code, EmitContext& ctx, IR::LocationDescriptor new_location, IR::LocationDescriptor old_location) { auto get_upper = [](const IR::LocationDescriptor& desc) -> u32 { return static_cast(A32::LocationDescriptor{desc}.SetSingleStepping(false).UniqueHash() >> 32); }; diff --git a/src/dynarmic/backend/arm64/emit_arm64_a64.cpp b/src/dynarmic/backend/arm64/emit_arm64_a64.cpp index f0a35ff6..d24f2481 100644 --- a/src/dynarmic/backend/arm64/emit_arm64_a64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64_a64.cpp @@ -21,6 +21,107 @@ using namespace oaknut::util; static constexpr int nzcv_c_flag_shift = 29; +oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond cond) { + oaknut::Label pass; + // TODO: Flags in host flags + code.LDR(Wscratch0, Xstate, offsetof(A64JitState, cpsr_nzcv)); + code.MSR(oaknut::SystemReg::NZCV, Xscratch0); + code.B(static_cast(cond), pass); + return pass; +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); + +void EmitA64Terminal(oaknut::CodeGenerator&, EmitContext&, IR::Term::Interpret, IR::LocationDescriptor, bool) { + ASSERT_FALSE("Interpret should never be emitted."); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { + EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) { + oaknut::Label fail; + + if (ctx.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { + if (ctx.conf.enable_cycle_counting) { + code.CMP(Xticks, 0); + code.B(LE, fail); + EmitBlockLinkRelocation(code, ctx, terminal.next); + } else { + code.LDAR(Wscratch0, Xhalt); + code.CBNZ(Wscratch0, fail); + EmitBlockLinkRelocation(code, ctx, terminal.next); + } + } + + code.l(fail); + code.MOV(Xscratch0, A64::LocationDescriptor{terminal.next}.PC()); + code.STR(Xscratch0, Xstate, offsetof(A64JitState, pc)); + EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { + if (ctx.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { + EmitBlockLinkRelocation(code, ctx, terminal.next); + } + + code.MOV(Wscratch0, A64::LocationDescriptor{terminal.next}.PC()); + code.STR(Wscratch0, Xstate, offsetof(A64JitState, pc)); + EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) { + EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); + + // TODO: Implement PopRSBHint optimization +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) { + EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); + + // TODO: Implement FastDispatchHint optimization +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + oaknut::Label pass = EmitA64Cond(code, ctx, terminal.if_); + EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step); + code.l(pass); + EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + oaknut::Label fail; + code.LDRB(Wscratch0, SP, offsetof(StackLayout, check_bit)); + code.CBZ(Wscratch0, fail); + EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step); + code.l(fail); + EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + oaknut::Label fail; + code.LDAR(Wscratch0, Xhalt); + code.CBNZ(Wscratch0, fail); + EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step); + code.l(fail); + EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + boost::apply_visitor([&](const auto& t) { EmitA64Terminal(code, ctx, t, initial_location, is_single_step); }, terminal); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) { + const A64::LocationDescriptor location{ctx.block.Location()}; + EmitA64Terminal(code, ctx, ctx.block.GetTerminal(), location.SetSingleStepping(false), location.SingleStepping()); +} + +void EmitA64ConditionFailedTerminal(oaknut::CodeGenerator& code, EmitContext& ctx) { + const A64::LocationDescriptor location{ctx.block.Location()}; + EmitA64Terminal(code, ctx, IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, location.SetSingleStepping(false), location.SingleStepping()); +} + template<> void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { auto args = ctx.reg_alloc.GetArgumentInfo(inst);