aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlizzie <lizzie@eden-emu.dev>2026-07-25 21:43:58 +0200
committercrueter <crueter@eden-emu.dev>2026-07-25 21:43:58 +0200
commit7b13113fbec7e3d37d064f1d30fb72a8a65467cc (patch)
tree870b65842c70240857f560ca3e75bb90df378d96
parentb9a88297cb33785e40f33b39002d45ca2bcd6770 (diff)
[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 <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4218 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: Maufeat <sahyno1996@gmail.com>
-rw-r--r--src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a32.cpp39
-rw-r--r--src/dynarmic/src/dynarmic/backend/arm64/emit_arm64_a64.cpp39
-rw-r--r--src/dynarmic/src/dynarmic/backend/riscv64/emit_riscv64_a32.cpp35
-rw-r--r--src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.cpp67
-rw-r--r--src/dynarmic/src/dynarmic/backend/x64/a32_emit_x64.h3
-rw-r--r--src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.cpp68
-rw-r--r--src/dynarmic/src/dynarmic/backend/x64/a64_emit_x64.h3
-rw-r--r--src/dynarmic/src/dynarmic/backend/x64/emit_x64.h3
-rw-r--r--src/dynarmic/src/dynarmic/ir/basic_block.cpp65
-rw-r--r--src/dynarmic/src/dynarmic/ir/basic_block.h10
-rw-r--r--src/dynarmic/src/dynarmic/ir/ir_emitter.h2
-rw-r--r--src/dynarmic/src/dynarmic/ir/terminal.h123
-rw-r--r--src/dynarmic/tests/A32/fuzz_arm.cpp40
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<IR::Term::ReturnToDispatch>(&terminal))
+ return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
+ return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
+ return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
+ return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&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<IR::Term::LeafTerminal>(&terminal))
+ return EmitA32LeafTerminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::If>(&terminal))
+ return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
+ return EmitA32Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckHalt>(&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<IR::Term::ReturnToDispatch>(&terminal))
+ return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
+ return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
+ return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
+ return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&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<IR::Term::LeafTerminal>(&terminal))
+ return EmitA64LeafTerminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::If>(&terminal))
+ return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
+ return EmitA64Terminal(code, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckHalt>(&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<IR::Term::ReturnToDispatch>(&terminal))
+ return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
+ return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
+ return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
+ return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&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<IR::Term::LeafTerminal>(&terminal))
+ return EmitA32LeafTerminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::If>(&terminal))
+ return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
+ return EmitA32Terminal(as, ctx, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckHalt>(&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<IR::Term::ReturnToDispatch>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&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<IR::Term::LeafTerminal>(&terminal))
+ return EmitLeafTerminal(*e, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::If>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckHalt>(&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 <fmt/ostream.h>
#include "common/assert.h"
#include "common/common_types.h"
+#include "dynarmic/ir/terminal.h"
#include "dynarmic/mcl/integer_of_size.hpp"
#include <boost/container/static_vector.hpp>
@@ -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<IR::Term::ReturnToDispatch>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlock>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::LinkBlockFast>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::PopRSBHint>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::FastDispatchHint>(&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<IR::Term::LeafTerminal>(&terminal))
+ return EmitLeafTerminal(*x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::If>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
+ return EmitTerminalImpl(*this, *x, initial_location, is_single_step);
+ if (auto const x = std::get_if<IR::Term::CheckHalt>(&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> {
- std::string operator()(const Term::Invalid&) const {
- return "<invalid terminal>";
- }
- 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> {
+ // std::string operator()(const std::monostate&) const {
+ // return "<invalid>";
+ // }
+ // 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<std::monostate>(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<u64>(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 <boost/variant.hpp>
+#include <variant>
#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<If>,
- boost::recursive_wrapper<CheckBit>,
- boost::recursive_wrapper<CheckHalt>>;
-
-/**
- * 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<typename Fn>
-bool AnyLocationDescriptorForTerminalHas(IR::Terminal terminal, Fn fn) {
- return boost::apply_visitor([&](auto t) -> bool {
- using T = std::decay_t<decltype(t)>;
- if constexpr (std::is_same_v<T, IR::Term::Invalid>) {
- return false;
- } else if constexpr (std::is_same_v<T, IR::Term::ReturnToDispatch>) {
- return false;
- } else if constexpr (std::is_same_v<T, IR::Term::LinkBlock>) {
- return fn(t.next);
- } else if constexpr (std::is_same_v<T, IR::Term::LinkBlockFast>) {
- return fn(t.next);
- } else if constexpr (std::is_same_v<T, IR::Term::PopRSBHint>) {
- return false;
- } else if constexpr (std::is_same_v<T, IR::Term::FastDispatchHint>) {
- return false;
- } else if constexpr (std::is_same_v<T, IR::Term::If>) {
- return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
- } else if constexpr (std::is_same_v<T, IR::Term::CheckBit>) {
- return AnyLocationDescriptorForTerminalHas(t.then_, fn) || AnyLocationDescriptorForTerminalHas(t.else_, fn);
- } else if constexpr (std::is_same_v<T, IR::Term::CheckHalt>) {
- 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<IR::Term::LeafTerminal>(&terminal)) {
+ if (auto const x = std::get_if<IR::Term::LinkBlock>(e))
+ return fn(x->next);
+ if (auto const x = std::get_if<IR::Term::LinkBlockFast>(e))
+ return fn(x->next);
+ }
+ if (auto const x = std::get_if<IR::Term::If>(&terminal))
+ return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
+ if (auto const x = std::get_if<IR::Term::CheckBit>(&terminal))
+ return AnyLocationDescriptorForTerminalHas(x->then_, fn) || AnyLocationDescriptorForTerminalHas(x->else_, fn);
+ if (auto const x = std::get_if<IR::Term::CheckHalt>(&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 = {}) {