summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-02-02 21:07:00 -0300
committerGravatar ameerj2021-07-22 21:51:21 -0400
commit6c4cc0cd062fbbba5349da1108d3c23cb330ca8a (patch)
tree544291931da8a85fafcea71964c77d9278ec7f29 /src/shader_recompiler/frontend
parentshader: Initial recompiler work (diff)
downloadyuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.tar.gz
yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.tar.xz
yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.zip
shader: SSA and dominance
Diffstat (limited to 'src/shader_recompiler/frontend')
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.cpp51
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.h20
-rw-r--r--src/shader_recompiler/frontend/ir/function.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/function.h25
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp22
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.h10
-rw-r--r--src/shader_recompiler/frontend/ir/opcode.inc8
-rw-r--r--src/shader_recompiler/frontend/ir/pred.h7
-rw-r--r--src/shader_recompiler/frontend/ir/reg.h9
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp37
-rw-r--r--src/shader_recompiler/frontend/ir/value.h3
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp130
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h44
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.cpp75
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.h11
-rw-r--r--src/shader_recompiler/frontend/maxwell/termination_code.cpp7
-rw-r--r--src/shader_recompiler/frontend/maxwell/termination_code.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp6
19 files changed, 401 insertions, 74 deletions
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp
index 0406726ad..e795618fc 100644
--- a/src/shader_recompiler/frontend/ir/basic_block.cpp
+++ b/src/shader_recompiler/frontend/ir/basic_block.cpp
@@ -37,6 +37,10 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
37 return result_it; 37 return result_it;
38} 38}
39 39
40void Block::AddImmediatePredecessor(IR::Block* immediate_predecessor) {
41 imm_predecessors.push_back(immediate_predecessor);
42}
43
40u32 Block::LocationBegin() const noexcept { 44u32 Block::LocationBegin() const noexcept {
41 return location_begin; 45 return location_begin;
42} 46}
@@ -53,6 +57,18 @@ const Block::InstructionList& Block::Instructions() const noexcept {
53 return instructions; 57 return instructions;
54} 58}
55 59
60std::span<IR::Block* const> Block::ImmediatePredecessors() const noexcept {
61 return imm_predecessors;
62}
63
64static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index,
65 Block* block) {
66 if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) {
67 return fmt::format("{{Block ${}}}", it->second);
68 }
69 return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block));
70}
71
56static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index, 72static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index,
57 const std::map<const Inst*, size_t>& inst_to_index, 73 const std::map<const Inst*, size_t>& inst_to_index,
58 const Value& arg) { 74 const Value& arg) {
@@ -60,10 +76,7 @@ static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_ind
60 return "<null>"; 76 return "<null>";
61 } 77 }
62 if (arg.IsLabel()) { 78 if (arg.IsLabel()) {
63 if (const auto it{block_to_index.find(arg.Label())}; it != block_to_index.end()) { 79 return BlockToIndex(block_to_index, arg.Label());
64 return fmt::format("{{Block ${}}}", it->second);
65 }
66 return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(arg.Label()));
67 } 80 }
68 if (!arg.IsImmediate()) { 81 if (!arg.IsImmediate()) {
69 if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) { 82 if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) {
@@ -115,16 +128,26 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>&
115 } else { 128 } else {
116 ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces 129 ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
117 } 130 }
118 const size_t arg_count{NumArgsOf(op)}; 131 if (op == Opcode::Phi) {
119 for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { 132 size_t val_index{0};
120 const Value arg{inst.Arg(arg_index)}; 133 for (const auto& [phi_block, phi_val] : inst.PhiOperands()) {
121 ret += arg_index != 0 ? ", " : " "; 134 ret += val_index != 0 ? ", " : " ";
122 ret += ArgToIndex(block_to_index, inst_to_index, arg); 135 ret += fmt::format("[ {}, {} ]", ArgToIndex(block_to_index, inst_to_index, phi_val),
123 136 BlockToIndex(block_to_index, phi_block));
124 const Type actual_type{arg.Type()}; 137 ++val_index;
125 const Type expected_type{ArgTypeOf(op, arg_index)}; 138 }
126 if (!AreTypesCompatible(actual_type, expected_type)) { 139 } else {
127 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type); 140 const size_t arg_count{NumArgsOf(op)};
141 for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
142 const Value arg{inst.Arg(arg_index)};
143 ret += arg_index != 0 ? ", " : " ";
144 ret += ArgToIndex(block_to_index, inst_to_index, arg);
145
146 const Type actual_type{arg.Type()};
147 const Type expected_type{ArgTypeOf(op, arg_index)};
148 if (!AreTypesCompatible(actual_type, expected_type)) {
149 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
150 }
128 } 151 }
129 } 152 }
130 if (TypeOf(op) != Type::Void) { 153 if (TypeOf(op) != Type::Void) {
diff --git a/src/shader_recompiler/frontend/ir/basic_block.h b/src/shader_recompiler/frontend/ir/basic_block.h
index 3ed2eb957..4b6b80c4b 100644
--- a/src/shader_recompiler/frontend/ir/basic_block.h
+++ b/src/shader_recompiler/frontend/ir/basic_block.h
@@ -6,6 +6,8 @@
6 6
7#include <initializer_list> 7#include <initializer_list>
8#include <map> 8#include <map>
9#include <span>
10#include <vector>
9 11
10#include <boost/intrusive/list.hpp> 12#include <boost/intrusive/list.hpp>
11#include <boost/pool/pool_alloc.hpp> 13#include <boost/pool/pool_alloc.hpp>
@@ -36,7 +38,11 @@ public:
36 void AppendNewInst(Opcode op, std::initializer_list<Value> args); 38 void AppendNewInst(Opcode op, std::initializer_list<Value> args);
37 39
38 /// Prepends a new instruction to this basic block before the insertion point. 40 /// Prepends a new instruction to this basic block before the insertion point.
39 iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args); 41 iterator PrependNewInst(iterator insertion_point, Opcode op,
42 std::initializer_list<Value> args = {});
43
44 /// Adds a new immediate predecessor to the basic block.
45 void AddImmediatePredecessor(IR::Block* immediate_predecessor);
40 46
41 /// Gets the starting location of this basic block. 47 /// Gets the starting location of this basic block.
42 [[nodiscard]] u32 LocationBegin() const noexcept; 48 [[nodiscard]] u32 LocationBegin() const noexcept;
@@ -44,9 +50,12 @@ public:
44 [[nodiscard]] u32 LocationEnd() const noexcept; 50 [[nodiscard]] u32 LocationEnd() const noexcept;
45 51
46 /// Gets a mutable reference to the instruction list for this basic block. 52 /// Gets a mutable reference to the instruction list for this basic block.
47 InstructionList& Instructions() noexcept; 53 [[nodiscard]] InstructionList& Instructions() noexcept;
48 /// Gets an immutable reference to the instruction list for this basic block. 54 /// Gets an immutable reference to the instruction list for this basic block.
49 const InstructionList& Instructions() const noexcept; 55 [[nodiscard]] const InstructionList& Instructions() const noexcept;
56
57 /// Gets an immutable span to the immediate predecessors.
58 [[nodiscard]] std::span<IR::Block* const> ImmediatePredecessors() const noexcept;
50 59
51 [[nodiscard]] bool empty() const { 60 [[nodiscard]] bool empty() const {
52 return instructions.empty(); 61 return instructions.empty();
@@ -115,13 +124,16 @@ private:
115 /// End location of this block 124 /// End location of this block
116 u32 location_end; 125 u32 location_end;
117 126
118 /// List of instructions in this block. 127 /// List of instructions in this block
119 InstructionList instructions; 128 InstructionList instructions;
120 129
121 /// Memory pool for instruction list 130 /// Memory pool for instruction list
122 boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free, 131 boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free,
123 boost::details::pool::null_mutex> 132 boost::details::pool::null_mutex>
124 instruction_alloc_pool; 133 instruction_alloc_pool;
134
135 /// Block immediate predecessors
136 std::vector<IR::Block*> imm_predecessors;
125}; 137};
126 138
127[[nodiscard]] std::string DumpBlock(const Block& block); 139[[nodiscard]] std::string DumpBlock(const Block& block);
diff --git a/src/shader_recompiler/frontend/ir/function.cpp b/src/shader_recompiler/frontend/ir/function.cpp
new file mode 100644
index 000000000..d1fc9461d
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/function.cpp
@@ -0,0 +1,5 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/frontend/ir/function.h"
diff --git a/src/shader_recompiler/frontend/ir/function.h b/src/shader_recompiler/frontend/ir/function.h
new file mode 100644
index 000000000..2d4dc5b98
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/function.h
@@ -0,0 +1,25 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <vector>
9
10#include "shader_recompiler/frontend/ir/basic_block.h"
11
12namespace Shader::IR {
13
14struct Function {
15 struct InplaceDelete {
16 void operator()(IR::Block* block) const noexcept {
17 std::destroy_at(block);
18 }
19 };
20 using UniqueBlock = std::unique_ptr<IR::Block, InplaceDelete>;
21
22 std::vector<UniqueBlock> blocks;
23};
24
25} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 553fec3b7..ecf76e23d 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -30,6 +30,11 @@ static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode)
30 30
31bool Inst::MayHaveSideEffects() const noexcept { 31bool Inst::MayHaveSideEffects() const noexcept {
32 switch (op) { 32 switch (op) {
33 case Opcode::Branch:
34 case Opcode::BranchConditional:
35 case Opcode::Exit:
36 case Opcode::Return:
37 case Opcode::Unreachable:
33 case Opcode::SetAttribute: 38 case Opcode::SetAttribute:
34 case Opcode::SetAttributeIndexed: 39 case Opcode::SetAttributeIndexed:
35 case Opcode::WriteGlobalU8: 40 case Opcode::WriteGlobalU8:
@@ -113,6 +118,17 @@ void Inst::SetArg(size_t index, Value value) {
113 args[index] = value; 118 args[index] = value;
114} 119}
115 120
121std::span<const std::pair<Block*, Value>> Inst::PhiOperands() const noexcept {
122 return phi_operands;
123}
124
125void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
126 if (!value.IsImmediate()) {
127 Use(value);
128 }
129 phi_operands.emplace_back(predecessor, value);
130}
131
116void Inst::Invalidate() { 132void Inst::Invalidate() {
117 ClearArgs(); 133 ClearArgs();
118 op = Opcode::Void; 134 op = Opcode::Void;
@@ -125,6 +141,12 @@ void Inst::ClearArgs() {
125 } 141 }
126 value = {}; 142 value = {};
127 } 143 }
144 for (auto& [phi_block, phi_op] : phi_operands) {
145 if (!phi_op.IsImmediate()) {
146 UndoUse(phi_op);
147 }
148 }
149 phi_operands.clear();
128} 150}
129 151
130void Inst::ReplaceUsesWith(Value replacement) { 152void Inst::ReplaceUsesWith(Value replacement) {
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.h b/src/shader_recompiler/frontend/ir/microinstruction.h
index 43460b950..7f1ed6710 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.h
+++ b/src/shader_recompiler/frontend/ir/microinstruction.h
@@ -5,6 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <span>
9#include <vector>
8 10
9#include <boost/intrusive/list.hpp> 11#include <boost/intrusive/list.hpp>
10 12
@@ -15,6 +17,8 @@
15 17
16namespace Shader::IR { 18namespace Shader::IR {
17 19
20class Block;
21
18constexpr size_t MAX_ARG_COUNT = 4; 22constexpr size_t MAX_ARG_COUNT = 4;
19 23
20class Inst : public boost::intrusive::list_base_hook<> { 24class Inst : public boost::intrusive::list_base_hook<> {
@@ -59,6 +63,11 @@ public:
59 /// Set the value of a given argument index. 63 /// Set the value of a given argument index.
60 void SetArg(size_t index, Value value); 64 void SetArg(size_t index, Value value);
61 65
66 /// Get an immutable span to the phi operands.
67 [[nodiscard]] std::span<const std::pair<Block*, Value>> PhiOperands() const noexcept;
68 /// Add phi operand to a phi instruction.
69 void AddPhiOperand(Block* predecessor, const Value& value);
70
62 void Invalidate(); 71 void Invalidate();
63 void ClearArgs(); 72 void ClearArgs();
64 73
@@ -76,6 +85,7 @@ private:
76 Inst* carry_inst{}; 85 Inst* carry_inst{};
77 Inst* overflow_inst{}; 86 Inst* overflow_inst{};
78 Inst* zsco_inst{}; 87 Inst* zsco_inst{};
88 std::vector<std::pair<Block*, Value>> phi_operands;
79 u64 flags{}; 89 u64 flags{};
80}; 90};
81 91
diff --git a/src/shader_recompiler/frontend/ir/opcode.inc b/src/shader_recompiler/frontend/ir/opcode.inc
index 371064bf3..40759e96a 100644
--- a/src/shader_recompiler/frontend/ir/opcode.inc
+++ b/src/shader_recompiler/frontend/ir/opcode.inc
@@ -5,6 +5,7 @@
5// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ... 5// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ...
6OPCODE(Void, Void, ) 6OPCODE(Void, Void, )
7OPCODE(Identity, Opaque, Opaque, ) 7OPCODE(Identity, Opaque, Opaque, )
8OPCODE(Phi, Opaque, /*todo*/ )
8 9
9// Control flow 10// Control flow
10OPCODE(Branch, Void, Label, ) 11OPCODE(Branch, Void, Label, )
@@ -35,6 +36,13 @@ OPCODE(SetSFlag, Void, U1,
35OPCODE(SetCFlag, Void, U1, ) 36OPCODE(SetCFlag, Void, U1, )
36OPCODE(SetOFlag, Void, U1, ) 37OPCODE(SetOFlag, Void, U1, )
37 38
39// Undefined
40OPCODE(Undef1, U1, )
41OPCODE(Undef8, U8, )
42OPCODE(Undef16, U16, )
43OPCODE(Undef32, U32, )
44OPCODE(Undef64, U64, )
45
38// Memory operations 46// Memory operations
39OPCODE(WriteGlobalU8, Void, U64, U32, ) 47OPCODE(WriteGlobalU8, Void, U64, U32, )
40OPCODE(WriteGlobalS8, Void, U64, U32, ) 48OPCODE(WriteGlobalS8, Void, U64, U32, )
diff --git a/src/shader_recompiler/frontend/ir/pred.h b/src/shader_recompiler/frontend/ir/pred.h
index 37cc53006..daf23193f 100644
--- a/src/shader_recompiler/frontend/ir/pred.h
+++ b/src/shader_recompiler/frontend/ir/pred.h
@@ -10,6 +10,13 @@ namespace Shader::IR {
10 10
11enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT }; 11enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT };
12 12
13constexpr size_t NUM_USER_PREDS = 6;
14constexpr size_t NUM_PREDS = 7;
15
16[[nodiscard]] constexpr size_t PredIndex(Pred pred) noexcept {
17 return static_cast<size_t>(pred);
18}
19
13} // namespace Shader::IR 20} // namespace Shader::IR
14 21
15template <> 22template <>
diff --git a/src/shader_recompiler/frontend/ir/reg.h b/src/shader_recompiler/frontend/ir/reg.h
index 316fc4be8..771094eb9 100644
--- a/src/shader_recompiler/frontend/ir/reg.h
+++ b/src/shader_recompiler/frontend/ir/reg.h
@@ -271,6 +271,9 @@ enum class Reg : u64 {
271}; 271};
272static_assert(static_cast<int>(Reg::RZ) == 255); 272static_assert(static_cast<int>(Reg::RZ) == 255);
273 273
274constexpr size_t NUM_USER_REGS = 255;
275constexpr size_t NUM_REGS = 256;
276
274[[nodiscard]] constexpr Reg operator+(Reg reg, int num) { 277[[nodiscard]] constexpr Reg operator+(Reg reg, int num) {
275 if (reg == Reg::RZ) { 278 if (reg == Reg::RZ) {
276 // Adding or subtracting registers from RZ yields RZ 279 // Adding or subtracting registers from RZ yields RZ
@@ -290,8 +293,12 @@ static_assert(static_cast<int>(Reg::RZ) == 255);
290 return reg + (-num); 293 return reg + (-num);
291} 294}
292 295
296[[nodiscard]] constexpr size_t RegIndex(Reg reg) noexcept {
297 return static_cast<size_t>(reg);
298}
299
293[[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) { 300[[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) {
294 return (static_cast<size_t>(reg) / align) * align == static_cast<size_t>(reg); 301 return (RegIndex(reg) / align) * align == RegIndex(reg);
295} 302}
296 303
297} // namespace Shader::IR 304} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
index 7b5b35d6c..1e974e88c 100644
--- a/src/shader_recompiler/frontend/ir/value.cpp
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -115,6 +115,43 @@ u64 Value::U64() const {
115 return imm_u64; 115 return imm_u64;
116} 116}
117 117
118bool Value::operator==(const Value& other) const {
119 if (type != other.type) {
120 return false;
121 }
122 switch (type) {
123 case Type::Void:
124 return true;
125 case Type::Opaque:
126 return inst == other.inst;
127 case Type::Label:
128 return label == other.label;
129 case Type::Reg:
130 return reg == other.reg;
131 case Type::Pred:
132 return pred == other.pred;
133 case Type::Attribute:
134 return attribute == other.attribute;
135 case Type::U1:
136 return imm_u1 == other.imm_u1;
137 case Type::U8:
138 return imm_u8 == other.imm_u8;
139 case Type::U16:
140 return imm_u16 == other.imm_u16;
141 case Type::U32:
142 return imm_u32 == other.imm_u32;
143 case Type::U64:
144 return imm_u64 == other.imm_u64;
145 case Type::ZSCO:
146 throw NotImplementedException("ZSCO comparison");
147 }
148 throw LogicError("Invalid type {}", type);
149}
150
151bool Value::operator!=(const Value& other) const {
152 return !operator==(other);
153}
154
118void Value::ValidateAccess(IR::Type expected) const { 155void Value::ValidateAccess(IR::Type expected) const {
119 if (type != expected) { 156 if (type != expected) {
120 throw LogicError("Reading {} out of {}", expected, type); 157 throw LogicError("Reading {} out of {}", expected, type);
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 664dacf9d..368119921 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -48,6 +48,9 @@ public:
48 [[nodiscard]] u32 U32() const; 48 [[nodiscard]] u32 U32() const;
49 [[nodiscard]] u64 U64() const; 49 [[nodiscard]] u64 U64() const;
50 50
51 [[nodiscard]] bool operator==(const Value& other) const;
52 [[nodiscard]] bool operator!=(const Value& other) const;
53
51private: 54private:
52 void ValidateAccess(IR::Type expected) const; 55 void ValidateAccess(IR::Type expected) const;
53 56
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
index fc4dba826..21ee98137 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
@@ -36,6 +36,7 @@ static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) {
36 .cond{true}, 36 .cond{true},
37 .branch_true{new_id}, 37 .branch_true{new_id},
38 .branch_false{UNREACHABLE_BLOCK_ID}, 38 .branch_false{UNREACHABLE_BLOCK_ID},
39 .imm_predecessors{},
39 }, 40 },
40 Block{ 41 Block{
41 .begin{pc}, 42 .begin{pc},
@@ -46,6 +47,7 @@ static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) {
46 .cond{block.cond}, 47 .cond{block.cond},
47 .branch_true{block.branch_true}, 48 .branch_true{block.branch_true},
48 .branch_false{block.branch_false}, 49 .branch_false{block.branch_false},
50 .imm_predecessors{},
49 }, 51 },
50 }; 52 };
51} 53}
@@ -108,7 +110,7 @@ static bool HasFlowTest(Opcode opcode) {
108 } 110 }
109} 111}
110 112
111static std::string Name(const Block& block) { 113static std::string NameOf(const Block& block) {
112 if (block.begin.IsVirtual()) { 114 if (block.begin.IsVirtual()) {
113 return fmt::format("\"Virtual {}\"", block.id); 115 return fmt::format("\"Virtual {}\"", block.id);
114 } else { 116 } else {
@@ -154,13 +156,127 @@ bool Block::Contains(Location pc) const noexcept {
154} 156}
155 157
156Function::Function(Location start_address) 158Function::Function(Location start_address)
157 : entrypoint{start_address}, labels{Label{ 159 : entrypoint{start_address}, labels{{
158 .address{start_address}, 160 .address{start_address},
159 .block_id{0}, 161 .block_id{0},
160 .stack{}, 162 .stack{},
161 }} {} 163 }} {}
162 164
165void Function::BuildBlocksMap() {
166 const size_t num_blocks{NumBlocks()};
167 blocks_map.resize(num_blocks);
168 for (size_t block_index = 0; block_index < num_blocks; ++block_index) {
169 Block& block{blocks_data[block_index]};
170 blocks_map[block.id] = &block;
171 }
172}
173
174void Function::BuildImmediatePredecessors() {
175 for (const Block& block : blocks_data) {
176 if (block.branch_true != UNREACHABLE_BLOCK_ID) {
177 blocks_map[block.branch_true]->imm_predecessors.push_back(block.id);
178 }
179 if (block.branch_false != UNREACHABLE_BLOCK_ID) {
180 blocks_map[block.branch_false]->imm_predecessors.push_back(block.id);
181 }
182 }
183}
184
185void Function::BuildPostOrder() {
186 boost::container::small_vector<BlockId, 0x110> block_stack;
187 post_order_map.resize(NumBlocks());
188
189 Block& first_block{blocks_data[blocks.front()]};
190 first_block.post_order_visited = true;
191 block_stack.push_back(first_block.id);
192
193 const auto visit_branch = [&](BlockId block_id, BlockId branch_id) {
194 if (branch_id == UNREACHABLE_BLOCK_ID) {
195 return false;
196 }
197 if (blocks_map[branch_id]->post_order_visited) {
198 return false;
199 }
200 blocks_map[branch_id]->post_order_visited = true;
201
202 // Calling push_back twice is faster than insert on msvc
203 block_stack.push_back(block_id);
204 block_stack.push_back(branch_id);
205 return true;
206 };
207 while (!block_stack.empty()) {
208 const Block* const block{blocks_map[block_stack.back()]};
209 block_stack.pop_back();
210
211 if (!visit_branch(block->id, block->branch_true) &&
212 !visit_branch(block->id, block->branch_false)) {
213 post_order_map[block->id] = static_cast<u32>(post_order_blocks.size());
214 post_order_blocks.push_back(block->id);
215 }
216 }
217}
218
219void Function::BuildImmediateDominators() {
220 auto transform_block_id{std::views::transform([this](BlockId id) { return blocks_map[id]; })};
221 auto reverse_order_but_first{std::views::reverse | std::views::drop(1) | transform_block_id};
222 auto has_idom{std::views::filter([](Block* block) { return block->imm_dominator; })};
223 auto intersect{[this](Block* finger1, Block* finger2) {
224 while (finger1 != finger2) {
225 while (post_order_map[finger1->id] < post_order_map[finger2->id]) {
226 finger1 = finger1->imm_dominator;
227 }
228 while (post_order_map[finger2->id] < post_order_map[finger1->id]) {
229 finger2 = finger2->imm_dominator;
230 }
231 }
232 return finger1;
233 }};
234 for (Block& block : blocks_data) {
235 block.imm_dominator = nullptr;
236 }
237 Block* const start_block{&blocks_data[blocks.front()]};
238 start_block->imm_dominator = start_block;
239
240 bool changed{true};
241 while (changed) {
242 changed = false;
243 for (Block* const block : post_order_blocks | reverse_order_but_first) {
244 Block* new_idom{};
245 for (Block* predecessor : block->imm_predecessors | transform_block_id | has_idom) {
246 new_idom = new_idom ? intersect(predecessor, new_idom) : predecessor;
247 }
248 changed |= block->imm_dominator != new_idom;
249 block->imm_dominator = new_idom;
250 }
251 }
252}
253
254void Function::BuildDominanceFrontier() {
255 auto transform_block_id{std::views::transform([this](BlockId id) { return blocks_map[id]; })};
256 auto has_enough_predecessors{[](Block& block) { return block.imm_predecessors.size() >= 2; }};
257 for (Block& block : blocks_data | std::views::filter(has_enough_predecessors)) {
258 for (Block* current : block.imm_predecessors | transform_block_id) {
259 while (current != block.imm_dominator) {
260 current->dominance_frontiers.push_back(current->id);
261 current = current->imm_dominator;
262 }
263 }
264 }
265}
266
163CFG::CFG(Environment& env_, Location start_address) : env{env_} { 267CFG::CFG(Environment& env_, Location start_address) : env{env_} {
268 VisitFunctions(start_address);
269
270 for (Function& function : functions) {
271 function.BuildBlocksMap();
272 function.BuildImmediatePredecessors();
273 function.BuildPostOrder();
274 function.BuildImmediateDominators();
275 function.BuildDominanceFrontier();
276 }
277}
278
279void CFG::VisitFunctions(Location start_address) {
164 functions.emplace_back(start_address); 280 functions.emplace_back(start_address);
165 for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { 281 for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) {
166 while (!functions[function_id].labels.empty()) { 282 while (!functions[function_id].labels.empty()) {
@@ -202,6 +318,7 @@ void CFG::AnalyzeLabel(FunctionId function_id, Label& label) {
202 .cond{true}, 318 .cond{true},
203 .branch_true{UNREACHABLE_BLOCK_ID}, 319 .branch_true{UNREACHABLE_BLOCK_ID},
204 .branch_false{UNREACHABLE_BLOCK_ID}, 320 .branch_false{UNREACHABLE_BLOCK_ID},
321 .imm_predecessors{},
205 }; 322 };
206 // Analyze instructions until it reaches an already visited block or there's a branch 323 // Analyze instructions until it reaches an already visited block or there's a branch
207 bool is_branch{false}; 324 bool is_branch{false};
@@ -310,7 +427,7 @@ CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Locati
310 // Technically CAL pushes into PRET, but that's implicit in the function call for us 427 // Technically CAL pushes into PRET, but that's implicit in the function call for us
311 // Insert the function into the list if it doesn't exist 428 // Insert the function into the list if it doesn't exist
312 if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { 429 if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) {
313 functions.push_back(cal_pc); 430 functions.emplace_back(cal_pc);
314 } 431 }
315 // Handle CAL like a regular instruction 432 // Handle CAL like a regular instruction
316 break; 433 break;
@@ -352,6 +469,7 @@ void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc,
352 .cond{cond}, 469 .cond{cond},
353 .branch_true{conditional_block_id}, 470 .branch_true{conditional_block_id},
354 .branch_false{UNREACHABLE_BLOCK_ID}, 471 .branch_false{UNREACHABLE_BLOCK_ID},
472 .imm_predecessors{},
355 })}; 473 })};
356 // Set the end properties of the conditional instruction and give it a new identity 474 // Set the end properties of the conditional instruction and give it a new identity
357 Block& conditional_block{block}; 475 Block& conditional_block{block};
@@ -465,14 +583,14 @@ std::string CFG::Dot() const {
465 dot += fmt::format("\t\tnode [style=filled];\n"); 583 dot += fmt::format("\t\tnode [style=filled];\n");
466 for (const u32 block_index : function.blocks) { 584 for (const u32 block_index : function.blocks) {
467 const Block& block{function.blocks_data[block_index]}; 585 const Block& block{function.blocks_data[block_index]};
468 const std::string name{Name(block)}; 586 const std::string name{NameOf(block)};
469 const auto add_branch = [&](BlockId branch_id, bool add_label) { 587 const auto add_branch = [&](BlockId branch_id, bool add_label) {
470 const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)}; 588 const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)};
471 dot += fmt::format("\t\t{}->", name); 589 dot += fmt::format("\t\t{}->", name);
472 if (it == function.blocks_data.end()) { 590 if (it == function.blocks_data.end()) {
473 dot += fmt::format("\"Unknown label {}\"", branch_id); 591 dot += fmt::format("\"Unknown label {}\"", branch_id);
474 } else { 592 } else {
475 dot += Name(*it); 593 dot += NameOf(*it);
476 }; 594 };
477 if (add_label && block.cond != true && block.cond != false) { 595 if (add_label && block.cond != true && block.cond != false) {
478 dot += fmt::format(" [label=\"{}\"]", block.cond); 596 dot += fmt::format(" [label=\"{}\"]", block.cond);
@@ -520,7 +638,7 @@ std::string CFG::Dot() const {
520 if (functions.front().blocks.empty()) { 638 if (functions.front().blocks.empty()) {
521 dot += "Start;\n"; 639 dot += "Start;\n";
522 } else { 640 } else {
523 dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front())); 641 dot += fmt::format("\tStart -> {};\n", NameOf(functions.front().blocks_data.front()));
524 } 642 }
525 dot += fmt::format("\tStart [shape=diamond];\n"); 643 dot += fmt::format("\tStart [shape=diamond];\n");
526 } 644 }
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
index b2ab0cdc3..20ada8afd 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -70,6 +70,12 @@ struct Block {
70 IR::Condition cond; 70 IR::Condition cond;
71 BlockId branch_true; 71 BlockId branch_true;
72 BlockId branch_false; 72 BlockId branch_false;
73 boost::container::small_vector<BlockId, 4> imm_predecessors;
74 boost::container::small_vector<BlockId, 8> dominance_frontiers;
75 union {
76 bool post_order_visited{false};
77 Block* imm_dominator;
78 };
73}; 79};
74 80
75struct Label { 81struct Label {
@@ -81,11 +87,30 @@ struct Label {
81struct Function { 87struct Function {
82 Function(Location start_address); 88 Function(Location start_address);
83 89
90 void BuildBlocksMap();
91
92 void BuildImmediatePredecessors();
93
94 void BuildPostOrder();
95
96 void BuildImmediateDominators();
97
98 void BuildDominanceFrontier();
99
100 [[nodiscard]] size_t NumBlocks() const noexcept {
101 return static_cast<size_t>(current_block_id) + 1;
102 }
103
84 Location entrypoint; 104 Location entrypoint;
85 BlockId current_block_id{0}; 105 BlockId current_block_id{0};
86 boost::container::small_vector<Label, 16> labels; 106 boost::container::small_vector<Label, 16> labels;
87 boost::container::small_vector<u32, 0x130> blocks; 107 boost::container::small_vector<u32, 0x130> blocks;
88 boost::container::small_vector<Block, 0x130> blocks_data; 108 boost::container::small_vector<Block, 0x130> blocks_data;
109 // Translates from BlockId to block index
110 boost::container::small_vector<Block*, 0x130> blocks_map;
111
112 boost::container::small_vector<u32, 0x130> post_order_blocks;
113 boost::container::small_vector<BlockId, 0x130> post_order_map;
89}; 114};
90 115
91class CFG { 116class CFG {
@@ -97,6 +122,12 @@ class CFG {
97public: 122public:
98 explicit CFG(Environment& env, Location start_address); 123 explicit CFG(Environment& env, Location start_address);
99 124
125 CFG& operator=(const CFG&) = delete;
126 CFG(const CFG&) = delete;
127
128 CFG& operator=(CFG&&) = delete;
129 CFG(CFG&&) = delete;
130
100 [[nodiscard]] std::string Dot() const; 131 [[nodiscard]] std::string Dot() const;
101 132
102 [[nodiscard]] std::span<const Function> Functions() const noexcept { 133 [[nodiscard]] std::span<const Function> Functions() const noexcept {
@@ -104,20 +135,22 @@ public:
104 } 135 }
105 136
106private: 137private:
138 void VisitFunctions(Location start_address);
139
107 void AnalyzeLabel(FunctionId function_id, Label& label); 140 void AnalyzeLabel(FunctionId function_id, Label& label);
108 141
109 /// Inspect already visited blocks. 142 /// Inspect already visited blocks.
110 /// Return true when the block has already been visited 143 /// Return true when the block has already been visited
111 [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label); 144 bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
112 145
113 [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc); 146 AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc);
114 147
115 void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class, 148 void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class,
116 IR::Condition cond); 149 IR::Condition cond);
117 150
118 /// Return true when the branch instruction is confirmed to be a branch 151 /// Return true when the branch instruction is confirmed to be a branch
119 [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc, 152 bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst,
120 Instruction inst, Opcode opcode); 153 Opcode opcode);
121 154
122 void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst, 155 void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
123 bool is_absolute); 156 bool is_absolute);
@@ -126,8 +159,7 @@ private:
126 AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst); 159 AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst);
127 160
128 /// Return the branch target block id 161 /// Return the branch target block id
129 [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc, 162 BlockId AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id);
130 FunctionId function_id);
131 163
132 Environment& env; 164 Environment& env;
133 boost::container::small_vector<Function, 1> functions; 165 boost::container::small_vector<Function, 1> functions;
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp
index 67a98ba57..49d1f4bfb 100644
--- a/src/shader_recompiler/frontend/maxwell/program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/program.cpp
@@ -8,40 +8,53 @@
8#include "shader_recompiler/frontend/maxwell/program.h" 8#include "shader_recompiler/frontend/maxwell/program.h"
9#include "shader_recompiler/frontend/maxwell/termination_code.h" 9#include "shader_recompiler/frontend/maxwell/termination_code.h"
10#include "shader_recompiler/frontend/maxwell/translate/translate.h" 10#include "shader_recompiler/frontend/maxwell/translate/translate.h"
11#include "shader_recompiler/ir_opt/passes.h"
11 12
12namespace Shader::Maxwell { 13namespace Shader::Maxwell {
14namespace {
15void TranslateCode(Environment& env, const Flow::Function& cfg_function, IR::Function& function,
16 std::span<IR::Block*> block_map, IR::Block* block_memory) {
17 const size_t num_blocks{cfg_function.blocks.size()};
18 function.blocks.reserve(num_blocks);
13 19
14Program::Function::~Function() { 20 for (const Flow::BlockId block_id : cfg_function.blocks) {
15 std::ranges::for_each(blocks, &std::destroy_at<IR::Block>); 21 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
16}
17
18Program::Program(Environment& env, const Flow::CFG& cfg) {
19 std::vector<IR::Block*> block_map;
20 functions.reserve(cfg.Functions().size());
21 22
22 for (const Flow::Function& cfg_function : cfg.Functions()) { 23 function.blocks.emplace_back(std::construct_at(block_memory, Translate(env, flow_block)));
23 Function& function{functions.emplace_back()}; 24 block_map[flow_block.id] = function.blocks.back().get();
25 ++block_memory;
26 }
27}
24 28
25 const size_t num_blocks{cfg_function.blocks.size()}; 29void EmitTerminationInsts(const Flow::Function& cfg_function,
26 IR::Block* block_memory{block_alloc_pool.allocate(num_blocks)}; 30 std::span<IR::Block* const> block_map) {
27 function.blocks.reserve(num_blocks); 31 for (const Flow::BlockId block_id : cfg_function.blocks) {
32 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
33 EmitTerminationCode(flow_block, block_map);
34 }
35}
28 36
29 block_map.resize(cfg_function.blocks_data.size()); 37void TranslateFunction(Environment& env, const Flow::Function& cfg_function, IR::Function& function,
38 IR::Block* block_memory) {
39 std::vector<IR::Block*> block_map;
40 block_map.resize(cfg_function.blocks_data.size());
30 41
31 // Visit the instructions of all blocks 42 TranslateCode(env, cfg_function, function, block_map, block_memory);
32 for (const Flow::BlockId block_id : cfg_function.blocks) { 43 EmitTerminationInsts(cfg_function, block_map);
33 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; 44}
45} // Anonymous namespace
34 46
35 IR::Block* const block{std::construct_at(block_memory, Translate(env, flow_block))}; 47Program::Program(Environment& env, const Flow::CFG& cfg) {
36 ++block_memory; 48 functions.reserve(cfg.Functions().size());
37 function.blocks.push_back(block); 49 for (const Flow::Function& cfg_function : cfg.Functions()) {
38 block_map[flow_block.id] = block; 50 TranslateFunction(env, cfg_function, functions.emplace_back(),
39 } 51 block_alloc_pool.allocate(cfg_function.blocks.size()));
40 // Now that all blocks are defined, emit the termination instructions 52 }
41 for (const Flow::BlockId block_id : cfg_function.blocks) { 53 std::ranges::for_each(functions, Optimization::SsaRewritePass);
42 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; 54 for (IR::Function& function : functions) {
43 EmitTerminationCode(flow_block, block_map); 55 Optimization::Invoke(Optimization::DeadCodeEliminationPass, function);
44 } 56 Optimization::Invoke(Optimization::IdentityRemovalPass, function);
57 // Optimization::Invoke(Optimization::VerificationPass, function);
45 } 58 }
46} 59}
47 60
@@ -50,16 +63,16 @@ std::string DumpProgram(const Program& program) {
50 std::map<const IR::Inst*, size_t> inst_to_index; 63 std::map<const IR::Inst*, size_t> inst_to_index;
51 std::map<const IR::Block*, size_t> block_to_index; 64 std::map<const IR::Block*, size_t> block_to_index;
52 65
53 for (const Program::Function& function : program.functions) { 66 for (const IR::Function& function : program.functions) {
54 for (const IR::Block* const block : function.blocks) { 67 for (const auto& block : function.blocks) {
55 block_to_index.emplace(block, index); 68 block_to_index.emplace(block.get(), index);
56 ++index; 69 ++index;
57 } 70 }
58 } 71 }
59 std::string ret; 72 std::string ret;
60 for (const Program::Function& function : program.functions) { 73 for (const IR::Function& function : program.functions) {
61 ret += fmt::format("Function\n"); 74 ret += fmt::format("Function\n");
62 for (const IR::Block* const block : function.blocks) { 75 for (const auto& block : function.blocks) {
63 ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; 76 ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
64 } 77 }
65 } 78 }
diff --git a/src/shader_recompiler/frontend/maxwell/program.h b/src/shader_recompiler/frontend/maxwell/program.h
index 7814b2c01..36e678a9e 100644
--- a/src/shader_recompiler/frontend/maxwell/program.h
+++ b/src/shader_recompiler/frontend/maxwell/program.h
@@ -4,13 +4,16 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <memory>
7#include <string> 8#include <string>
8#include <vector> 9#include <vector>
9 10
11#include <boost/container/small_vector.hpp>
10#include <boost/pool/pool_alloc.hpp> 12#include <boost/pool/pool_alloc.hpp>
11 13
12#include "shader_recompiler/environment.h" 14#include "shader_recompiler/environment.h"
13#include "shader_recompiler/frontend/ir/basic_block.h" 15#include "shader_recompiler/frontend/ir/basic_block.h"
16#include "shader_recompiler/frontend/ir/function.h"
14#include "shader_recompiler/frontend/maxwell/control_flow.h" 17#include "shader_recompiler/frontend/maxwell/control_flow.h"
15 18
16namespace Shader::Maxwell { 19namespace Shader::Maxwell {
@@ -22,16 +25,10 @@ public:
22 explicit Program(Environment& env, const Flow::CFG& cfg); 25 explicit Program(Environment& env, const Flow::CFG& cfg);
23 26
24private: 27private:
25 struct Function {
26 ~Function();
27
28 std::vector<IR::Block*> blocks;
29 };
30
31 boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete, 28 boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete,
32 boost::details::pool::null_mutex> 29 boost::details::pool::null_mutex>
33 block_alloc_pool; 30 block_alloc_pool;
34 std::vector<Function> functions; 31 boost::container::small_vector<IR::Function, 1> functions;
35}; 32};
36 33
37[[nodiscard]] std::string DumpProgram(const Program& program); 34[[nodiscard]] std::string DumpProgram(const Program& program);
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.cpp b/src/shader_recompiler/frontend/maxwell/termination_code.cpp
index a4ea5c5e3..ed5137f20 100644
--- a/src/shader_recompiler/frontend/maxwell/termination_code.cpp
+++ b/src/shader_recompiler/frontend/maxwell/termination_code.cpp
@@ -47,12 +47,19 @@ static IR::U1 GetCond(IR::Condition cond, IR::IREmitter& ir) {
47 47
48static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map, 48static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map,
49 IR::IREmitter& ir) { 49 IR::IREmitter& ir) {
50 const auto add_immediate_predecessor = [&](Flow::BlockId label) {
51 block_map[label]->AddImmediatePredecessor(&ir.block);
52 };
50 if (flow_block.cond == true) { 53 if (flow_block.cond == true) {
54 add_immediate_predecessor(flow_block.branch_true);
51 return ir.Branch(block_map[flow_block.branch_true]); 55 return ir.Branch(block_map[flow_block.branch_true]);
52 } 56 }
53 if (flow_block.cond == false) { 57 if (flow_block.cond == false) {
58 add_immediate_predecessor(flow_block.branch_false);
54 return ir.Branch(block_map[flow_block.branch_false]); 59 return ir.Branch(block_map[flow_block.branch_false]);
55 } 60 }
61 add_immediate_predecessor(flow_block.branch_true);
62 add_immediate_predecessor(flow_block.branch_false);
56 return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true], 63 return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true],
57 block_map[flow_block.branch_false]); 64 block_map[flow_block.branch_false]);
58} 65}
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.h b/src/shader_recompiler/frontend/maxwell/termination_code.h
index b0d667942..04e044534 100644
--- a/src/shader_recompiler/frontend/maxwell/termination_code.h
+++ b/src/shader_recompiler/frontend/maxwell/termination_code.h
@@ -11,6 +11,7 @@
11 11
12namespace Shader::Maxwell { 12namespace Shader::Maxwell {
13 13
14/// Emit termination instructions and collect immediate predecessors
14void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map); 15void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map);
15 16
16} // namespace Shader::Maxwell 17} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
index bc607b002..8be7d6ff1 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
@@ -208,7 +208,7 @@ public:
208 void P2R_reg(u64 insn); 208 void P2R_reg(u64 insn);
209 void P2R_cbuf(u64 insn); 209 void P2R_cbuf(u64 insn);
210 void P2R_imm(u64 insn); 210 void P2R_imm(u64 insn);
211 void PBK(u64 insn); 211 void PBK();
212 void PCNT(u64 insn); 212 void PCNT(u64 insn);
213 void PEXIT(u64 insn); 213 void PEXIT(u64 insn);
214 void PIXLD(u64 insn); 214 void PIXLD(u64 insn);
@@ -252,7 +252,7 @@ public:
252 void SHR_reg(u64 insn); 252 void SHR_reg(u64 insn);
253 void SHR_cbuf(u64 insn); 253 void SHR_cbuf(u64 insn);
254 void SHR_imm(u64 insn); 254 void SHR_imm(u64 insn);
255 void SSY(u64 insn); 255 void SSY();
256 void ST(u64 insn); 256 void ST(u64 insn);
257 void STG(u64 insn); 257 void STG(u64 insn);
258 void STL(u64 insn); 258 void STL(u64 insn);
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
index c907c1ffb..0f52696d1 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -762,7 +762,7 @@ void TranslatorVisitor::P2R_imm(u64) {
762 ThrowNotImplemented(Opcode::P2R_imm); 762 ThrowNotImplemented(Opcode::P2R_imm);
763} 763}
764 764
765void TranslatorVisitor::PBK(u64) { 765void TranslatorVisitor::PBK() {
766 // PBK is a no-op 766 // PBK is a no-op
767} 767}
768 768
@@ -938,8 +938,8 @@ void TranslatorVisitor::SHR_imm(u64) {
938 ThrowNotImplemented(Opcode::SHR_imm); 938 ThrowNotImplemented(Opcode::SHR_imm);
939} 939}
940 940
941void TranslatorVisitor::SSY(u64) { 941void TranslatorVisitor::SSY() {
942 ThrowNotImplemented(Opcode::SSY); 942 // SSY is a no-op
943} 943}
944 944
945void TranslatorVisitor::ST(u64) { 945void TranslatorVisitor::ST(u64) {