From f22fd1714ba40f1444a79a629dbe53e0744c85cd Mon Sep 17 00:00:00 2001 From: lizzie Date: Tue, 7 Jul 2026 19:53:13 +0200 Subject: [PATCH] [dynarmic] use LinkBlockFast for branches which are guarnateed to be unconditional (or conditionally unconditional) (#4177) CBZ is basically ```c if (thing) { jump(A); } else { jump(B); } ``` those jumps are locally unconditional so we can add the LinkBlockFast without much repercussions for a small win in JIT opts Signed-off-by: lizzie Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4177 Reviewed-by: MaranBr Reviewed-by: CamilleLaVey --- docs/dynarmic/Design.md | 5 ++- .../A32/translate/impl/a32_branch.cpp | 12 ++++-- .../translate/impl/status_register_access.cpp | 5 +-- .../frontend/A32/translate/impl/thumb16.cpp | 21 ++++++----- .../A32/translate/impl/thumb32_branch.cpp | 37 +++++++++---------- .../A64/translate/impl/a64_branch.cpp | 24 ++++++------ 6 files changed, 54 insertions(+), 50 deletions(-) diff --git a/docs/dynarmic/Design.md b/docs/dynarmic/Design.md index 020cfd65bd..b46e7e5348 100644 --- a/docs/dynarmic/Design.md +++ b/docs/dynarmic/Design.md @@ -311,8 +311,9 @@ dispatcher, which will return control to the host. SetTerm(IR::Term::LinkBlockFast{next}) ``` -This terminal instruction jumps to the basic block described by `next` unconditionally. -This promises guarantees that must be held at runtime - i.e that the program wont hang, +This terminal instruction jumps to the basic block described by `next` unconditionally. This promises guarantees that must be held at runtime - i.e that the program wont hang, + +This is a valid expression if contained within another block (say a `Terminal::If`) and the semantics allow it to be so. ### Terminal: PopRSBHint diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_branch.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_branch.cpp index 63b40b8c31..adef1adbec 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_branch.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/a32_branch.cpp @@ -17,10 +17,14 @@ bool TranslatorVisitor::arm_B(Cond cond, Imm<24> imm24) { if (!ArmConditionPassed(cond)) { return true; } - const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8; const auto new_location = ir.current_location.AdvancePC(imm32); - ir.SetTerm(IR::Term::LinkBlock{new_location}); + // pattern match => b . + if (imm32 == 0) { + ir.SetTerm(IR::Term::LinkBlock{new_location}); + } else { + ir.SetTerm(IR::Term::LinkBlockFast{new_location}); + } return false; } @@ -35,7 +39,7 @@ bool TranslatorVisitor::arm_BL(Cond cond, Imm<24> imm24) { const u32 imm32 = mcl::bit::sign_extend<26, u32>(imm24.ZeroExtend() << 2) + 8; const auto new_location = ir.current_location.AdvancePC(imm32); - ir.SetTerm(IR::Term::LinkBlock{new_location}); + ir.SetTerm(IR::Term::LinkBlockFast{new_location}); return false; } @@ -46,7 +50,7 @@ bool TranslatorVisitor::arm_BLX_imm(bool H, Imm<24> imm24) { const u32 imm32 = mcl::bit::sign_extend<26, u32>((imm24.ZeroExtend() << 2)) + (H ? 2 : 0) + 8; const auto new_location = ir.current_location.AdvancePC(imm32).SetTFlag(true); - ir.SetTerm(IR::Term::LinkBlock{new_location}); + ir.SetTerm(IR::Term::LinkBlockFast{new_location}); return false; } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/status_register_access.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/status_register_access.cpp index 7a0640598c..bd52d3d4a9 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/status_register_access.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/status_register_access.cpp @@ -56,11 +56,10 @@ bool TranslatorVisitor::arm_MSR_imm(Cond cond, unsigned mask, int rotate, Imm<8> if (write_e) { const bool E = (imm32 & 0x00000200) != 0; if (E != ir.current_location.EFlag()) { - ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)}); + ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).SetEFlag(E)}); return false; } } - return true; } @@ -112,7 +111,7 @@ bool TranslatorVisitor::arm_RFE() { // SETEND bool TranslatorVisitor::arm_SETEND(bool E) { - ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(4).SetEFlag(E)}); + ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).SetEFlag(E)}); return false; } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp index 082cd30db2..e7bb36f285 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb16.cpp @@ -803,7 +803,7 @@ bool TranslatorVisitor::thumb16_SETEND(bool E) { return true; } - ir.SetTerm(IR::Term::LinkBlock{ir.current_location.AdvancePC(2).SetEFlag(E).AdvanceIT()}); + ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).SetEFlag(E).AdvanceIT()}); return false; } @@ -906,9 +906,8 @@ bool TranslatorVisitor::thumb16_CBZ_CBNZ(bool nonzero, Imm<1> i, Imm<5> imm5, Re ir.SetCheckBit(ir.IsZero(rn)); const auto [cond_pass, cond_fail] = [this, imm, nonzero] { - const auto skip = IR::Term::LinkBlock{ir.current_location.AdvancePC(2).AdvanceIT()}; - const auto branch = IR::Term::LinkBlock{ir.current_location.AdvancePC(imm + 4).AdvanceIT()}; - + const auto skip = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).AdvanceIT()}; + const auto branch = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm + 4).AdvanceIT()}; if (nonzero) { return std::make_pair(skip, branch); } else { @@ -975,10 +974,10 @@ bool TranslatorVisitor::thumb16_B_t1(Cond cond, Imm<8> imm8) { } const s32 imm32 = static_cast((imm8.SignExtend() << 1) + 4); - const auto then_location = ir.current_location.AdvancePC(imm32).AdvanceIT(); - const auto else_location = ir.current_location.AdvancePC(2).AdvanceIT(); + const auto then_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm32).AdvanceIT()}; + const auto else_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(2).AdvanceIT()}; - ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}}); + ir.SetTerm(IR::Term::If{cond, then_, else_}); return false; } @@ -990,8 +989,12 @@ bool TranslatorVisitor::thumb16_B_t2(Imm<11> imm11) { const s32 imm32 = static_cast((imm11.SignExtend() << 1) + 4); const auto next_location = ir.current_location.AdvancePC(imm32).AdvanceIT(); - - ir.SetTerm(IR::Term::LinkBlock{next_location}); + // pattern (b +#0) + if (imm32 == 0) { + ir.SetTerm(IR::Term::LinkBlock{next_location}); + } else { + ir.SetTerm(IR::Term::LinkBlockFast{next_location}); + } return false; } diff --git a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_branch.cpp b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_branch.cpp index 83734f5c70..4b19449872 100644 --- a/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_branch.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A32/translate/impl/thumb32_branch.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + /* This file is part of the dynarmic project. * Copyright (c) 2016 MerryMage * SPDX-License-Identifier: 0BSD @@ -20,10 +23,8 @@ bool TranslatorVisitor::thumb32_BL_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1)); const s32 imm32 = static_cast((concatenate(S, i1, i2, hi, lo).SignExtend() << 1) + 4); - const auto new_location = ir.current_location - .AdvancePC(imm32) - .AdvanceIT(); - ir.SetTerm(IR::Term::LinkBlock{new_location}); + auto const new_location = ir.current_location.AdvancePC(imm32).AdvanceIT(); + ir.SetTerm(IR::Term::LinkBlockFast{new_location}); return false; } @@ -44,11 +45,8 @@ bool TranslatorVisitor::thumb32_BLX_imm(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> ir.SetRegister(Reg::LR, ir.Imm32((ir.current_location.PC() + 4) | 1)); const s32 imm32 = static_cast(concatenate(S, i1, i2, hi, lo).SignExtend() << 1); - const auto new_location = ir.current_location - .SetPC(ir.AlignPC(4) + imm32) - .SetTFlag(false) - .AdvanceIT(); - ir.SetTerm(IR::Term::LinkBlock{new_location}); + const auto new_location = ir.current_location.SetPC(ir.AlignPC(4) + imm32).SetTFlag(false).AdvanceIT(); + ir.SetTerm(IR::Term::LinkBlockFast{new_location}); return false; } @@ -61,10 +59,13 @@ bool TranslatorVisitor::thumb32_B(Imm<1> S, Imm<10> hi, Imm<1> j1, Imm<1> j2, Im } const s32 imm32 = static_cast((concatenate(S, i1, i2, hi, lo).SignExtend() << 1) + 4); - const auto new_location = ir.current_location - .AdvancePC(imm32) - .AdvanceIT(); - ir.SetTerm(IR::Term::LinkBlock{new_location}); + auto const new_location = ir.current_location.AdvancePC(imm32).AdvanceIT(); + // Pattern to halt execution (b +#0) + if (imm32 == 0) { + ir.SetTerm(IR::Term::LinkBlock{new_location}); + } else { + ir.SetTerm(IR::Term::LinkBlockFast{new_location}); + } return false; } @@ -75,13 +76,9 @@ bool TranslatorVisitor::thumb32_B_cond(Imm<1> S, Cond cond, Imm<6> hi, Imm<1> i1 // Note: i1 and i2 were not inverted from encoding and are opposite compared to the other B instructions. const s32 imm32 = static_cast((concatenate(S, i2, i1, hi, lo).SignExtend() << 1) + 4); - const auto then_location = ir.current_location - .AdvancePC(imm32) - .AdvanceIT(); - const auto else_location = ir.current_location - .AdvancePC(4) - .AdvanceIT(); - ir.SetTerm(IR::Term::If{cond, IR::Term::LinkBlock{then_location}, IR::Term::LinkBlock{else_location}}); + const auto then_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(imm32).AdvanceIT()}; + const auto else_ = IR::Term::LinkBlockFast{ir.current_location.AdvancePC(4).AdvanceIT()}; + ir.SetTerm(IR::Term::If{cond, then_, else_}); return false; } diff --git a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/a64_branch.cpp b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/a64_branch.cpp index 36b91f13e9..a07160394c 100644 --- a/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/a64_branch.cpp +++ b/src/dynarmic/src/dynarmic/frontend/A64/translate/impl/a64_branch.cpp @@ -14,8 +14,8 @@ bool TranslatorVisitor::B_cond(Imm<19> imm19, Cond cond) { const s64 offset = concatenate(imm19, Imm<2>{0}).SignExtend(); const u64 target = ir.PC() + offset; - const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)}; - const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)}; + const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}; + const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)}; ir.SetTerm(IR::Term::If{cond, cond_pass, cond_fail}); return false; } @@ -26,9 +26,9 @@ bool TranslatorVisitor::B_uncond(Imm<26> imm26) { // Pattern to halt execution (B .) if (target == ir.PC()) { ir.SetTerm(IR::Term::LinkBlock{ir.current_location->SetPC(target)}); - return false; + } else { + ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}); } - ir.SetTerm(IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}); return false; } @@ -79,8 +79,8 @@ bool TranslatorVisitor::CBZ(bool sf, Imm<19> imm19, Reg Rt) { ir.SetCheckBit(ir.IsZero(operand1)); const u64 target = ir.PC() + offset; - const auto cond_pass = IR::Term::LinkBlock{ir.current_location->SetPC(target)}; - const auto cond_fail = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)}; + const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}; + const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)}; ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail}); return false; } @@ -94,8 +94,8 @@ bool TranslatorVisitor::CBNZ(bool sf, Imm<19> imm19, Reg Rt) { ir.SetCheckBit(ir.IsZero(operand1)); const u64 target = ir.PC() + offset; - const auto cond_pass = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)}; - const auto cond_fail = IR::Term::LinkBlock{ir.current_location->SetPC(target)}; + const auto cond_pass = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)}; + const auto cond_fail = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}; ir.SetTerm(IR::Term::CheckBit{cond_pass, cond_fail}); return false; } @@ -110,8 +110,8 @@ bool TranslatorVisitor::TBZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos))); const u64 target = ir.PC() + offset; - const auto cond_1 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)}; - const auto cond_0 = IR::Term::LinkBlock{ir.current_location->SetPC(target)}; + const auto cond_1 = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)}; + const auto cond_0 = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}; ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0}); return false; } @@ -126,8 +126,8 @@ bool TranslatorVisitor::TBNZ(Imm<1> b5, Imm<5> b40, Imm<14> imm14, Reg Rt) { ir.SetCheckBit(ir.TestBit(operand, ir.Imm8(bit_pos))); const u64 target = ir.PC() + offset; - const auto cond_1 = IR::Term::LinkBlock{ir.current_location->SetPC(target)}; - const auto cond_0 = IR::Term::LinkBlock{ir.current_location->AdvancePC(4)}; + const auto cond_1 = IR::Term::LinkBlockFast{ir.current_location->SetPC(target)}; + const auto cond_0 = IR::Term::LinkBlockFast{ir.current_location->AdvancePC(4)}; ir.SetTerm(IR::Term::CheckBit{cond_1, cond_0}); return false; }