From 7b13113fbec7e3d37d064f1d30fb72a8a65467cc Mon Sep 17 00:00:00 2001 From: lizzie Date: Sat, 25 Jul 2026 21:43:58 +0200 Subject: [PATCH] [dynarmic] flatten terminal variants to not use recursive pointers (#4218) take If{then, else} for example 100% of the time If{} only has a leaf terminal, supporting If{If{}, If{}} is very dumb and we don't need that so remove that this reduces > The recursive_wrapper class template has an interface similar to a simple value container, but its content is allocated dynamically aka. uses malloc and spams heap EVERYTIME A CONDITIONAL TERMINAL IS USED thats bad tbf, i dont like it, removing it is better for general speedup also we dont REALLY need if{if{if{if{}}}} support, like really Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4218 Reviewed-by: MaranBr Reviewed-by: Maufeat --- .../dynarmic/backend/arm64/emit_arm64_a32.cpp | 39 ++++-- .../dynarmic/backend/arm64/emit_arm64_a64.cpp | 39 ++++-- .../backend/riscv64/emit_riscv64_a32.cpp | 35 ++++- .../src/dynarmic/backend/x64/a32_emit_x64.cpp | 67 +++++++--- .../src/dynarmic/backend/x64/a32_emit_x64.h | 3 +- .../src/dynarmic/backend/x64/a64_emit_x64.cpp | 68 +++++++--- .../src/dynarmic/backend/x64/a64_emit_x64.h | 3 +- .../src/dynarmic/backend/x64/emit_x64.h | 3 +- src/dynarmic/src/dynarmic/ir/basic_block.cpp | 65 ++++----- src/dynarmic/src/dynarmic/ir/basic_block.h | 10 +- src/dynarmic/src/dynarmic/ir/ir_emitter.h | 2 +- src/dynarmic/src/dynarmic/ir/terminal.h | 123 ++++++++---------- src/dynarmic/tests/A32/fuzz_arm.cpp | 40 ++---- 13 files changed, 296 insertions(+), 201 deletions(-) diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp index 213403b4ba..b48ff057a7 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp @@ -34,7 +34,8 @@ oaknut::Label EmitA32Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co return pass; } -void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); +void EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step); +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step); void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); @@ -125,31 +126,53 @@ void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa 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); + EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step); code.l(pass); - EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step); + EmitA32LeafTerminal(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, SP, offsetof(StackLayout, check_bit)); code.CBZ(Wscratch0, fail); - EmitA32Terminal(code, ctx, terminal.then_, initial_location, is_single_step); + EmitA32LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step); code.l(fail); - EmitA32Terminal(code, ctx, terminal.else_, initial_location, is_single_step); + EmitA32LeafTerminal(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); + EmitA32LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step); code.l(fail); EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); } -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 EmitA32LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + UNREACHABLE(); +} + +void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step); + UNREACHABLE(); } void EmitA32Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) { diff --git a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a64.cpp b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a64.cpp index da4364c1b5..69533b9ca4 100644 --- a/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a64.cpp +++ b/src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a64.cpp @@ -33,7 +33,8 @@ oaknut::Label EmitA64Cond(oaknut::CodeGenerator& code, EmitContext&, IR::Cond co return pass; } -void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); +void EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step); +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step); void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { EmitRelocation(code, ctx, LinkTarget::ReturnToDispatcher); @@ -108,31 +109,53 @@ void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Fa 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); + EmitA64LeafTerminal(code, ctx, terminal.else_, initial_location, is_single_step); code.l(pass); - EmitA64Terminal(code, ctx, terminal.then_, initial_location, is_single_step); + EmitA64LeafTerminal(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); + EmitA64LeafTerminal(code, ctx, terminal.then_, initial_location, is_single_step); code.l(fail); - EmitA64Terminal(code, ctx, terminal.else_, initial_location, is_single_step); + EmitA64LeafTerminal(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); + EmitA64LeafTerminal(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 EmitA64LeafTerminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + UNREACHABLE(); +} + +void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx, IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + if (auto const x = std::get_if(&terminal)) + return EmitA64LeafTerminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step); + UNREACHABLE(); } void EmitA64Terminal(oaknut::CodeGenerator& code, EmitContext& ctx) { diff --git a/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp b/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp index 8218ca3489..36709c7dbc 100644 --- a/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp +++ b/src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp @@ -112,6 +112,7 @@ void EmitA32Cond(biscuit::Assembler& as, EmitContext&, IR::Cond cond, biscuit::L } } +void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step); void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { @@ -170,18 +171,18 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::FastDis void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { biscuit::Label pass; EmitA32Cond(as, ctx, terminal.if_, &pass); - EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step); + EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step); as.Bind(&pass); - EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step); + EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step); } void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { biscuit::Label fail; as.LBU(Xscratch0, offsetof(StackLayout, check_bit), Xstate); as.BEQZ(Xscratch0, &fail); - EmitA32Terminal(as, ctx, terminal.then_, initial_location, is_single_step); + EmitA32LeafTerminal(as, ctx, terminal.then_, initial_location, is_single_step); as.Bind(&fail); - EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step); + EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step); } void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { @@ -189,13 +190,35 @@ void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::CheckHa as.LWU(Xscratch0, 0, Xhalt); as.FENCE(biscuit::FenceOrder::RW, biscuit::FenceOrder::RW); as.BNEZ(Xscratch0, &fail); - EmitA32Terminal(as, ctx, terminal.else_, initial_location, is_single_step); + EmitA32LeafTerminal(as, ctx, terminal.else_, initial_location, is_single_step); as.Bind(&fail); EmitRelocation(as, ctx, LinkTarget::ReturnFromRunCode); } +void EmitA32LeafTerminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::LeafTerminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); + UNREACHABLE(); +} + void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx, IR::Term::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) { - boost::apply_visitor([&](const auto& t) { EmitA32Terminal(as, ctx, t, initial_location, is_single_step); }, terminal); + if (auto const x = std::get_if(&terminal)) + return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step); + UNREACHABLE(); } void EmitA32Terminal(biscuit::Assembler& as, EmitContext& ctx) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp index 1c21886c60..a062ed6221 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp @@ -175,7 +175,7 @@ finish_this_inst: if (conf.enable_cycle_counting) EmitAddCycles(block.CycleCount()); code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]); - EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); + EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.int3(); for (auto& deferred_emit : ctx.deferred_emits) @@ -219,7 +219,7 @@ void A32EmitX64::EmitCondPrelude(const A32EmitContext& ctx) { if (conf.enable_cycle_counting) { EmitAddCycles(ctx.block.ConditionFailedCycleCount()); } - EmitTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); + EmitLeafTerminal(IR::Term::LinkBlock{ctx.block.ConditionFailedLocation()}, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.L(pass); } @@ -1155,11 +1155,12 @@ void A32EmitX64::EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_locat } namespace { -void EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { e.code.ReturnFromRunCode(); + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor initial_location, bool is_single_step) { e.EmitSetUpperLocationDescriptor(terminal.next, initial_location); if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) { e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); @@ -1186,9 +1187,10 @@ void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD e.PushRSBHelper(rax, rbx, terminal.next); e.code.ForceReturnFromRunCode(); } + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor initial_location, bool is_single_step) { e.EmitSetUpperLocationDescriptor(terminal.next, initial_location); if (!e.conf.HasOptimization(OptimizationFlag::BlockLinking) || is_single_step) { e.code.mov(MJitStateReg(A32::Reg::PC), A32::LocationDescriptor{terminal.next}.PC()); @@ -1201,55 +1203,78 @@ void EmitTerminalImpl(A32EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat e.EmitPatchJmp(terminal.next); } } + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { if (!e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) || is_single_step) { e.code.ReturnFromRunCode(); } else { e.code.jmp(e.terminal_handler_pop_rsb_hint); } + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) { e.code.ReturnFromRunCode(); } else { e.code.jmp(e.terminal_handler_fast_dispatch_hint); } + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label pass = e.EmitCond(terminal.if_); - e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step); e.code.L(pass); - e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step); + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label fail; e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0)); e.code.jz(fail); - e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step); e.code.L(fail); - e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step); + return true; } -void EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A32EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A32JitState, halt_reason)], 0); e.code.jne(e.code.GetForceReturnFromRunCodeAddress()); - e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step); + return true; } -void EmitTerminalImpl(A32EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) { - UNREACHABLE(); } + +bool A32EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + UNREACHABLE(); } -void A32EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { - boost::apply_visitor([this, initial_location, is_single_step](auto x) { - EmitTerminalImpl(*this, x, initial_location, is_single_step); - }, terminal); +bool A32EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { + if (auto const e = std::get_if(&terminal)) + return EmitLeafTerminal(*e, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + UNREACHABLE(); } void A32EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h index 8e97dc7737..31b1271800 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h @@ -112,7 +112,8 @@ public: // Terminal instruction emitters void EmitSetUpperLocationDescriptor(IR::LocationDescriptor new_location, IR::LocationDescriptor old_location); - void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; + bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; + bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; // Patching void Unpatch(const IR::LocationDescriptor& target_desc) override; diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp index 53381aa1df..933008c258 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp @@ -12,6 +12,7 @@ #include #include "common/assert.h" #include "common/common_types.h" +#include "dynarmic/ir/terminal.h" #include "dynarmic/mcl/integer_of_size.hpp" #include @@ -147,7 +148,7 @@ finish_this_inst: if (conf.enable_cycle_counting) EmitAddCycles(block.CycleCount()); code.mov(rbp, code.qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, abi_base_pointer)]); - EmitTerminal(block.GetTerminal(), ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); + EmitTerminal(block.terminal, ctx.Location().SetSingleStepping(false), ctx.IsSingleStep()); code.int3(); for (auto& deferred_emit : ctx.deferred_emits) deferred_emit(); @@ -617,11 +618,12 @@ std::string A64EmitX64::LocationDescriptorToFriendlyName(const IR::LocationDescr } namespace { -void EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::ReturnToDispatch, IR::LocationDescriptor, bool) { e.code.ReturnFromRunCode(); + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationDescriptor, bool is_single_step) { // Used for patches and linking if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { if (e.conf.enable_cycle_counting) { @@ -649,9 +651,10 @@ void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlock terminal, IR::LocationD e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); e.code.ReturnFromRunCode(); } + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::LocationDescriptor, bool is_single_step) { if (e.conf.HasOptimization(OptimizationFlag::BlockLinking) && !is_single_step) { e.patch_information[terminal.next].jmp.push_back(e.code.getCurr()); if (auto next_bb = e.GetBasicBlock(terminal.next)) { @@ -664,63 +667,86 @@ void EmitTerminalImpl(A64EmitX64& e, IR::Term::LinkBlockFast terminal, IR::Locat e.code.mov(qword[e.code.ABI_JIT_PTR + offsetof(A64JitState, pc)], rax); e.code.ReturnFromRunCode(); } + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::PopRSBHint, IR::LocationDescriptor, bool is_single_step) { if (e.conf.HasOptimization(OptimizationFlag::ReturnStackBuffer) && !is_single_step) { e.code.jmp(e.terminal_handler_pop_rsb_hint); } else { e.code.ReturnFromRunCode(); } + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::FastDispatchHint, IR::LocationDescriptor, bool is_single_step) { if (!e.conf.HasOptimization(OptimizationFlag::FastDispatch) || is_single_step) { e.code.ReturnFromRunCode(); } else { e.code.jmp(e.terminal_handler_fast_dispatch_hint); } + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::If terminal, IR::LocationDescriptor initial_location, bool is_single_step) { switch (terminal.if_) { case IR::Cond::AL: case IR::Cond::NV: - e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step); break; default: Xbyak::Label pass = e.EmitCond(terminal.if_); - e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step); e.code.L(pass); - e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step); break; } + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckBit terminal, IR::LocationDescriptor initial_location, bool is_single_step) { Xbyak::Label fail; e.code.cmp(e.code.byte[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, check_bit)], u8(0)); e.code.jz(fail); - e.EmitTerminal(terminal.then_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.then_, initial_location, is_single_step); e.code.L(fail); - e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step); + return true; } -void EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { +bool EmitTerminalImpl(A64EmitX64& e, IR::Term::CheckHalt terminal, IR::LocationDescriptor initial_location, bool is_single_step) { e.code.cmp(dword[e.code.ABI_JIT_PTR + offsetof(A64JitState, halt_reason)], 0); e.code.jne(e.code.GetForceReturnFromRunCodeAddress()); - e.EmitTerminal(terminal.else_, initial_location, is_single_step); + e.EmitLeafTerminal(terminal.else_, initial_location, is_single_step); + return true; } -void EmitTerminalImpl(A64EmitX64&, IR::Term::Invalid, IR::LocationDescriptor, bool) { - UNREACHABLE(); } + +bool A64EmitX64::EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + UNREACHABLE(); } -void A64EmitX64::EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { - boost::apply_visitor([this, initial_location, is_single_step](auto x) { - EmitTerminalImpl(*this, x, initial_location, is_single_step); - }, terminal); +bool A64EmitX64::EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept { + if (auto const x = std::get_if(&terminal)) + return EmitLeafTerminal(*x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + if (auto const x = std::get_if(&terminal)) + return EmitTerminalImpl(*this, *x, initial_location, is_single_step); + UNREACHABLE(); } void A64EmitX64::EmitPatchJg(const IR::LocationDescriptor& target_desc, CodePtr target_code_ptr) { diff --git a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h index d57b1d81b9..8800455813 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h @@ -107,7 +107,8 @@ public: void EmitExclusiveWriteMemoryInline(A64EmitContext& ctx, IR::Inst* inst); // Terminal instruction emitters - void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; + bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; + bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept override; // Patching void Unpatch(const IR::LocationDescriptor& target_desc) override; diff --git a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h index 619945e19a..11494c2d3e 100644 --- a/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h +++ b/src/dynarmic/src/dynarmic/backend/x64/emit_x64.h @@ -111,7 +111,8 @@ public: #ifndef NDEBUG void EmitVerboseDebuggingOutput(RegAlloc& reg_alloc); #endif - virtual void EmitTerminal(IR::Terminal terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0; + virtual bool EmitLeafTerminal(IR::Term::LeafTerminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0; + virtual bool EmitTerminal(IR::Term::Terminal const& terminal, IR::LocationDescriptor initial_location, bool is_single_step) noexcept = 0; // Patching struct PatchInformation { diff --git a/src/dynarmic/src/dynarmic/ir/basic_block.cpp b/src/dynarmic/src/dynarmic/ir/basic_block.cpp index 284f115328..4a7b2bf261 100644 --- a/src/dynarmic/src/dynarmic/ir/basic_block.cpp +++ b/src/dynarmic/src/dynarmic/ir/basic_block.cpp @@ -66,43 +66,44 @@ void Block::Reset(LocationDescriptor location_) noexcept { location = location_; end_location = location_; cond = Cond::AL; - terminal = Term::Invalid{}; + terminal = std::monostate{}; cond_failed_cycle_count = 0; cycle_count = 0; ASSERT(instructions.size() == 0); } -static std::string TerminalToString(const Terminal& terminal_variant) noexcept { - struct : boost::static_visitor { - std::string operator()(const Term::Invalid&) const { - return ""; - } - std::string operator()(const Term::ReturnToDispatch&) const { - return "ReturnToDispatch{}"; - } - std::string operator()(const Term::LinkBlock& terminal) const { - return fmt::format("LinkBlock{{{}}}", terminal.next); - } - std::string operator()(const Term::LinkBlockFast& terminal) const { - return fmt::format("LinkBlockFast{{{}}}", terminal.next); - } - std::string operator()(const Term::PopRSBHint&) const { - return "PopRSBHint{}"; - } - std::string operator()(const Term::FastDispatchHint&) const { - return "FastDispatchHint{}"; - } - std::string operator()(const Term::If& terminal) const { - return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_)); - } - std::string operator()(const Term::CheckBit& terminal) const { - return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_)); - } - std::string operator()(const Term::CheckHalt& terminal) const { - return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_)); - } - } visitor; - return boost::apply_visitor(visitor, terminal_variant); +static std::string TerminalToString(const Term::Terminal& terminal_variant) noexcept { + // struct : boost::static_visitor { + // std::string operator()(const std::monostate&) const { + // return ""; + // } + // std::string operator()(const Term::ReturnToDispatch&) const { + // return "ReturnToDispatch{}"; + // } + // std::string operator()(const Term::LinkBlock& terminal) const { + // return fmt::format("LinkBlock{{{}}}", terminal.next); + // } + // std::string operator()(const Term::LinkBlockFast& terminal) const { + // return fmt::format("LinkBlockFast{{{}}}", terminal.next); + // } + // std::string operator()(const Term::PopRSBHint&) const { + // return "PopRSBHint{}"; + // } + // std::string operator()(const Term::FastDispatchHint&) const { + // return "FastDispatchHint{}"; + // } + // std::string operator()(const Term::If& terminal) const { + // return fmt::format("If{{{}, {}, {}}}", A64::CondToString(terminal.if_), TerminalToString(terminal.then_), TerminalToString(terminal.else_)); + // } + // std::string operator()(const Term::CheckBit& terminal) const { + // return fmt::format("CheckBit{{{}, {}}}", TerminalToString(terminal.then_), TerminalToString(terminal.else_)); + // } + // std::string operator()(const Term::CheckHalt& terminal) const { + // return fmt::format("CheckHalt{{{}}}", TerminalToString(terminal.else_)); + // } + // } visitor; + // return boost::apply_visitor(visitor, terminal_variant); + return ""; } std::string DumpBlock(const IR::Block& block) noexcept { diff --git a/src/dynarmic/src/dynarmic/ir/basic_block.h b/src/dynarmic/src/dynarmic/ir/basic_block.h index 1518903d2a..37db8745a4 100644 --- a/src/dynarmic/src/dynarmic/ir/basic_block.h +++ b/src/dynarmic/src/dynarmic/ir/basic_block.h @@ -114,22 +114,22 @@ public: } /// Gets the terminal instruction for this basic block. - inline Terminal GetTerminal() const noexcept { + inline Term::Terminal GetTerminal() const noexcept { return terminal; } /// Sets the terminal instruction for this basic block. - inline void SetTerminal(Terminal term) noexcept { + inline void SetTerminal(Term::Terminal term) noexcept { ASSERT(!HasTerminal() && "Terminal has already been set."); terminal = std::move(term); } /// Replaces the terminal instruction for this basic block. - inline void ReplaceTerminal(Terminal term) noexcept { + inline void ReplaceTerminal(Term::Terminal term) noexcept { ASSERT(HasTerminal() && "Terminal has not been set."); terminal = std::move(term); } /// Determines whether or not this basic block has a terminal instruction. inline bool HasTerminal() const noexcept { - return terminal.which() != 0; + return !std::holds_alternative(terminal); } /// Gets a mutable reference to the cycle count for this basic block. @@ -156,7 +156,7 @@ public: /// Conditional to pass in order to execute this block Cond cond = Cond::AL; /// Terminal instruction of this block. - Terminal terminal = Term::Invalid{}; + Term::Terminal terminal = std::monostate{}; /// Number of cycles this block takes to execute if the conditional fails. size_t cond_failed_cycle_count = 0; /// Number of cycles this block takes to execute. diff --git a/src/dynarmic/src/dynarmic/ir/ir_emitter.h b/src/dynarmic/src/dynarmic/ir/ir_emitter.h index c035af2708..23ec707ee9 100644 --- a/src/dynarmic/src/dynarmic/ir/ir_emitter.h +++ b/src/dynarmic/src/dynarmic/ir/ir_emitter.h @@ -2943,7 +2943,7 @@ public: Inst(Opcode::CallHostFunction, Imm64(std::bit_cast(fn)), arg1, arg2, arg3); } - void SetTerm(const Terminal& terminal) { + void SetTerm(const Term::Terminal& terminal) { block.SetTerminal(terminal); } diff --git a/src/dynarmic/src/dynarmic/ir/terminal.h b/src/dynarmic/src/dynarmic/ir/terminal.h index 9b91b27382..b8de480939 100644 --- a/src/dynarmic/src/dynarmic/ir/terminal.h +++ b/src/dynarmic/src/dynarmic/ir/terminal.h @@ -8,7 +8,7 @@ #pragma once -#include +#include #include "common/common_types.h" #include "dynarmic/ir/cond.h" @@ -17,106 +17,89 @@ namespace Dynarmic::IR { namespace Term { -struct Invalid {}; - -/** - * This terminal instruction returns control to the dispatcher. - * The dispatcher will use the current cpu state to determine what comes next. - */ +/// This terminal instruction returns control to the dispatcher. +/// The dispatcher will use the current cpu state to determine what comes next. struct ReturnToDispatch {}; -/** - * This terminal instruction jumps to the basic block described by `next` if we have enough - * cycles remaining. If we do not have enough cycles remaining, we return to the - * dispatcher, which will return control to the host. - */ +/// This terminal instruction jumps to the basic block described by `next` if we have enough +/// cycles remaining. If we do not have enough cycles remaining, we return to the +/// dispatcher, which will return control to the host. struct LinkBlock { - explicit LinkBlock(const LocationDescriptor& next_) - : next(next_) {} + explicit LinkBlock(const LocationDescriptor& next_) : next(next_) {} LocationDescriptor next; ///< Location descriptor for next block. }; -/** - * This terminal instruction jumps to the basic block described by `next` unconditionally. - * This is an optimization and MUST only be emitted when this is guaranteed not to result - * in hanging, even in the face of other optimizations. (In practice, this means that only - * forward jumps to short-ish blocks would use this instruction.) - * A backend that doesn't support this optimization may choose to implement this exactly - * as LinkBlock. - */ +/// This terminal instruction jumps to the basic block described by `next` unconditionally. +/// This is an optimization and MUST only be emitted when this is guaranteed not to result +/// in hanging, even in the face of other optimizations. (In practice, this means that only +/// forward jumps to short-ish blocks would use this instruction.) +/// A backend that doesn't support this optimization may choose to implement this exactly +/// as LinkBlock. struct LinkBlockFast { - explicit LinkBlockFast(const LocationDescriptor& next_) - : next(next_) {} + explicit LinkBlockFast(const LocationDescriptor& next_) : next(next_) {} LocationDescriptor next; ///< Location descriptor for next block. }; -/** - * This terminal instruction checks the top of the Return Stack Buffer against the current - * location descriptor. If RSB lookup fails, control is returned to the dispatcher. - * This is an optimization for faster function calls. A backend that doesn't support - * this optimization or doesn't have a RSB may choose to implement this exactly as - * ReturnToDispatch. - */ +/// This terminal instruction checks the top of the Return Stack Buffer against the current +/// location descriptor. If RSB lookup fails, control is returned to the dispatcher. +/// This is an optimization for faster function calls. A backend that doesn't support +/// this optimization or doesn't have a RSB may choose to implement this exactly as +/// ReturnToDispatch. struct PopRSBHint {}; -/** - * This terminal instruction performs a lookup of the current location descriptor in the - * fast dispatch lookup table. A backend that doesn't support this optimization may choose - * to implement this exactly as ReturnToDispatch. - */ +/// This terminal instruction performs a lookup of the current location descriptor in the +/// fast dispatch lookup table. A backend that doesn't support this optimization may choose +/// to implement this exactly as ReturnToDispatch. struct FastDispatchHint {}; struct If; struct CheckBit; struct CheckHalt; -/// A Terminal is the terminal instruction in a MicroBlock. -using Terminal = boost::variant< - Invalid, + +/// Non recursive kind of terminal +using LeafTerminal = std::variant< + std::monostate, ReturnToDispatch, LinkBlock, LinkBlockFast, PopRSBHint, - FastDispatchHint, - boost::recursive_wrapper, - boost::recursive_wrapper, - boost::recursive_wrapper>; - -/** - * This terminal instruction conditionally executes one terminal or another depending - * on the run-time state of the ARM flags. - */ + FastDispatchHint +>; + +/// A Terminal is the terminal instruction in a MicroBlock. +using Terminal = std::variant< + std::monostate, + LeafTerminal, + If, + CheckBit, + CheckHalt +>; + +/// This terminal instruction conditionally executes one terminal or another depending +/// on the run-time state of the ARM flags. struct If { - If(Cond if_, Terminal then_, Terminal else_) - : if_(if_), then_(std::move(then_)), else_(std::move(else_)) {} + explicit If(Cond if_, LeafTerminal then_, LeafTerminal else_) : if_(if_), then_(std::move(then_)), else_(std::move(else_)) {} Cond if_; - Terminal then_; - Terminal else_; + LeafTerminal then_; + LeafTerminal else_; }; -/** - * This terminal instruction conditionally executes one terminal or another depending - * on the run-time state of the check bit. - * then_ is executed if the check bit is non-zero, otherwise else_ is executed. - */ +/// This terminal instruction conditionally executes one terminal or another depending +/// on the run-time state of the check bit. +/// then_ is executed if the check bit is non-zero, otherwise else_ is executed. struct CheckBit { - CheckBit(Terminal then_, Terminal else_) - : then_(std::move(then_)), else_(std::move(else_)) {} - Terminal then_; - Terminal else_; + explicit CheckBit(LeafTerminal then_, LeafTerminal else_) : then_(std::move(then_)), else_(std::move(else_)) {} + LeafTerminal then_; + LeafTerminal else_; }; -/** - * This terminal instruction checks if a halt was requested. If it wasn't, else_ is - * executed. - */ +/// This terminal instruction checks if a halt was requested. If it wasn't, else_ is +/// executed. struct CheckHalt { - explicit CheckHalt(Terminal else_) - : else_(std::move(else_)) {} - Terminal else_; + explicit CheckHalt(LeafTerminal else_) : else_(std::move(else_)) {} + LeafTerminal else_; }; } // namespace Term -using Term::Terminal; - } // namespace Dynarmic::IR diff --git a/src/dynarmic/tests/A32/fuzz_arm.cpp b/src/dynarmic/tests/A32/fuzz_arm.cpp index 922f1e4f26..e8f3011799 100644 --- a/src/dynarmic/tests/A32/fuzz_arm.cpp +++ b/src/dynarmic/tests/A32/fuzz_arm.cpp @@ -43,32 +43,20 @@ namespace { using namespace Dynarmic; template -bool AnyLocationDescriptorForTerminalHas(IR::Terminal terminal, Fn fn) { - return boost::apply_visitor([&](auto t) -> bool { - using T = std::decay_t; - if constexpr (std::is_same_v) { - return false; - } else if constexpr (std::is_same_v) { - return false; - } else if constexpr (std::is_same_v) { - return fn(t.next); - } else if constexpr (std::is_same_v) { - return fn(t.next); - } else if constexpr (std::is_same_v) { - return false; - } else if constexpr (std::is_same_v) { - return false; - } else if constexpr (std::is_same_v) { - return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn); - } else if constexpr (std::is_same_v) { - return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn); - } else if constexpr (std::is_same_v) { - return AnyLocationDescriptorForTerminalHas(t.else_, fn); - } else { - ASSERT(false && "Invalid terminal type"); - return false; - } - }, terminal); +bool AnyLocationDescriptorForTerminalHas(IR::Term::Terminal terminal, Fn fn) { + if (auto const e = std::get_if(&terminal)) { + if (auto const x = std::get_if(e)) + return fn(x->next); + if (auto const x = std::get_if(e)) + return fn(x->next); + } + if (auto const x = std::get_if(&terminal)) + return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn); + if (auto const x = std::get_if(&terminal)) + return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn); + if (auto const x = std::get_if(&terminal)) + return AnyLocationDescriptorForTerminalHas(x->else_, fn); + return false; } bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst, A32::ITState it_state = {}) {