summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-03-14 03:41:05 -0300
committerGravatar ameerj2021-07-22 21:51:23 -0400
commit71f96fa6366dc6dd306a953bca1b958fb32bc55a (patch)
tree12e13f9502e4b9510446c967a831e5d4bacb729e /src/shader_recompiler/frontend
parentspirv: Add SignedZeroInfNanPreserve logic (diff)
downloadyuzu-71f96fa6366dc6dd306a953bca1b958fb32bc55a.tar.gz
yuzu-71f96fa6366dc6dd306a953bca1b958fb32bc55a.tar.xz
yuzu-71f96fa6366dc6dd306a953bca1b958fb32bc55a.zip
shader: Implement CAL inlining function calls
Diffstat (limited to '')
-rw-r--r--src/shader_recompiler/frontend/ir/function.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/function.h18
-rw-r--r--src/shader_recompiler/frontend/ir/program.cpp18
-rw-r--r--src/shader_recompiler/frontend/ir/program.h5
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp78
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h19
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.cpp71
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp (renamed from src/shader_recompiler/frontend/ir/structured_control_flow.cpp)254
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.h (renamed from src/shader_recompiler/frontend/ir/structured_control_flow.h)12
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.h2
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp4
11 files changed, 232 insertions, 254 deletions
diff --git a/src/shader_recompiler/frontend/ir/function.cpp b/src/shader_recompiler/frontend/ir/function.cpp
deleted file mode 100644
index d1fc9461d..000000000
--- a/src/shader_recompiler/frontend/ir/function.cpp
+++ /dev/null
@@ -1,5 +0,0 @@
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
deleted file mode 100644
index d1f061146..000000000
--- a/src/shader_recompiler/frontend/ir/function.h
+++ /dev/null
@@ -1,18 +0,0 @@
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 <boost/container/small_vector.hpp>
8
9#include "shader_recompiler/frontend/ir/basic_block.h"
10
11namespace Shader::IR {
12
13struct Function {
14 BlockList blocks;
15 BlockList post_order_blocks;
16};
17
18} // namespace Shader::IR
diff --git a/src/shader_recompiler/frontend/ir/program.cpp b/src/shader_recompiler/frontend/ir/program.cpp
index 8c301c3a1..5f51aeb5f 100644
--- a/src/shader_recompiler/frontend/ir/program.cpp
+++ b/src/shader_recompiler/frontend/ir/program.cpp
@@ -9,7 +9,8 @@
9 9
10#include <fmt/format.h> 10#include <fmt/format.h>
11 11
12#include "shader_recompiler/frontend/ir/function.h" 12#include "shader_recompiler/frontend/ir/basic_block.h"
13#include "shader_recompiler/frontend/ir/microinstruction.h"
13#include "shader_recompiler/frontend/ir/program.h" 14#include "shader_recompiler/frontend/ir/program.h"
14 15
15namespace Shader::IR { 16namespace Shader::IR {
@@ -19,18 +20,13 @@ std::string DumpProgram(const Program& program) {
19 std::map<const IR::Inst*, size_t> inst_to_index; 20 std::map<const IR::Inst*, size_t> inst_to_index;
20 std::map<const IR::Block*, size_t> block_to_index; 21 std::map<const IR::Block*, size_t> block_to_index;
21 22
22 for (const IR::Function& function : program.functions) { 23 for (const IR::Block* const block : program.blocks) {
23 for (const IR::Block* const block : function.blocks) { 24 block_to_index.emplace(block, index);
24 block_to_index.emplace(block, index); 25 ++index;
25 ++index;
26 }
27 } 26 }
28 std::string ret; 27 std::string ret;
29 for (const IR::Function& function : program.functions) { 28 for (const auto& block : program.blocks) {
30 ret += fmt::format("Function\n"); 29 ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
31 for (const auto& block : function.blocks) {
32 ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
33 }
34 } 30 }
35 return ret; 31 return ret;
36} 32}
diff --git a/src/shader_recompiler/frontend/ir/program.h b/src/shader_recompiler/frontend/ir/program.h
index 98aab2dc6..bce8b19b3 100644
--- a/src/shader_recompiler/frontend/ir/program.h
+++ b/src/shader_recompiler/frontend/ir/program.h
@@ -8,13 +8,14 @@
8 8
9#include <boost/container/small_vector.hpp> 9#include <boost/container/small_vector.hpp>
10 10
11#include "shader_recompiler/frontend/ir/function.h" 11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/shader_info.h" 12#include "shader_recompiler/shader_info.h"
13 13
14namespace Shader::IR { 14namespace Shader::IR {
15 15
16struct Program { 16struct Program {
17 boost::container::small_vector<Function, 1> functions; 17 BlockList blocks;
18 BlockList post_order_blocks;
18 Info info; 19 Info info;
19}; 20};
20 21
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
index d0dc66330..715c0e92d 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
@@ -31,13 +31,12 @@ struct Compare {
31 return lhs.begin < rhs.begin; 31 return lhs.begin < rhs.begin;
32 } 32 }
33}; 33};
34} // Anonymous namespace
35 34
36static u32 BranchOffset(Location pc, Instruction inst) { 35u32 BranchOffset(Location pc, Instruction inst) {
37 return pc.Offset() + inst.branch.Offset() + 8; 36 return pc.Offset() + inst.branch.Offset() + 8;
38} 37}
39 38
40static void Split(Block* old_block, Block* new_block, Location pc) { 39void Split(Block* old_block, Block* new_block, Location pc) {
41 if (pc <= old_block->begin || pc >= old_block->end) { 40 if (pc <= old_block->begin || pc >= old_block->end) {
42 throw InvalidArgument("Invalid address to split={}", pc); 41 throw InvalidArgument("Invalid address to split={}", pc);
43 } 42 }
@@ -49,21 +48,19 @@ static void Split(Block* old_block, Block* new_block, Location pc) {
49 .cond{old_block->cond}, 48 .cond{old_block->cond},
50 .branch_true{old_block->branch_true}, 49 .branch_true{old_block->branch_true},
51 .branch_false{old_block->branch_false}, 50 .branch_false{old_block->branch_false},
52 .ir{nullptr},
53 }; 51 };
54 *old_block = Block{ 52 *old_block = Block{
55 .begin{old_block->begin}, 53 .begin{old_block->begin},
56 .end{pc}, 54 .end{pc},
57 .end_class{EndClass::Branch}, 55 .end_class{EndClass::Branch},
58 .stack{std::move(old_block->stack)}, 56 .stack{std::move(old_block->stack)},
59 .cond{IR::Condition{true}}, 57 .cond{true},
60 .branch_true{new_block}, 58 .branch_true{new_block},
61 .branch_false{nullptr}, 59 .branch_false{nullptr},
62 .ir{nullptr},
63 }; 60 };
64} 61}
65 62
66static Token OpcodeToken(Opcode opcode) { 63Token OpcodeToken(Opcode opcode) {
67 switch (opcode) { 64 switch (opcode) {
68 case Opcode::PBK: 65 case Opcode::PBK:
69 case Opcode::BRK: 66 case Opcode::BRK:
@@ -89,7 +86,7 @@ static Token OpcodeToken(Opcode opcode) {
89 } 86 }
90} 87}
91 88
92static bool IsAbsoluteJump(Opcode opcode) { 89bool IsAbsoluteJump(Opcode opcode) {
93 switch (opcode) { 90 switch (opcode) {
94 case Opcode::JCAL: 91 case Opcode::JCAL:
95 case Opcode::JMP: 92 case Opcode::JMP:
@@ -100,7 +97,7 @@ static bool IsAbsoluteJump(Opcode opcode) {
100 } 97 }
101} 98}
102 99
103static bool HasFlowTest(Opcode opcode) { 100bool HasFlowTest(Opcode opcode) {
104 switch (opcode) { 101 switch (opcode) {
105 case Opcode::BRA: 102 case Opcode::BRA:
106 case Opcode::BRX: 103 case Opcode::BRX:
@@ -121,13 +118,14 @@ static bool HasFlowTest(Opcode opcode) {
121 } 118 }
122} 119}
123 120
124static std::string NameOf(const Block& block) { 121std::string NameOf(const Block& block) {
125 if (block.begin.IsVirtual()) { 122 if (block.begin.IsVirtual()) {
126 return fmt::format("\"Virtual {}\"", block.begin); 123 return fmt::format("\"Virtual {}\"", block.begin);
127 } else { 124 } else {
128 return fmt::format("\"{}\"", block.begin); 125 return fmt::format("\"{}\"", block.begin);
129 } 126 }
130} 127}
128} // Anonymous namespace
131 129
132void Stack::Push(Token token, Location target) { 130void Stack::Push(Token token, Location target) {
133 entries.push_back({ 131 entries.push_back({
@@ -166,26 +164,24 @@ bool Block::Contains(Location pc) const noexcept {
166 return pc >= begin && pc < end; 164 return pc >= begin && pc < end;
167} 165}
168 166
169Function::Function(Location start_address) 167Function::Function(ObjectPool<Block>& block_pool, Location start_address)
170 : entrypoint{start_address}, labels{{ 168 : entrypoint{start_address}, labels{{
171 .address{start_address}, 169 .address{start_address},
172 .block{nullptr}, 170 .block{block_pool.Create(Block{
171 .begin{start_address},
172 .end{start_address},
173 .end_class{EndClass::Branch},
174 .stack{},
175 .cond{true},
176 .branch_true{nullptr},
177 .branch_false{nullptr},
178 })},
173 .stack{}, 179 .stack{},
174 }} {} 180 }} {}
175 181
176CFG::CFG(Environment& env_, ObjectPool<Block>& block_pool_, Location start_address) 182CFG::CFG(Environment& env_, ObjectPool<Block>& block_pool_, Location start_address)
177 : env{env_}, block_pool{block_pool_} { 183 : env{env_}, block_pool{block_pool_} {
178 functions.emplace_back(start_address); 184 functions.emplace_back(block_pool, start_address);
179 functions.back().labels.back().block = block_pool.Create(Block{
180 .begin{start_address},
181 .end{start_address},
182 .end_class{EndClass::Branch},
183 .stack{},
184 .cond{IR::Condition{true}},
185 .branch_true{nullptr},
186 .branch_false{nullptr},
187 .ir{nullptr},
188 });
189 for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { 185 for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) {
190 while (!functions[function_id].labels.empty()) { 186 while (!functions[function_id].labels.empty()) {
191 Function& function{functions[function_id]}; 187 Function& function{functions[function_id]};
@@ -308,11 +304,17 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati
308 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; 304 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
309 // Technically CAL pushes into PRET, but that's implicit in the function call for us 305 // Technically CAL pushes into PRET, but that's implicit in the function call for us
310 // Insert the function into the list if it doesn't exist 306 // Insert the function into the list if it doesn't exist
311 if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { 307 const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)};
312 functions.emplace_back(cal_pc); 308 const bool exists{it != functions.end()};
309 const FunctionId call_id{exists ? std::distance(functions.begin(), it) : functions.size()};
310 if (!exists) {
311 functions.emplace_back(block_pool, cal_pc);
313 } 312 }
314 // Handle CAL like a regular instruction 313 block->end_class = EndClass::Call;
315 break; 314 block->function_call = call_id;
315 block->return_block = AddLabel(block, block->stack, pc + 1, function_id);
316 block->end = pc;
317 return AnalysisState::Branch;
316 } 318 }
317 default: 319 default:
318 break; 320 break;
@@ -348,7 +350,6 @@ void CFG::AnalyzeCondInst(Block* block, FunctionId function_id, Location pc,
348 .cond{cond}, 350 .cond{cond},
349 .branch_true{conditional_block}, 351 .branch_true{conditional_block},
350 .branch_false{nullptr}, 352 .branch_false{nullptr},
351 .ir{nullptr},
352 }; 353 };
353 // Save the contents of the visited block in the conditional block 354 // Save the contents of the visited block in the conditional block
354 *conditional_block = std::move(*block); 355 *conditional_block = std::move(*block);
@@ -401,16 +402,6 @@ void CFG::AnalyzeBRX(Block*, Location, Instruction, bool is_absolute) {
401 throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX"); 402 throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX");
402} 403}
403 404
404void CFG::AnalyzeCAL(Location pc, Instruction inst, bool is_absolute) {
405 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
406 // Technically CAL pushes into PRET, but that's implicit in the function call for us
407 // Insert the function to the function list if it doesn't exist
408 const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)};
409 if (it == functions.end()) {
410 functions.emplace_back(cal_pc);
411 }
412}
413
414CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, 405CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc,
415 Instruction inst) { 406 Instruction inst) {
416 const IR::FlowTest flow_test{inst.branch.flow_test}; 407 const IR::FlowTest flow_test{inst.branch.flow_test};
@@ -455,10 +446,9 @@ Block* CFG::AddLabel(Block* block, Stack stack, Location pc, FunctionId function
455 .end{pc}, 446 .end{pc},
456 .end_class{EndClass::Branch}, 447 .end_class{EndClass::Branch},
457 .stack{stack}, 448 .stack{stack},
458 .cond{IR::Condition{true}}, 449 .cond{true},
459 .branch_true{nullptr}, 450 .branch_true{nullptr},
460 .branch_false{nullptr}, 451 .branch_false{nullptr},
461 .ir{nullptr},
462 })}; 452 })};
463 function.labels.push_back(Label{ 453 function.labels.push_back(Label{
464 .address{pc}, 454 .address{pc},
@@ -495,6 +485,14 @@ std::string CFG::Dot() const {
495 add_branch(block.branch_false, false); 485 add_branch(block.branch_false, false);
496 } 486 }
497 break; 487 break;
488 case EndClass::Call:
489 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
490 dot += fmt::format("\t\tN{}->{};\n", node_uid, NameOf(*block.return_block));
491 dot += fmt::format("\t\tN{} [label=\"Call {}\"][shape=square][style=stripped];\n",
492 node_uid, block.function_call);
493 dot += '\n';
494 ++node_uid;
495 break;
498 case EndClass::Exit: 496 case EndClass::Exit:
499 dot += fmt::format("\t\t{}->N{};\n", name, node_uid); 497 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
500 dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n", 498 dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n",
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
index 209c9e551..fe74f210f 100644
--- a/src/shader_recompiler/frontend/maxwell/control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -20,16 +20,13 @@
20#include "shader_recompiler/frontend/maxwell/opcodes.h" 20#include "shader_recompiler/frontend/maxwell/opcodes.h"
21#include "shader_recompiler/object_pool.h" 21#include "shader_recompiler/object_pool.h"
22 22
23namespace Shader::IR {
24class Block;
25}
26
27namespace Shader::Maxwell::Flow { 23namespace Shader::Maxwell::Flow {
28 24
29using FunctionId = size_t; 25using FunctionId = size_t;
30 26
31enum class EndClass { 27enum class EndClass {
32 Branch, 28 Branch,
29 Call,
33 Exit, 30 Exit,
34 Return, 31 Return,
35}; 32};
@@ -75,9 +72,14 @@ struct Block : boost::intrusive::set_base_hook<
75 EndClass end_class; 72 EndClass end_class;
76 Stack stack; 73 Stack stack;
77 IR::Condition cond; 74 IR::Condition cond;
78 Block* branch_true; 75 union {
79 Block* branch_false; 76 Block* branch_true;
80 IR::Block* ir; 77 FunctionId function_call;
78 };
79 union {
80 Block* branch_false;
81 Block* return_block;
82 };
81}; 83};
82 84
83struct Label { 85struct Label {
@@ -87,7 +89,7 @@ struct Label {
87}; 89};
88 90
89struct Function { 91struct Function {
90 Function(Location start_address); 92 explicit Function(ObjectPool<Block>& block_pool, Location start_address);
91 93
92 Location entrypoint; 94 Location entrypoint;
93 boost::container::small_vector<Label, 16> labels; 95 boost::container::small_vector<Label, 16> labels;
@@ -137,7 +139,6 @@ private:
137 void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, 139 void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst,
138 bool is_absolute); 140 bool is_absolute);
139 void AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute); 141 void AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute);
140 void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute);
141 AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); 142 AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst);
142 143
143 /// Return the branch target block id 144 /// Return the branch target block id
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp
index b270bbccd..8bfa64326 100644
--- a/src/shader_recompiler/frontend/maxwell/program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/program.cpp
@@ -8,67 +8,44 @@
8 8
9#include "shader_recompiler/frontend/ir/basic_block.h" 9#include "shader_recompiler/frontend/ir/basic_block.h"
10#include "shader_recompiler/frontend/ir/post_order.h" 10#include "shader_recompiler/frontend/ir/post_order.h"
11#include "shader_recompiler/frontend/ir/structured_control_flow.h"
12#include "shader_recompiler/frontend/maxwell/program.h" 11#include "shader_recompiler/frontend/maxwell/program.h"
12#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
13#include "shader_recompiler/frontend/maxwell/translate/translate.h" 13#include "shader_recompiler/frontend/maxwell/translate/translate.h"
14#include "shader_recompiler/ir_opt/passes.h" 14#include "shader_recompiler/ir_opt/passes.h"
15 15
16namespace Shader::Maxwell { 16namespace Shader::Maxwell {
17namespace { 17
18IR::BlockList TranslateCode(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 18static void RemoveUnreachableBlocks(IR::Program& program) {
19 Environment& env, Flow::Function& cfg_function) { 19 // Some blocks might be unreachable if a function call exists unconditionally
20 const size_t num_blocks{cfg_function.blocks.size()}; 20 // If this happens the number of blocks and post order blocks will mismatch
21 std::vector<IR::Block*> blocks(cfg_function.blocks.size()); 21 if (program.blocks.size() == program.post_order_blocks.size()) {
22 std::ranges::for_each(cfg_function.blocks, [&, i = size_t{0}](auto& cfg_block) mutable { 22 return;
23 const u32 begin{cfg_block.begin.Offset()}; 23 }
24 const u32 end{cfg_block.end.Offset()}; 24 const IR::BlockList& post_order{program.post_order_blocks};
25 blocks[i] = block_pool.Create(inst_pool, begin, end); 25 std::erase_if(program.blocks, [&](IR::Block* block) {
26 cfg_block.ir = blocks[i]; 26 return std::ranges::find(post_order, block) == post_order.end();
27 ++i;
28 });
29 std::ranges::for_each(cfg_function.blocks, [&, i = size_t{0}](auto& cfg_block) mutable {
30 IR::Block* const block{blocks[i]};
31 ++i;
32 if (cfg_block.end_class != Flow::EndClass::Branch) {
33 block->SetReturn();
34 } else if (cfg_block.cond == IR::Condition{true}) {
35 block->SetBranch(cfg_block.branch_true->ir);
36 } else if (cfg_block.cond == IR::Condition{false}) {
37 block->SetBranch(cfg_block.branch_false->ir);
38 } else {
39 block->SetBranches(cfg_block.cond, cfg_block.branch_true->ir,
40 cfg_block.branch_false->ir);
41 }
42 }); 27 });
43 return IR::VisitAST(inst_pool, block_pool, blocks,
44 [&](IR::Block* block) { Translate(env, block); });
45} 28}
46} // Anonymous namespace
47 29
48IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, 30IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
49 Environment& env, Flow::CFG& cfg) { 31 Environment& env, Flow::CFG& cfg) {
50 IR::Program program; 32 IR::Program program;
51 auto& functions{program.functions}; 33 program.blocks = VisitAST(inst_pool, block_pool, env, cfg);
52 functions.reserve(cfg.Functions().size()); 34 program.post_order_blocks = PostOrder(program.blocks);
53 for (Flow::Function& cfg_function : cfg.Functions()) { 35 RemoveUnreachableBlocks(program);
54 functions.push_back(IR::Function{ 36
55 .blocks{TranslateCode(inst_pool, block_pool, env, cfg_function)}, 37 // Replace instructions before the SSA rewrite
56 .post_order_blocks{},
57 });
58 }
59 Optimization::LowerFp16ToFp32(program); 38 Optimization::LowerFp16ToFp32(program);
60 for (IR::Function& function : functions) { 39
61 function.post_order_blocks = PostOrder(function.blocks); 40 Optimization::SsaRewritePass(program);
62 Optimization::SsaRewritePass(function.post_order_blocks); 41
63 }
64 Optimization::GlobalMemoryToStorageBufferPass(program); 42 Optimization::GlobalMemoryToStorageBufferPass(program);
65 Optimization::TexturePass(env, program); 43 Optimization::TexturePass(env, program);
66 for (IR::Function& function : functions) { 44
67 Optimization::PostOrderInvoke(Optimization::ConstantPropagationPass, function); 45 Optimization::ConstantPropagationPass(program);
68 Optimization::PostOrderInvoke(Optimization::DeadCodeEliminationPass, function); 46 Optimization::DeadCodeEliminationPass(program);
69 Optimization::IdentityRemovalPass(function); 47 Optimization::IdentityRemovalPass(program);
70 Optimization::VerificationPass(function); 48 Optimization::VerificationPass(program);
71 }
72 Optimization::CollectShaderInfoPass(program); 49 Optimization::CollectShaderInfoPass(program);
73 return program; 50 return program;
74} 51}
diff --git a/src/shader_recompiler/frontend/ir/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index bfba55a7e..5f5d9cf17 100644
--- a/src/shader_recompiler/frontend/ir/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -14,11 +14,14 @@
14 14
15#include <boost/intrusive/list.hpp> 15#include <boost/intrusive/list.hpp>
16 16
17#include "shader_recompiler/environment.h"
17#include "shader_recompiler/frontend/ir/basic_block.h" 18#include "shader_recompiler/frontend/ir/basic_block.h"
18#include "shader_recompiler/frontend/ir/ir_emitter.h" 19#include "shader_recompiler/frontend/ir/ir_emitter.h"
20#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
21#include "shader_recompiler/frontend/maxwell/translate/translate.h"
19#include "shader_recompiler/object_pool.h" 22#include "shader_recompiler/object_pool.h"
20 23
21namespace Shader::IR { 24namespace Shader::Maxwell {
22namespace { 25namespace {
23struct Statement; 26struct Statement;
24 27
@@ -79,7 +82,7 @@ struct Variable {};
79#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement 82#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
80#endif 83#endif
81struct Statement : ListBaseHook { 84struct Statement : ListBaseHook {
82 Statement(Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {} 85 Statement(IR::Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {}
83 Statement(Goto, Statement* cond_, Node label_, Statement* up_) 86 Statement(Goto, Statement* cond_, Node label_, Statement* up_)
84 : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {} 87 : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
85 Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {} 88 Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
@@ -91,7 +94,7 @@ struct Statement : ListBaseHook {
91 : cond{cond_}, up{up_}, type{StatementType::Break} {} 94 : cond{cond_}, up{up_}, type{StatementType::Break} {}
92 Statement(Return) : type{StatementType::Return} {} 95 Statement(Return) : type{StatementType::Return} {}
93 Statement(FunctionTag) : children{}, type{StatementType::Function} {} 96 Statement(FunctionTag) : children{}, type{StatementType::Function} {}
94 Statement(Identity, Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} 97 Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {}
95 Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} 98 Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {}
96 Statement(Or, Statement* op_a_, Statement* op_b_) 99 Statement(Or, Statement* op_a_, Statement* op_b_)
97 : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} 100 : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {}
@@ -106,10 +109,10 @@ struct Statement : ListBaseHook {
106 } 109 }
107 110
108 union { 111 union {
109 Block* code; 112 IR::Block* code;
110 Node label; 113 Node label;
111 Tree children; 114 Tree children;
112 Condition guest_cond; 115 IR::Condition guest_cond;
113 Statement* op; 116 Statement* op;
114 Statement* op_a; 117 Statement* op_a;
115 }; 118 };
@@ -269,9 +272,10 @@ bool SearchNode(const Tree& tree, ConstNode stmt, size_t& offset) {
269 272
270class GotoPass { 273class GotoPass {
271public: 274public:
272 explicit GotoPass(std::span<Block* const> blocks, ObjectPool<Statement>& stmt_pool) 275 explicit GotoPass(Flow::CFG& cfg, ObjectPool<IR::Inst>& inst_pool_,
273 : pool{stmt_pool} { 276 ObjectPool<IR::Block>& block_pool_, ObjectPool<Statement>& stmt_pool)
274 std::vector gotos{BuildUnorderedTreeGetGotos(blocks)}; 277 : inst_pool{inst_pool_}, block_pool{block_pool_}, pool{stmt_pool} {
278 std::vector gotos{BuildTree(cfg)};
275 for (const Node& goto_stmt : gotos | std::views::reverse) { 279 for (const Node& goto_stmt : gotos | std::views::reverse) {
276 RemoveGoto(goto_stmt); 280 RemoveGoto(goto_stmt);
277 } 281 }
@@ -316,18 +320,20 @@ private:
316 } 320 }
317 } 321 }
318 // TODO: Remove this 322 // TODO: Remove this
319 Node it{goto_stmt}; 323 {
320 bool sibling{false}; 324 Node it{goto_stmt};
321 do { 325 bool sibling{false};
322 sibling |= it == label_stmt; 326 do {
323 --it; 327 sibling |= it == label_stmt;
324 } while (it != goto_stmt->up->children.begin()); 328 --it;
325 while (it != goto_stmt->up->children.end()) { 329 } while (it != goto_stmt->up->children.begin());
326 sibling |= it == label_stmt; 330 while (it != goto_stmt->up->children.end()) {
327 ++it; 331 sibling |= it == label_stmt;
328 } 332 ++it;
329 if (!sibling) { 333 }
330 throw LogicError("Not siblings"); 334 if (!sibling) {
335 throw LogicError("Not siblings");
336 }
331 } 337 }
332 // goto_stmt and label_stmt are guaranteed to be siblings, eliminate 338 // goto_stmt and label_stmt are guaranteed to be siblings, eliminate
333 if (std::next(goto_stmt) == label_stmt) { 339 if (std::next(goto_stmt) == label_stmt) {
@@ -342,63 +348,84 @@ private:
342 } 348 }
343 } 349 }
344 350
345 std::vector<Node> BuildUnorderedTreeGetGotos(std::span<Block* const> blocks) { 351 std::vector<Node> BuildTree(Flow::CFG& cfg) {
346 // Assume all blocks have two branches 352 u32 label_id{0};
347 std::vector<Node> gotos; 353 std::vector<Node> gotos;
348 gotos.reserve(blocks.size() * 2); 354 Flow::Function& first_function{cfg.Functions().front()};
349 355 BuildTree(cfg, first_function, label_id, gotos, root_stmt.children.end(), std::nullopt);
350 const std::unordered_map labels_map{BuildLabels(blocks)};
351 Tree& root{root_stmt.children};
352 auto insert_point{root.begin()};
353 // Skip all goto variables zero-initialization
354 std::advance(insert_point, labels_map.size());
355
356 for (Block* const block : blocks) {
357 // Skip label
358 ++insert_point;
359 // Skip set variable
360 ++insert_point;
361 root.insert(insert_point, *pool.Create(block, &root_stmt));
362
363 if (block->IsTerminationBlock()) {
364 root.insert(insert_point, *pool.Create(Return{}));
365 continue;
366 }
367 const Condition cond{block->BranchCondition()};
368 Statement* const true_cond{pool.Create(Identity{}, Condition{true})};
369 if (cond == Condition{true} || cond == Condition{false}) {
370 const bool is_true{cond == Condition{true}};
371 const Block* const branch{is_true ? block->TrueBranch() : block->FalseBranch()};
372 const Node label{labels_map.at(branch)};
373 Statement* const goto_stmt{pool.Create(Goto{}, true_cond, label, &root_stmt)};
374 gotos.push_back(root.insert(insert_point, *goto_stmt));
375 } else {
376 Statement* const ident_cond{pool.Create(Identity{}, cond)};
377 const Node true_label{labels_map.at(block->TrueBranch())};
378 const Node false_label{labels_map.at(block->FalseBranch())};
379 Statement* goto_true{pool.Create(Goto{}, ident_cond, true_label, &root_stmt)};
380 Statement* goto_false{pool.Create(Goto{}, true_cond, false_label, &root_stmt)};
381 gotos.push_back(root.insert(insert_point, *goto_true));
382 gotos.push_back(root.insert(insert_point, *goto_false));
383 }
384 }
385 return gotos; 356 return gotos;
386 } 357 }
387 358
388 std::unordered_map<const Block*, Node> BuildLabels(std::span<Block* const> blocks) { 359 void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id,
389 // TODO: Consider storing labels intrusively inside the block 360 std::vector<Node>& gotos, Node function_insert_point,
390 std::unordered_map<const Block*, Node> labels_map; 361 std::optional<Node> return_label) {
362 Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false})};
391 Tree& root{root_stmt.children}; 363 Tree& root{root_stmt.children};
392 u32 label_id{0}; 364 std::unordered_map<Flow::Block*, Node> local_labels;
393 for (const Block* const block : blocks) { 365 local_labels.reserve(function.blocks.size());
366
367 for (Flow::Block& block : function.blocks) {
394 Statement* const label{pool.Create(Label{}, label_id, &root_stmt)}; 368 Statement* const label{pool.Create(Label{}, label_id, &root_stmt)};
395 labels_map.emplace(block, root.insert(root.end(), *label)); 369 const Node label_it{root.insert(function_insert_point, *label)};
396 Statement* const false_stmt{pool.Create(Identity{}, Condition{false})}; 370 local_labels.emplace(&block, label_it);
397 root.push_back(*pool.Create(SetVariable{}, label_id, false_stmt, &root_stmt));
398 root.push_front(*pool.Create(SetVariable{}, label_id, false_stmt, &root_stmt));
399 ++label_id; 371 ++label_id;
400 } 372 }
401 return labels_map; 373 for (Flow::Block& block : function.blocks) {
374 const Node label{local_labels.at(&block)};
375 // Insertion point
376 const Node ip{std::next(label)};
377
378 // Reset goto variables before the first block and after its respective label
379 const auto make_reset_variable{[&]() -> Statement& {
380 return *pool.Create(SetVariable{}, label->id, false_stmt, &root_stmt);
381 }};
382 root.push_front(make_reset_variable());
383 root.insert(ip, make_reset_variable());
384
385 const u32 begin_offset{block.begin.Offset()};
386 const u32 end_offset{block.end.Offset()};
387 IR::Block* const ir_block{block_pool.Create(inst_pool, begin_offset, end_offset)};
388 root.insert(ip, *pool.Create(ir_block, &root_stmt));
389
390 switch (block.end_class) {
391 case Flow::EndClass::Branch: {
392 Statement* const always_cond{pool.Create(Identity{}, IR::Condition{true})};
393 if (block.cond == IR::Condition{true}) {
394 const Node true_label{local_labels.at(block.branch_true)};
395 gotos.push_back(
396 root.insert(ip, *pool.Create(Goto{}, always_cond, true_label, &root_stmt)));
397 } else if (block.cond == IR::Condition{false}) {
398 const Node false_label{local_labels.at(block.branch_false)};
399 gotos.push_back(root.insert(
400 ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
401 } else {
402 const Node true_label{local_labels.at(block.branch_true)};
403 const Node false_label{local_labels.at(block.branch_false)};
404 Statement* const true_cond{pool.Create(Identity{}, block.cond)};
405 gotos.push_back(
406 root.insert(ip, *pool.Create(Goto{}, true_cond, true_label, &root_stmt)));
407 gotos.push_back(root.insert(
408 ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
409 }
410 break;
411 }
412 case Flow::EndClass::Call: {
413 Flow::Function& call{cfg.Functions()[block.function_call]};
414 const Node call_return_label{local_labels.at(block.return_block)};
415 BuildTree(cfg, call, label_id, gotos, ip, call_return_label);
416 break;
417 }
418 case Flow::EndClass::Exit:
419 root.insert(ip, *pool.Create(Return{}));
420 break;
421 case Flow::EndClass::Return: {
422 Statement* const always_cond{pool.Create(Identity{}, block.cond)};
423 auto goto_stmt{pool.Create(Goto{}, always_cond, return_label.value(), &root_stmt)};
424 gotos.push_back(root.insert(ip, *goto_stmt));
425 break;
426 }
427 }
428 }
402 } 429 }
403 430
404 void UpdateTreeUp(Statement* tree) { 431 void UpdateTreeUp(Statement* tree) {
@@ -556,11 +583,13 @@ private:
556 return offset; 583 return offset;
557 } 584 }
558 585
586 ObjectPool<IR::Inst>& inst_pool;
587 ObjectPool<IR::Block>& block_pool;
559 ObjectPool<Statement>& pool; 588 ObjectPool<Statement>& pool;
560 Statement root_stmt{FunctionTag{}}; 589 Statement root_stmt{FunctionTag{}};
561}; 590};
562 591
563Block* TryFindForwardBlock(const Statement& stmt) { 592IR::Block* TryFindForwardBlock(const Statement& stmt) {
564 const Tree& tree{stmt.up->children}; 593 const Tree& tree{stmt.up->children};
565 const ConstNode end{tree.cend()}; 594 const ConstNode end{tree.cend()};
566 ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))}; 595 ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))};
@@ -573,12 +602,12 @@ Block* TryFindForwardBlock(const Statement& stmt) {
573 return nullptr; 602 return nullptr;
574} 603}
575 604
576[[nodiscard]] U1 VisitExpr(IREmitter& ir, const Statement& stmt) { 605[[nodiscard]] IR::U1 VisitExpr(IR::IREmitter& ir, const Statement& stmt) {
577 switch (stmt.type) { 606 switch (stmt.type) {
578 case StatementType::Identity: 607 case StatementType::Identity:
579 return ir.Condition(stmt.guest_cond); 608 return ir.Condition(stmt.guest_cond);
580 case StatementType::Not: 609 case StatementType::Not:
581 return ir.LogicalNot(U1{VisitExpr(ir, *stmt.op)}); 610 return ir.LogicalNot(IR::U1{VisitExpr(ir, *stmt.op)});
582 case StatementType::Or: 611 case StatementType::Or:
583 return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); 612 return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b));
584 case StatementType::Variable: 613 case StatementType::Variable:
@@ -590,18 +619,18 @@ Block* TryFindForwardBlock(const Statement& stmt) {
590 619
591class TranslatePass { 620class TranslatePass {
592public: 621public:
593 TranslatePass(ObjectPool<Inst>& inst_pool_, ObjectPool<Block>& block_pool_, 622 TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
594 ObjectPool<Statement>& stmt_pool_, Statement& root_stmt, 623 ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
595 const std::function<void(IR::Block*)>& func_, BlockList& block_list_) 624 IR::BlockList& block_list_)
596 : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, func{func_}, 625 : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
597 block_list{block_list_} { 626 block_list{block_list_} {
598 Visit(root_stmt, nullptr, nullptr); 627 Visit(root_stmt, nullptr, nullptr);
599 } 628 }
600 629
601private: 630private:
602 void Visit(Statement& parent, Block* continue_block, Block* break_block) { 631 void Visit(Statement& parent, IR::Block* continue_block, IR::Block* break_block) {
603 Tree& tree{parent.children}; 632 Tree& tree{parent.children};
604 Block* current_block{nullptr}; 633 IR::Block* current_block{nullptr};
605 634
606 for (auto it = tree.begin(); it != tree.end(); ++it) { 635 for (auto it = tree.begin(); it != tree.end(); ++it) {
607 Statement& stmt{*it}; 636 Statement& stmt{*it};
@@ -611,11 +640,10 @@ private:
611 break; 640 break;
612 case StatementType::Code: { 641 case StatementType::Code: {
613 if (current_block && current_block != stmt.code) { 642 if (current_block && current_block != stmt.code) {
614 IREmitter ir{*current_block}; 643 IR::IREmitter{*current_block}.Branch(stmt.code);
615 ir.Branch(stmt.code);
616 } 644 }
617 current_block = stmt.code; 645 current_block = stmt.code;
618 func(stmt.code); 646 Translate(env, stmt.code);
619 block_list.push_back(stmt.code); 647 block_list.push_back(stmt.code);
620 break; 648 break;
621 } 649 }
@@ -623,7 +651,7 @@ private:
623 if (!current_block) { 651 if (!current_block) {
624 current_block = MergeBlock(parent, stmt); 652 current_block = MergeBlock(parent, stmt);
625 } 653 }
626 IREmitter ir{*current_block}; 654 IR::IREmitter ir{*current_block};
627 ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); 655 ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op));
628 break; 656 break;
629 } 657 }
@@ -632,16 +660,16 @@ private:
632 current_block = block_pool.Create(inst_pool); 660 current_block = block_pool.Create(inst_pool);
633 block_list.push_back(current_block); 661 block_list.push_back(current_block);
634 } 662 }
635 Block* const merge_block{MergeBlock(parent, stmt)}; 663 IR::Block* const merge_block{MergeBlock(parent, stmt)};
636 664
637 // Visit children 665 // Visit children
638 const size_t first_block_index{block_list.size()}; 666 const size_t first_block_index{block_list.size()};
639 Visit(stmt, merge_block, break_block); 667 Visit(stmt, merge_block, break_block);
640 668
641 // Implement if header block 669 // Implement if header block
642 Block* const first_if_block{block_list.at(first_block_index)}; 670 IR::Block* const first_if_block{block_list.at(first_block_index)};
643 IREmitter ir{*current_block}; 671 IR::IREmitter ir{*current_block};
644 const U1 cond{VisitExpr(ir, *stmt.cond)}; 672 const IR::U1 cond{VisitExpr(ir, *stmt.cond)};
645 ir.SelectionMerge(merge_block); 673 ir.SelectionMerge(merge_block);
646 ir.BranchConditional(cond, first_if_block, merge_block); 674 ir.BranchConditional(cond, first_if_block, merge_block);
647 675
@@ -649,14 +677,14 @@ private:
649 break; 677 break;
650 } 678 }
651 case StatementType::Loop: { 679 case StatementType::Loop: {
652 Block* const loop_header_block{block_pool.Create(inst_pool)}; 680 IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
653 if (current_block) { 681 if (current_block) {
654 IREmitter{*current_block}.Branch(loop_header_block); 682 IR::IREmitter{*current_block}.Branch(loop_header_block);
655 } 683 }
656 block_list.push_back(loop_header_block); 684 block_list.push_back(loop_header_block);
657 685
658 Block* const new_continue_block{block_pool.Create(inst_pool)}; 686 IR::Block* const new_continue_block{block_pool.Create(inst_pool)};
659 Block* const merge_block{MergeBlock(parent, stmt)}; 687 IR::Block* const merge_block{MergeBlock(parent, stmt)};
660 688
661 // Visit children 689 // Visit children
662 const size_t first_block_index{block_list.size()}; 690 const size_t first_block_index{block_list.size()};
@@ -666,14 +694,14 @@ private:
666 block_list.push_back(new_continue_block); 694 block_list.push_back(new_continue_block);
667 695
668 // Implement loop header block 696 // Implement loop header block
669 Block* const first_loop_block{block_list.at(first_block_index)}; 697 IR::Block* const first_loop_block{block_list.at(first_block_index)};
670 IREmitter ir{*loop_header_block}; 698 IR::IREmitter ir{*loop_header_block};
671 ir.LoopMerge(merge_block, new_continue_block); 699 ir.LoopMerge(merge_block, new_continue_block);
672 ir.Branch(first_loop_block); 700 ir.Branch(first_loop_block);
673 701
674 // Implement continue block 702 // Implement continue block
675 IREmitter continue_ir{*new_continue_block}; 703 IR::IREmitter continue_ir{*new_continue_block};
676 const U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)}; 704 const IR::U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)};
677 continue_ir.BranchConditional(continue_cond, ir.block, merge_block); 705 continue_ir.BranchConditional(continue_cond, ir.block, merge_block);
678 706
679 current_block = merge_block; 707 current_block = merge_block;
@@ -684,9 +712,9 @@ private:
684 current_block = block_pool.Create(inst_pool); 712 current_block = block_pool.Create(inst_pool);
685 block_list.push_back(current_block); 713 block_list.push_back(current_block);
686 } 714 }
687 Block* const skip_block{MergeBlock(parent, stmt)}; 715 IR::Block* const skip_block{MergeBlock(parent, stmt)};
688 716
689 IREmitter ir{*current_block}; 717 IR::IREmitter ir{*current_block};
690 ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block); 718 ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block);
691 719
692 current_block = skip_block; 720 current_block = skip_block;
@@ -697,7 +725,7 @@ private:
697 current_block = block_pool.Create(inst_pool); 725 current_block = block_pool.Create(inst_pool);
698 block_list.push_back(current_block); 726 block_list.push_back(current_block);
699 } 727 }
700 IREmitter{*current_block}.Return(); 728 IR::IREmitter{*current_block}.Return();
701 current_block = nullptr; 729 current_block = nullptr;
702 break; 730 break;
703 } 731 }
@@ -706,39 +734,37 @@ private:
706 } 734 }
707 } 735 }
708 if (current_block && continue_block) { 736 if (current_block && continue_block) {
709 IREmitter ir{*current_block}; 737 IR::IREmitter{*current_block}.Branch(continue_block);
710 ir.Branch(continue_block);
711 } 738 }
712 } 739 }
713 740
714 Block* MergeBlock(Statement& parent, Statement& stmt) { 741 IR::Block* MergeBlock(Statement& parent, Statement& stmt) {
715 if (Block* const block{TryFindForwardBlock(stmt)}) { 742 if (IR::Block* const block{TryFindForwardBlock(stmt)}) {
716 return block; 743 return block;
717 } 744 }
718 // Create a merge block we can visit later 745 // Create a merge block we can visit later
719 Block* const block{block_pool.Create(inst_pool)}; 746 IR::Block* const block{block_pool.Create(inst_pool)};
720 Statement* const merge_stmt{stmt_pool.Create(block, &parent)}; 747 Statement* const merge_stmt{stmt_pool.Create(block, &parent)};
721 parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt); 748 parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
722 return block; 749 return block;
723 } 750 }
724 751
725 ObjectPool<Statement>& stmt_pool; 752 ObjectPool<Statement>& stmt_pool;
726 ObjectPool<Inst>& inst_pool; 753 ObjectPool<IR::Inst>& inst_pool;
727 ObjectPool<Block>& block_pool; 754 ObjectPool<IR::Block>& block_pool;
728 const std::function<void(IR::Block*)>& func; 755 Environment& env;
729 BlockList& block_list; 756 IR::BlockList& block_list;
730}; 757};
731} // Anonymous namespace 758} // Anonymous namespace
732 759
733BlockList VisitAST(ObjectPool<Inst>& inst_pool, ObjectPool<Block>& block_pool, 760IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
734 std::span<Block* const> unordered_blocks, 761 Environment& env, Flow::CFG& cfg) {
735 const std::function<void(Block*)>& func) {
736 ObjectPool<Statement> stmt_pool{64}; 762 ObjectPool<Statement> stmt_pool{64};
737 GotoPass goto_pass{unordered_blocks, stmt_pool}; 763 GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool};
738 BlockList block_list; 764 Statement& root{goto_pass.RootStatement()};
739 TranslatePass translate_pass{inst_pool, block_pool, stmt_pool, goto_pass.RootStatement(), 765 IR::BlockList block_list;
740 func, block_list}; 766 TranslatePass{inst_pool, block_pool, stmt_pool, env, root, block_list};
741 return block_list; 767 return block_list;
742} 768}
743 769
744} // namespace Shader::IR 770} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/ir/structured_control_flow.h b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
index a574c24f7..e4797291e 100644
--- a/src/shader_recompiler/frontend/ir/structured_control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
@@ -9,14 +9,16 @@
9 9
10#include <boost/intrusive/list.hpp> 10#include <boost/intrusive/list.hpp>
11 11
12#include "shader_recompiler/environment.h"
12#include "shader_recompiler/frontend/ir/basic_block.h" 13#include "shader_recompiler/frontend/ir/basic_block.h"
13#include "shader_recompiler/frontend/ir/microinstruction.h" 14#include "shader_recompiler/frontend/ir/microinstruction.h"
15#include "shader_recompiler/frontend/maxwell/control_flow.h"
14#include "shader_recompiler/object_pool.h" 16#include "shader_recompiler/object_pool.h"
15 17
16namespace Shader::IR { 18namespace Shader::Maxwell {
17 19
18[[nodiscard]] BlockList VisitAST(ObjectPool<Inst>& inst_pool, ObjectPool<Block>& block_pool, 20[[nodiscard]] IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool,
19 std::span<Block* const> unordered_blocks, 21 ObjectPool<IR::Block>& block_pool, Environment& env,
20 const std::function<void(Block*)>& func); 22 Flow::CFG& cfg);
21 23
22} // namespace Shader::IR 24} // 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 c6253c40c..45d6f5e06 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
@@ -62,7 +62,7 @@ public:
62 void BRA(u64 insn); 62 void BRA(u64 insn);
63 void BRK(u64 insn); 63 void BRK(u64 insn);
64 void BRX(u64 insn); 64 void BRX(u64 insn);
65 void CAL(u64 insn); 65 void CAL();
66 void CCTL(u64 insn); 66 void CCTL(u64 insn);
67 void CCTLL(u64 insn); 67 void CCTLL(u64 insn);
68 void CONT(u64 insn); 68 void CONT(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 01ecbb4cc..92da5c7e8 100644
--- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -65,8 +65,8 @@ void TranslatorVisitor::BRX(u64) {
65 ThrowNotImplemented(Opcode::BRX); 65 ThrowNotImplemented(Opcode::BRX);
66} 66}
67 67
68void TranslatorVisitor::CAL(u64) { 68void TranslatorVisitor::CAL() {
69 ThrowNotImplemented(Opcode::CAL); 69 // CAL is a no-op
70} 70}
71 71
72void TranslatorVisitor::CCTL(u64) { 72void TranslatorVisitor::CCTL(u64) {