diff --git a/src/dynarmic/backend/arm64/a32_address_space.cpp b/src/dynarmic/backend/arm64/a32_address_space.cpp index 75a9bfb3..119145e6 100644 --- a/src/dynarmic/backend/arm64/a32_address_space.cpp +++ b/src/dynarmic/backend/arm64/a32_address_space.cpp @@ -112,7 +112,10 @@ EmittedBlockInfo A32AddressSpace::Emit(IR::Block block) { mem.unprotect(); - EmittedBlockInfo block_info = EmitArm64(code, std::move(block), {}); + EmittedBlockInfo block_info = EmitArm64(code, std::move(block), { + .enable_cycle_counting = conf.enable_cycle_counting, + .always_little_endian = conf.always_little_endian, + }); Link(block_info); mem.protect(); diff --git a/src/dynarmic/backend/arm64/emit_arm64.cpp b/src/dynarmic/backend/arm64/emit_arm64.cpp index c139b93a..a35cc7eb 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64.cpp @@ -93,19 +93,27 @@ EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const E reg_alloc.AssertNoMoreUses(); - // TODO: Add Cycles + if (emit_conf.enable_cycle_counting) { + const size_t cycles_to_add = block.CycleCount(); + code.LDR(Xscratch0, SP, offsetof(StackLayout, cycles_remaining)); + if (oaknut::AddSubImm::is_valid(cycles_to_add)) { + code.SUBS(Xscratch0, Xscratch0, cycles_to_add); + } else { + code.MOV(Xscratch1, cycles_to_add); + code.SUBS(Xscratch0, Xscratch0, Xscratch1); + } + code.STR(Xscratch0, SP, offsetof(StackLayout, cycles_remaining)); + } - // TODO: Emit Terminal - const auto term = block.GetTerminal(); - const IR::Term::LinkBlock* link_block_term = boost::get(&term); - ASSERT(link_block_term); - code.MOV(Xscratch0, link_block_term->next.Value()); - code.STUR(Xscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15); - ebi.relocations.emplace_back(Relocation{code.ptr() - ebi.entry_point, LinkTarget::ReturnFromRunCode}); - code.NOP(); + EmitA32Terminal(code, ctx); ebi.size = code.ptr() - ebi.entry_point; return ebi; } +void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target) { + ctx.ebi.relocations.emplace_back(Relocation{code.ptr() - ctx.ebi.entry_point, link_target}); + code.NOP(); +} + } // namespace Dynarmic::Backend::Arm64 diff --git a/src/dynarmic/backend/arm64/emit_arm64.h b/src/dynarmic/backend/arm64/emit_arm64.h index 2149c4bd..f20f2aea 100644 --- a/src/dynarmic/backend/arm64/emit_arm64.h +++ b/src/dynarmic/backend/arm64/emit_arm64.h @@ -15,12 +15,14 @@ struct PointerCodeGeneratorPolicy; template class BasicCodeGenerator; using CodeGenerator = BasicCodeGenerator; +struct Label; } // namespace oaknut namespace Dynarmic::IR { class Block; -enum class Opcode; class Inst; +enum class Cond; +enum class Opcode; } // namespace Dynarmic::IR namespace Dynarmic::Backend::Arm64 { @@ -43,13 +45,18 @@ struct EmittedBlockInfo { }; struct EmitConfig { + bool enable_cycle_counting; + bool always_little_endian; }; struct EmitContext; -template -void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); - EmittedBlockInfo EmitArm64(oaknut::CodeGenerator& code, IR::Block block, const EmitConfig& emit_conf); +template +void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst); +void EmitRelocation(oaknut::CodeGenerator& code, EmitContext& ctx, LinkTarget link_target); +oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Cond cond); +void EmitA32Terminal(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 19ce31cc..fc5f1226 100644 --- a/src/dynarmic/backend/arm64/emit_arm64_a32.cpp +++ b/src/dynarmic/backend/arm64/emit_arm64_a32.cpp @@ -19,6 +19,108 @@ namespace Dynarmic::Backend::Arm64 { using namespace oaknut::util; +oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond cond) { + oaknut::Label pass; + // TODO: Flags in host flags + code.LDR(Wscratch0, Xstate, offsetof(A32JitState, cpsr_nzcv)); + code.MSR(static_cast(0b11'011'0100'0010'000), Xscratch0); + code.B(static_cast(cond), pass); + return pass; +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); + +void EmitA32Terminal(oaknut::CodeGenerator&, EmitContext&, IR::Term::Interpret, IR::LocationDescriptor, bool) { + ASSERT_FALSE("Interpret should never be emitted."); +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { + EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode); +} + +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); + }; + + const u32 old_upper = get_upper(old_location); + const u32 new_upper = [&] { + const u32 mask = ~u32(ctx.emit_conf.always_little_endian ? 0x2 : 0); + return get_upper(new_location) & mask; + }(); + + if (old_upper != new_upper) { + code.MOV(Xscratch0, new_upper); + code.STR(Xscratch0, Xstate, offsetof(A32JitState, upper_location_descriptor)); + } +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool) { + EmitSetUpperLocationDescriptor(code, ctx, terminal.next, initial_location); + + code.MOV(Xscratch0, terminal.next.Value()); + code.STUR(Xscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15); + EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode); + + // TODO: Implement LinkBlock optimization +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool) { + EmitSetUpperLocationDescriptor(code, ctx, terminal.next, initial_location); + + code.MOV(Xscratch0, terminal.next.Value()); + code.STUR(Xscratch0, Xstate, offsetof(A32JitState, regs) + sizeof(u32) * 15); + EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode); + + // TODO: Implement LinkBlockFast optimization +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::PopRSBHint, IR::LocationDescriptor, bool) { + EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode); + + // TODO: Implement PopRSBHint optimization +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool) { + EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode); + + // TODO: Implement FastDispatchHint optimization +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + oaknut::Label pass = EmitA32Cond(code, ctx, terminal.if_); + EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step); + code.l(pass); + EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step); +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + oaknut::Label fail; + code.LDRB(Wscratch0, Xstate, offsetof(StackLayout, check_bit)); + code.CBZ(Wscratch0, fail); + EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step); + code.l(fail); + EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step); +} + +void EmitA32Terminal(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); + EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step); + code.l(fail); + EmitRelocation(code, ctx, LinkTarget::ReturnFromRunCode); +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + boost::apply_visitor([&](const auto& t) { EmitA32Terminal(code, ctx, t, initial_location, is_single_step); }, terminal); +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) { + const A32::LocationDescriptor location{ctx.block.Location()}; + EmitA32Terminal(code, ctx, ctx.block.GetTerminal(), location.SetSingleStepping(false), location.SingleStepping()); +} + template<> void EmitIR(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Inst* inst) { const A32::Reg reg = inst->GetArg(0).GetA32RegRef();