From e0c83e1575175677f2de49d6b64df6f565b302f0 Mon Sep 17 00:00:00 2001 From: wildcard Date: Sat, 20 Sep 2025 21:19:58 +0200 Subject: [PATCH 1/3] [Shader Recompiler] Optimize SSA rewrite using better data structures. Optimize SSA rewrite using, Better data structures. Tail End optimization, Change from std:visit with direct switch case. Better hash function. --- .../ir_opt/ssa_rewrite_pass.cpp | 96 ++++++++++++++++--- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index 07cabca43e..b506550c8a 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp @@ -50,8 +50,55 @@ struct IndirectBranchVariable { auto operator<=>(const IndirectBranchVariable&) const noexcept = default; }; -using Variant = std::variant; + +enum class VarTag : uint8_t { Reg, Pred, Z, S, C, O, Goto, Indirect }; + +struct VariableKey { + uint32_t tag_index; // [31:24]=tag, [23:0]=index + bool operator==(const VariableKey&) const = default; +}; + +struct VariableKeyHash { + size_t operator()(VariableKey k) const noexcept { + uint64_t x = k.tag_index * 0x9E3779B185EBCA87ull; + x ^= (x >> 33); x *= 0xC2B2AE3D27D4EB4Full; x ^= (x >> 29); + return static_cast(x); + } +}; + +constexpr uint32_t PackKey(VarTag tag, uint32_t idx = 0) { + return (static_cast(tag) << 24) | (idx & 0x00FFFFFFu); +} + +inline VariableKey KeyOf(IR::Reg r){ + return {PackKey(VarTag::Reg, static_cast(r))}; +} + +inline VariableKey KeyOf(IR::Pred p){ + return {PackKey(VarTag::Pred, static_cast(IR::PredIndex(p)))}; +} + +inline VariableKey KeyOf(ZeroFlagTag){ + return {PackKey(VarTag::Z)}; +} + +inline VariableKey KeyOf(SignFlagTag){ + return {PackKey(VarTag::S)}; +} +inline VariableKey KeyOf(CarryFlagTag){ + return {PackKey(VarTag::C)}; +} +inline VariableKey KeyOf(OverflowFlagTag){ + return {PackKey(VarTag::O)}; +} +inline VariableKey KeyOf(GotoVariable g){ + return {PackKey(VarTag::Goto, g.index)}; +} +inline VariableKey KeyOf(IndirectBranchVariable){ + return {PackKey(VarTag::Indirect)}; +} + +using IncompleteMap = std::unordered_map; using ValueMap = std::unordered_map; struct DefTable { @@ -124,6 +171,10 @@ IR::Opcode UndefOpcode(IR::Reg) noexcept { return IR::Opcode::UndefU32; } +IR::Opcode UndefOpcode(GotoVariable) noexcept { + return IR::Opcode::UndefU32; +} + IR::Opcode UndefOpcode(IR::Pred) noexcept { return IR::Opcode::UndefU1; } @@ -194,13 +245,14 @@ public: IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; phi->SetFlags(IR::TypeOf(UndefOpcode(variable))); - incomplete_phis[block].insert_or_assign(variable, phi); + incomplete_phis[block].insert_or_assign(KeyOf(variable), phi); + WriteVariable(variable, block, IR::Value{phi}); stack.back().result = IR::Value{&*phi}; } else if (const std::span imm_preds = block->ImmPredecessors(); - imm_preds.size() == 1) { - // Optimize the common case of one predecessor: no phi needed - stack.back().pc = Status::SetValue; - stack.emplace_back(imm_preds.front()); + imm_preds.size() == 1) { + // Tail-advance: reuse this frame + stack.back().block = imm_preds.front(); + stack.back().pc = Status::Start; break; } else { // Break potential cycles with operandless phi @@ -239,13 +291,29 @@ public: } void SealBlock(IR::Block* block) { - const auto it{incomplete_phis.find(block)}; - if (it != incomplete_phis.end()) { - for (auto& pair : it->second) { - auto& variant{pair.first}; - auto& phi{pair.second}; - std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant); + if (auto it = incomplete_phis.find(block); it != incomplete_phis.end()) { + for (auto& [key, phi] : it->second) { + switch (static_cast(key.tag_index >> 24)) { + case VarTag::Reg: + AddPhiOperands(IR::Reg(static_cast(key.tag_index & 0xFFFFFF)), *phi, block); + break; + case VarTag::Pred: + AddPhiOperands(static_cast(key.tag_index & 0xFFFFFF), *phi, block); + break; + case VarTag::Z: AddPhiOperands(ZeroFlagTag{}, *phi, block); break; + case VarTag::S: AddPhiOperands(SignFlagTag{}, *phi, block); break; + case VarTag::C: AddPhiOperands(CarryFlagTag{}, *phi, block); break; + case VarTag::O: AddPhiOperands(OverflowFlagTag{}, *phi, block); break; + case VarTag::Goto: + AddPhiOperands(GotoVariable{static_cast(key.tag_index & 0xFFFFFF)}, *phi, block); + break; + case VarTag::Indirect: + AddPhiOperands(IndirectBranchVariable{}, *phi, block); + break; + } } + it->second.clear(); + incomplete_phis.erase(it); } block->SsaSeal(); } @@ -295,7 +363,7 @@ private: return same; } - std::unordered_map> incomplete_phis; + std::unordered_map incomplete_phis; DefTable current_def; }; From f85753be8ddadebca40fcc9b3c05a44383140702 Mon Sep 17 00:00:00 2001 From: wildcard Date: Sat, 20 Sep 2025 21:21:43 +0200 Subject: [PATCH 2/3] Update src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp --- src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index b506550c8a..d068e3b484 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later From d7b8a72c980858327326680ec3925ad84a40fdde Mon Sep 17 00:00:00 2001 From: wildcard Date: Sun, 21 Sep 2025 10:51:43 +0200 Subject: [PATCH 3/3] Update src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp test-1 fix rendering bugs --- src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index d068e3b484..63a2a7c871 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp @@ -77,7 +77,7 @@ inline VariableKey KeyOf(IR::Reg r){ } inline VariableKey KeyOf(IR::Pred p){ - return {PackKey(VarTag::Pred, static_cast(IR::PredIndex(p)))}; + return {PackKey(VarTag::Pred, static_cast(p))}; } inline VariableKey KeyOf(ZeroFlagTag){ @@ -251,10 +251,9 @@ public: WriteVariable(variable, block, IR::Value{phi}); stack.back().result = IR::Value{&*phi}; } else if (const std::span imm_preds = block->ImmPredecessors(); - imm_preds.size() == 1) { - // Tail-advance: reuse this frame - stack.back().block = imm_preds.front(); - stack.back().pc = Status::Start; + imm_preds.size() == 1) { + stack.back().pc = Status::SetValue; + stack.emplace_back(imm_preds.front()); break; } else { // Break potential cycles with operandless phi