summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/maxwell
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/frontend/maxwell')
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp531
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h137
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.cpp149
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.h14
-rw-r--r--src/shader_recompiler/frontend/maxwell/instruction.h62
-rw-r--r--src/shader_recompiler/frontend/maxwell/location.h106
-rw-r--r--src/shader_recompiler/frontend/maxwell/maxwell.inc285
-rw-r--r--src/shader_recompiler/frontend/maxwell/opcode.cpp26
-rw-r--r--src/shader_recompiler/frontend/maxwell/opcode.h30
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.cpp69
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.h39
-rw-r--r--src/shader_recompiler/frontend/maxwell/termination_code.cpp79
-rw-r--r--src/shader_recompiler/frontend/maxwell/termination_code.h16
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp15
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp133
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp71
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp79
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.h316
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp92
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp90
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp1105
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp45
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/translate.cpp50
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/translate.h16
24 files changed, 3555 insertions, 0 deletions
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
new file mode 100644
index 000000000..fc4dba826
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
@@ -0,0 +1,531 @@
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 <algorithm>
6#include <array>
7#include <optional>
8#include <ranges>
9#include <string>
10#include <utility>
11
12#include <fmt/format.h>
13
14#include "shader_recompiler/exception.h"
15#include "shader_recompiler/frontend/maxwell/control_flow.h"
16#include "shader_recompiler/frontend/maxwell/decode.h"
17#include "shader_recompiler/frontend/maxwell/location.h"
18
19namespace Shader::Maxwell::Flow {
20
21static u32 BranchOffset(Location pc, Instruction inst) {
22 return pc.Offset() + inst.branch.Offset() + 8;
23}
24
25static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) {
26 if (pc <= block.begin || pc >= block.end) {
27 throw InvalidArgument("Invalid address to split={}", pc);
28 }
29 return {
30 Block{
31 .begin{block.begin},
32 .end{pc},
33 .end_class{EndClass::Branch},
34 .id{block.id},
35 .stack{block.stack},
36 .cond{true},
37 .branch_true{new_id},
38 .branch_false{UNREACHABLE_BLOCK_ID},
39 },
40 Block{
41 .begin{pc},
42 .end{block.end},
43 .end_class{block.end_class},
44 .id{new_id},
45 .stack{std::move(block.stack)},
46 .cond{block.cond},
47 .branch_true{block.branch_true},
48 .branch_false{block.branch_false},
49 },
50 };
51}
52
53static Token OpcodeToken(Opcode opcode) {
54 switch (opcode) {
55 case Opcode::PBK:
56 case Opcode::BRK:
57 return Token::PBK;
58 case Opcode::PCNT:
59 case Opcode::CONT:
60 return Token::PBK;
61 case Opcode::PEXIT:
62 case Opcode::EXIT:
63 return Token::PEXIT;
64 case Opcode::PLONGJMP:
65 case Opcode::LONGJMP:
66 return Token::PLONGJMP;
67 case Opcode::PRET:
68 case Opcode::RET:
69 case Opcode::CAL:
70 return Token::PRET;
71 case Opcode::SSY:
72 case Opcode::SYNC:
73 return Token::SSY;
74 default:
75 throw InvalidArgument("{}", opcode);
76 }
77}
78
79static bool IsAbsoluteJump(Opcode opcode) {
80 switch (opcode) {
81 case Opcode::JCAL:
82 case Opcode::JMP:
83 case Opcode::JMX:
84 return true;
85 default:
86 return false;
87 }
88}
89
90static bool HasFlowTest(Opcode opcode) {
91 switch (opcode) {
92 case Opcode::BRA:
93 case Opcode::BRX:
94 case Opcode::EXIT:
95 case Opcode::JMP:
96 case Opcode::JMX:
97 case Opcode::BRK:
98 case Opcode::CONT:
99 case Opcode::LONGJMP:
100 case Opcode::RET:
101 case Opcode::SYNC:
102 return true;
103 case Opcode::CAL:
104 case Opcode::JCAL:
105 return false;
106 default:
107 throw InvalidArgument("Invalid branch {}", opcode);
108 }
109}
110
111static std::string Name(const Block& block) {
112 if (block.begin.IsVirtual()) {
113 return fmt::format("\"Virtual {}\"", block.id);
114 } else {
115 return fmt::format("\"{}\"", block.begin);
116 }
117}
118
119void Stack::Push(Token token, Location target) {
120 entries.push_back({
121 .token{token},
122 .target{target},
123 });
124}
125
126std::pair<Location, Stack> Stack::Pop(Token token) const {
127 const std::optional<Location> pc{Peek(token)};
128 if (!pc) {
129 throw LogicError("Token could not be found");
130 }
131 return {*pc, Remove(token)};
132}
133
134std::optional<Location> Stack::Peek(Token token) const {
135 const auto reverse_entries{entries | std::views::reverse};
136 const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)};
137 if (it == reverse_entries.end()) {
138 return std::nullopt;
139 }
140 return it->target;
141}
142
143Stack Stack::Remove(Token token) const {
144 const auto reverse_entries{entries | std::views::reverse};
145 const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)};
146 const auto pos{std::distance(reverse_entries.begin(), it)};
147 Stack result;
148 result.entries.insert(result.entries.end(), entries.begin(), entries.end() - pos - 1);
149 return result;
150}
151
152bool Block::Contains(Location pc) const noexcept {
153 return pc >= begin && pc < end;
154}
155
156Function::Function(Location start_address)
157 : entrypoint{start_address}, labels{Label{
158 .address{start_address},
159 .block_id{0},
160 .stack{},
161 }} {}
162
163CFG::CFG(Environment& env_, Location start_address) : env{env_} {
164 functions.emplace_back(start_address);
165 for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) {
166 while (!functions[function_id].labels.empty()) {
167 Function& function{functions[function_id]};
168 Label label{function.labels.back()};
169 function.labels.pop_back();
170 AnalyzeLabel(function_id, label);
171 }
172 }
173}
174
175void CFG::AnalyzeLabel(FunctionId function_id, Label& label) {
176 if (InspectVisitedBlocks(function_id, label)) {
177 // Label address has been visited
178 return;
179 }
180 // Try to find the next block
181 Function* function{&functions[function_id]};
182 Location pc{label.address};
183 const auto next{std::upper_bound(function->blocks.begin(), function->blocks.end(), pc,
184 [function](Location pc, u32 block_index) {
185 return pc < function->blocks_data[block_index].begin;
186 })};
187 const auto next_index{std::distance(function->blocks.begin(), next)};
188 const bool is_last{next == function->blocks.end()};
189 Location next_pc;
190 BlockId next_id{UNREACHABLE_BLOCK_ID};
191 if (!is_last) {
192 next_pc = function->blocks_data[*next].begin;
193 next_id = function->blocks_data[*next].id;
194 }
195 // Insert before the next block
196 Block block{
197 .begin{pc},
198 .end{pc},
199 .end_class{EndClass::Branch},
200 .id{label.block_id},
201 .stack{std::move(label.stack)},
202 .cond{true},
203 .branch_true{UNREACHABLE_BLOCK_ID},
204 .branch_false{UNREACHABLE_BLOCK_ID},
205 };
206 // Analyze instructions until it reaches an already visited block or there's a branch
207 bool is_branch{false};
208 while (is_last || pc < next_pc) {
209 is_branch = AnalyzeInst(block, function_id, pc) == AnalysisState::Branch;
210 if (is_branch) {
211 break;
212 }
213 ++pc;
214 }
215 if (!is_branch) {
216 // If the block finished without a branch,
217 // it means that the next instruction is already visited, jump to it
218 block.end = pc;
219 block.cond = true;
220 block.branch_true = next_id;
221 block.branch_false = UNREACHABLE_BLOCK_ID;
222 }
223 // Function's pointer might be invalid, resolve it again
224 function = &functions[function_id];
225 const u32 new_block_index = static_cast<u32>(function->blocks_data.size());
226 function->blocks.insert(function->blocks.begin() + next_index, new_block_index);
227 function->blocks_data.push_back(std::move(block));
228}
229
230bool CFG::InspectVisitedBlocks(FunctionId function_id, const Label& label) {
231 const Location pc{label.address};
232 Function& function{functions[function_id]};
233 const auto it{std::ranges::find_if(function.blocks, [&function, pc](u32 block_index) {
234 return function.blocks_data[block_index].Contains(pc);
235 })};
236 if (it == function.blocks.end()) {
237 // Address has not been visited
238 return false;
239 }
240 Block& block{function.blocks_data[*it]};
241 if (block.begin == pc) {
242 throw LogicError("Dangling branch");
243 }
244 const u32 first_index{*it};
245 const u32 second_index{static_cast<u32>(function.blocks_data.size())};
246 const std::array new_indices{first_index, second_index};
247 std::array split_blocks{Split(std::move(block), pc, label.block_id)};
248 function.blocks_data[*it] = std::move(split_blocks[0]);
249 function.blocks_data.push_back(std::move(split_blocks[1]));
250 function.blocks.insert(function.blocks.erase(it), new_indices.begin(), new_indices.end());
251 return true;
252}
253
254CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Location pc) {
255 const Instruction inst{env.ReadInstruction(pc.Offset())};
256 const Opcode opcode{Decode(inst.raw)};
257 switch (opcode) {
258 case Opcode::BRA:
259 case Opcode::BRX:
260 case Opcode::JMP:
261 case Opcode::JMX:
262 case Opcode::RET:
263 if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) {
264 return AnalysisState::Continue;
265 }
266 switch (opcode) {
267 case Opcode::BRA:
268 case Opcode::JMP:
269 AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode));
270 break;
271 case Opcode::BRX:
272 case Opcode::JMX:
273 AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode));
274 break;
275 case Opcode::RET:
276 block.end_class = EndClass::Return;
277 break;
278 default:
279 break;
280 }
281 block.end = pc;
282 return AnalysisState::Branch;
283 case Opcode::BRK:
284 case Opcode::CONT:
285 case Opcode::LONGJMP:
286 case Opcode::SYNC: {
287 if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) {
288 return AnalysisState::Continue;
289 }
290 const auto [stack_pc, new_stack]{block.stack.Pop(OpcodeToken(opcode))};
291 block.branch_true = AddLabel(block, new_stack, stack_pc, function_id);
292 block.end = pc;
293 return AnalysisState::Branch;
294 }
295 case Opcode::PBK:
296 case Opcode::PCNT:
297 case Opcode::PEXIT:
298 case Opcode::PLONGJMP:
299 case Opcode::SSY:
300 block.stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst));
301 return AnalysisState::Continue;
302 case Opcode::EXIT:
303 return AnalyzeEXIT(block, function_id, pc, inst);
304 case Opcode::PRET:
305 throw NotImplementedException("PRET flow analysis");
306 case Opcode::CAL:
307 case Opcode::JCAL: {
308 const bool is_absolute{IsAbsoluteJump(opcode)};
309 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
310 // 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
312 if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) {
313 functions.push_back(cal_pc);
314 }
315 // Handle CAL like a regular instruction
316 break;
317 }
318 default:
319 break;
320 }
321 const Predicate pred{inst.Pred()};
322 if (pred == Predicate{true} || pred == Predicate{false}) {
323 return AnalysisState::Continue;
324 }
325 const IR::Condition cond{static_cast<IR::Pred>(pred.index), pred.negated};
326 AnalyzeCondInst(block, function_id, pc, EndClass::Branch, cond);
327 return AnalysisState::Branch;
328}
329
330void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc,
331 EndClass insn_end_class, IR::Condition cond) {
332 if (block.begin != pc) {
333 // If the block doesn't start in the conditional instruction
334 // mark it as a label to visit it later
335 block.end = pc;
336 block.cond = true;
337 block.branch_true = AddLabel(block, block.stack, pc, function_id);
338 block.branch_false = UNREACHABLE_BLOCK_ID;
339 return;
340 }
341 // Impersonate the visited block with a virtual block
342 // Jump from this virtual to the real conditional instruction and the next instruction
343 Function& function{functions[function_id]};
344 const BlockId conditional_block_id{++function.current_block_id};
345 function.blocks.push_back(static_cast<u32>(function.blocks_data.size()));
346 Block& virtual_block{function.blocks_data.emplace_back(Block{
347 .begin{}, // Virtual block
348 .end{},
349 .end_class{EndClass::Branch},
350 .id{block.id}, // Impersonating
351 .stack{block.stack},
352 .cond{cond},
353 .branch_true{conditional_block_id},
354 .branch_false{UNREACHABLE_BLOCK_ID},
355 })};
356 // Set the end properties of the conditional instruction and give it a new identity
357 Block& conditional_block{block};
358 conditional_block.end = pc;
359 conditional_block.end_class = insn_end_class;
360 conditional_block.id = conditional_block_id;
361 // Add a label to the instruction after the conditional instruction
362 const BlockId endif_block_id{AddLabel(conditional_block, block.stack, pc + 1, function_id)};
363 // Branch to the next instruction from the virtual block
364 virtual_block.branch_false = endif_block_id;
365 // And branch to it from the conditional instruction if it is a branch
366 if (insn_end_class == EndClass::Branch) {
367 conditional_block.cond = true;
368 conditional_block.branch_true = endif_block_id;
369 conditional_block.branch_false = UNREACHABLE_BLOCK_ID;
370 }
371}
372
373bool CFG::AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst,
374 Opcode opcode) {
375 if (inst.branch.is_cbuf) {
376 throw NotImplementedException("Branch with constant buffer offset");
377 }
378 const Predicate pred{inst.Pred()};
379 if (pred == Predicate{false}) {
380 return false;
381 }
382 const bool has_flow_test{HasFlowTest(opcode)};
383 const IR::FlowTest flow_test{has_flow_test ? inst.branch.flow_test.Value() : IR::FlowTest::T};
384 if (pred != Predicate{true} || flow_test != IR::FlowTest::T) {
385 block.cond = IR::Condition(flow_test, static_cast<IR::Pred>(pred.index), pred.negated);
386 block.branch_false = AddLabel(block, block.stack, pc + 1, function_id);
387 } else {
388 block.cond = true;
389 }
390 return true;
391}
392
393void CFG::AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
394 bool is_absolute) {
395 const Location bra_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
396 block.branch_true = AddLabel(block, block.stack, bra_pc, function_id);
397}
398
399void CFG::AnalyzeBRX(Block&, Location, Instruction, bool is_absolute) {
400 throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX");
401}
402
403void CFG::AnalyzeCAL(Location pc, Instruction inst, bool is_absolute) {
404 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
405 // Technically CAL pushes into PRET, but that's implicit in the function call for us
406 // Insert the function to the function list if it doesn't exist
407 const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)};
408 if (it == functions.end()) {
409 functions.emplace_back(cal_pc);
410 }
411}
412
413CFG::AnalysisState CFG::AnalyzeEXIT(Block& block, FunctionId function_id, Location pc,
414 Instruction inst) {
415 const IR::FlowTest flow_test{inst.branch.flow_test};
416 const Predicate pred{inst.Pred()};
417 if (pred == Predicate{false} || flow_test == IR::FlowTest::F) {
418 // EXIT will never be taken
419 return AnalysisState::Continue;
420 }
421 if (pred != Predicate{true} || flow_test != IR::FlowTest::T) {
422 if (block.stack.Peek(Token::PEXIT).has_value()) {
423 throw NotImplementedException("Conditional EXIT with PEXIT token");
424 }
425 const IR::Condition cond{flow_test, static_cast<IR::Pred>(pred.index), pred.negated};
426 AnalyzeCondInst(block, function_id, pc, EndClass::Exit, cond);
427 return AnalysisState::Branch;
428 }
429 if (const std::optional<Location> exit_pc{block.stack.Peek(Token::PEXIT)}) {
430 const Stack popped_stack{block.stack.Remove(Token::PEXIT)};
431 block.cond = true;
432 block.branch_true = AddLabel(block, popped_stack, *exit_pc, function_id);
433 block.branch_false = UNREACHABLE_BLOCK_ID;
434 return AnalysisState::Branch;
435 }
436 block.end = pc;
437 block.end_class = EndClass::Exit;
438 return AnalysisState::Branch;
439}
440
441BlockId CFG::AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id) {
442 Function& function{functions[function_id]};
443 if (block.begin == pc) {
444 return block.id;
445 }
446 const auto target{std::ranges::find(function.blocks_data, pc, &Block::begin)};
447 if (target != function.blocks_data.end()) {
448 return target->id;
449 }
450 const BlockId block_id{++function.current_block_id};
451 function.labels.push_back(Label{
452 .address{pc},
453 .block_id{block_id},
454 .stack{std::move(stack)},
455 });
456 return block_id;
457}
458
459std::string CFG::Dot() const {
460 int node_uid{0};
461
462 std::string dot{"digraph shader {\n"};
463 for (const Function& function : functions) {
464 dot += fmt::format("\tsubgraph cluster_{} {{\n", function.entrypoint);
465 dot += fmt::format("\t\tnode [style=filled];\n");
466 for (const u32 block_index : function.blocks) {
467 const Block& block{function.blocks_data[block_index]};
468 const std::string name{Name(block)};
469 const auto add_branch = [&](BlockId branch_id, bool add_label) {
470 const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)};
471 dot += fmt::format("\t\t{}->", name);
472 if (it == function.blocks_data.end()) {
473 dot += fmt::format("\"Unknown label {}\"", branch_id);
474 } else {
475 dot += Name(*it);
476 };
477 if (add_label && block.cond != true && block.cond != false) {
478 dot += fmt::format(" [label=\"{}\"]", block.cond);
479 }
480 dot += '\n';
481 };
482 dot += fmt::format("\t\t{};\n", name);
483 switch (block.end_class) {
484 case EndClass::Branch:
485 if (block.cond != false) {
486 add_branch(block.branch_true, true);
487 }
488 if (block.cond != true) {
489 add_branch(block.branch_false, false);
490 }
491 break;
492 case EndClass::Exit:
493 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
494 dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n",
495 node_uid);
496 ++node_uid;
497 break;
498 case EndClass::Return:
499 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
500 dot += fmt::format("\t\tN{} [label=\"Return\"][shape=square][style=stripped];\n",
501 node_uid);
502 ++node_uid;
503 break;
504 case EndClass::Unreachable:
505 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
506 dot += fmt::format(
507 "\t\tN{} [label=\"Unreachable\"][shape=square][style=stripped];\n", node_uid);
508 ++node_uid;
509 break;
510 }
511 }
512 if (function.entrypoint == 8) {
513 dot += fmt::format("\t\tlabel = \"main\";\n");
514 } else {
515 dot += fmt::format("\t\tlabel = \"Function {}\";\n", function.entrypoint);
516 }
517 dot += "\t}\n";
518 }
519 if (!functions.empty()) {
520 if (functions.front().blocks.empty()) {
521 dot += "Start;\n";
522 } else {
523 dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front()));
524 }
525 dot += fmt::format("\tStart [shape=diamond];\n");
526 }
527 dot += "}\n";
528 return dot;
529}
530
531} // namespace Shader::Maxwell::Flow
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
new file mode 100644
index 000000000..b2ab0cdc3
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -0,0 +1,137 @@
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 <compare>
8#include <optional>
9#include <span>
10#include <string>
11#include <vector>
12
13#include <boost/container/small_vector.hpp>
14
15#include "shader_recompiler/environment.h"
16#include "shader_recompiler/frontend/ir/condition.h"
17#include "shader_recompiler/frontend/maxwell/instruction.h"
18#include "shader_recompiler/frontend/maxwell/location.h"
19#include "shader_recompiler/frontend/maxwell/opcode.h"
20
21namespace Shader::Maxwell::Flow {
22
23using BlockId = u32;
24using FunctionId = size_t;
25
26constexpr BlockId UNREACHABLE_BLOCK_ID{static_cast<u32>(-1)};
27
28enum class EndClass {
29 Branch,
30 Exit,
31 Return,
32 Unreachable,
33};
34
35enum class Token {
36 SSY,
37 PBK,
38 PEXIT,
39 PRET,
40 PCNT,
41 PLONGJMP,
42};
43
44struct StackEntry {
45 auto operator<=>(const StackEntry&) const noexcept = default;
46
47 Token token;
48 Location target;
49};
50
51class Stack {
52public:
53 void Push(Token token, Location target);
54 [[nodiscard]] std::pair<Location, Stack> Pop(Token token) const;
55 [[nodiscard]] std::optional<Location> Peek(Token token) const;
56 [[nodiscard]] Stack Remove(Token token) const;
57
58private:
59 boost::container::small_vector<StackEntry, 3> entries;
60};
61
62struct Block {
63 [[nodiscard]] bool Contains(Location pc) const noexcept;
64
65 Location begin;
66 Location end;
67 EndClass end_class;
68 BlockId id;
69 Stack stack;
70 IR::Condition cond;
71 BlockId branch_true;
72 BlockId branch_false;
73};
74
75struct Label {
76 Location address;
77 BlockId block_id;
78 Stack stack;
79};
80
81struct Function {
82 Function(Location start_address);
83
84 Location entrypoint;
85 BlockId current_block_id{0};
86 boost::container::small_vector<Label, 16> labels;
87 boost::container::small_vector<u32, 0x130> blocks;
88 boost::container::small_vector<Block, 0x130> blocks_data;
89};
90
91class CFG {
92 enum class AnalysisState {
93 Branch,
94 Continue,
95 };
96
97public:
98 explicit CFG(Environment& env, Location start_address);
99
100 [[nodiscard]] std::string Dot() const;
101
102 [[nodiscard]] std::span<const Function> Functions() const noexcept {
103 return std::span(functions.data(), functions.size());
104 }
105
106private:
107 void AnalyzeLabel(FunctionId function_id, Label& label);
108
109 /// Inspect already visited blocks.
110 /// Return true when the block has already been visited
111 [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
112
113 [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc);
114
115 void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class,
116 IR::Condition cond);
117
118 /// Return true when the branch instruction is confirmed to be a branch
119 [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc,
120 Instruction inst, Opcode opcode);
121
122 void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
123 bool is_absolute);
124 void AnalyzeBRX(Block& block, Location pc, Instruction inst, bool is_absolute);
125 void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute);
126 AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst);
127
128 /// Return the branch target block id
129 [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc,
130 FunctionId function_id);
131
132 Environment& env;
133 boost::container::small_vector<Function, 1> functions;
134 FunctionId current_function_id{0};
135};
136
137} // namespace Shader::Maxwell::Flow
diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp
new file mode 100644
index 000000000..ab1cc6c8d
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/decode.cpp
@@ -0,0 +1,149 @@
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 <algorithm>
6#include <array>
7#include <bit>
8#include <memory>
9#include <string_view>
10
11#include "common/common_types.h"
12#include "shader_recompiler/exception.h"
13#include "shader_recompiler/frontend/maxwell/decode.h"
14#include "shader_recompiler/frontend/maxwell/opcode.h"
15
16namespace Shader::Maxwell {
17namespace {
18struct MaskValue {
19 u64 mask;
20 u64 value;
21};
22
23constexpr MaskValue MaskValueFromEncoding(const char* encoding) {
24 u64 mask{};
25 u64 value{};
26 u64 bit{u64(1) << 63};
27 while (*encoding) {
28 switch (*encoding) {
29 case '0':
30 mask |= bit;
31 break;
32 case '1':
33 mask |= bit;
34 value |= bit;
35 break;
36 case '-':
37 break;
38 case ' ':
39 break;
40 default:
41 throw LogicError("Invalid encoding character '{}'", *encoding);
42 }
43 ++encoding;
44 if (*encoding != ' ') {
45 bit >>= 1;
46 }
47 }
48 return MaskValue{.mask{mask}, .value{value}};
49}
50
51struct InstEncoding {
52 MaskValue mask_value;
53 Opcode opcode;
54};
55constexpr std::array UNORDERED_ENCODINGS{
56#define INST(name, cute, encode) \
57 InstEncoding{ \
58 .mask_value{MaskValueFromEncoding(encode)}, \
59 .opcode{Opcode::name}, \
60 },
61#include "maxwell.inc"
62#undef INST
63};
64
65constexpr auto SortedEncodings() {
66 std::array encodings{UNORDERED_ENCODINGS};
67 std::ranges::sort(encodings, [](const InstEncoding& lhs, const InstEncoding& rhs) {
68 return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask);
69 });
70 return encodings;
71}
72constexpr auto ENCODINGS{SortedEncodings()};
73
74constexpr int WidestLeftBits() {
75 int bits{64};
76 for (const InstEncoding& encoding : ENCODINGS) {
77 bits = std::min(bits, std::countr_zero(encoding.mask_value.mask));
78 }
79 return 64 - bits;
80}
81constexpr int WIDEST_LEFT_BITS{WidestLeftBits()};
82constexpr int MASK_SHIFT{64 - WIDEST_LEFT_BITS};
83
84constexpr size_t ToFastLookupIndex(u64 value) {
85 return static_cast<size_t>(value >> MASK_SHIFT);
86}
87
88constexpr size_t FastLookupSize() {
89 size_t max_width{};
90 for (const InstEncoding& encoding : ENCODINGS) {
91 max_width = std::max(max_width, ToFastLookupIndex(encoding.mask_value.mask));
92 }
93 return max_width + 1;
94}
95constexpr size_t FAST_LOOKUP_SIZE{FastLookupSize()};
96
97struct InstInfo {
98 [[nodiscard]] u64 Mask() const noexcept {
99 return static_cast<u64>(high_mask) << MASK_SHIFT;
100 }
101
102 [[nodiscard]] u64 Value() const noexcept {
103 return static_cast<u64>(high_value) << MASK_SHIFT;
104 }
105
106 u16 high_mask;
107 u16 high_value;
108 Opcode opcode;
109};
110
111constexpr auto MakeFastLookupTableIndex(size_t index) {
112 std::array<InstInfo, 2> encodings{};
113 size_t element{};
114 for (const auto& encoding : ENCODINGS) {
115 const size_t mask{ToFastLookupIndex(encoding.mask_value.mask)};
116 const size_t value{ToFastLookupIndex(encoding.mask_value.value)};
117 if ((index & mask) == value) {
118 encodings.at(element) = InstInfo{
119 .high_mask{static_cast<u16>(encoding.mask_value.mask >> MASK_SHIFT)},
120 .high_value{static_cast<u16>(encoding.mask_value.value >> MASK_SHIFT)},
121 .opcode{encoding.opcode},
122 };
123 ++element;
124 }
125 }
126 return encodings;
127}
128
129/*constexpr*/ auto MakeFastLookupTable() {
130 auto encodings{std::make_unique<std::array<std::array<InstInfo, 2>, FAST_LOOKUP_SIZE>>()};
131 for (size_t index = 0; index < FAST_LOOKUP_SIZE; ++index) {
132 (*encodings)[index] = MakeFastLookupTableIndex(index);
133 }
134 return encodings;
135}
136const auto FAST_LOOKUP_TABLE{MakeFastLookupTable()};
137} // Anonymous namespace
138
139Opcode Decode(u64 insn) {
140 const auto& table{(*FAST_LOOKUP_TABLE)[ToFastLookupIndex(insn)]};
141 const auto it{std::ranges::find_if(
142 table, [insn](const InstInfo& info) { return (insn & info.Mask()) == info.Value(); })};
143 if (it == table.end()) {
144 throw NotImplementedException("Instruction 0x{:016x} is unknown / unimplemented", insn);
145 }
146 return it->opcode;
147}
148
149} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/decode.h b/src/shader_recompiler/frontend/maxwell/decode.h
new file mode 100644
index 000000000..2a3dd28e8
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/decode.h
@@ -0,0 +1,14 @@
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 "common/common_types.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9
10namespace Shader::Maxwell {
11
12[[nodiscard]] Opcode Decode(u64 insn);
13
14} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/instruction.h b/src/shader_recompiler/frontend/maxwell/instruction.h
new file mode 100644
index 000000000..57fd531f2
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/instruction.h
@@ -0,0 +1,62 @@
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 "common/bit_field.h"
8#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/flow_test.h"
10
11namespace Shader::Maxwell {
12
13struct Predicate {
14 Predicate() = default;
15 Predicate(unsigned index_, bool negated_ = false) : index{index_}, negated{negated_} {}
16 Predicate(bool value) : index{7}, negated{!value} {}
17 Predicate(u64 raw) : index{static_cast<unsigned>(raw & 7)}, negated{(raw & 8) != 0} {}
18
19 unsigned index;
20 bool negated;
21};
22
23inline bool operator==(const Predicate& lhs, const Predicate& rhs) noexcept {
24 return lhs.index == rhs.index && lhs.negated == rhs.negated;
25}
26
27inline bool operator!=(const Predicate& lhs, const Predicate& rhs) noexcept {
28 return !(lhs == rhs);
29}
30
31union Instruction {
32 Instruction(u64 raw_) : raw{raw_} {}
33
34 u64 raw;
35
36 union {
37 BitField<5, 1, u64> is_cbuf;
38 BitField<0, 5, IR::FlowTest> flow_test;
39
40 [[nodiscard]] u32 Absolute() const noexcept {
41 return static_cast<u32>(absolute);
42 }
43
44 [[nodiscard]] s32 Offset() const noexcept {
45 return static_cast<s32>(offset);
46 }
47
48 private:
49 BitField<20, 24, s64> offset;
50 BitField<20, 32, u64> absolute;
51 } branch;
52
53 [[nodiscard]] Predicate Pred() const noexcept {
54 return Predicate{pred};
55 }
56
57private:
58 BitField<16, 4, u64> pred;
59};
60static_assert(std::is_trivially_copyable_v<Instruction>);
61
62} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/location.h b/src/shader_recompiler/frontend/maxwell/location.h
new file mode 100644
index 000000000..66b51a19e
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/location.h
@@ -0,0 +1,106 @@
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 <compare>
8#include <iterator>
9
10#include <fmt/format.h>
11
12#include "common/common_types.h"
13#include "shader_recompiler/exception.h"
14
15namespace Shader::Maxwell {
16
17class Location {
18 static constexpr u32 VIRTUAL_OFFSET{std::numeric_limits<u32>::max()};
19
20public:
21 constexpr Location() = default;
22
23 constexpr Location(u32 initial_offset) : offset{initial_offset} {
24 if (initial_offset % 8 != 0) {
25 throw InvalidArgument("initial_offset={} is not a multiple of 8", initial_offset);
26 }
27 Align();
28 }
29
30 [[nodiscard]] constexpr u32 Offset() const noexcept {
31 return offset;
32 }
33
34 [[nodiscard]] constexpr bool IsVirtual() const {
35 return offset == VIRTUAL_OFFSET;
36 }
37
38 constexpr auto operator<=>(const Location&) const noexcept = default;
39
40 constexpr Location operator++() noexcept {
41 const Location copy{*this};
42 Step();
43 return copy;
44 }
45
46 constexpr Location operator++(int) noexcept {
47 Step();
48 return *this;
49 }
50
51 constexpr Location operator--() noexcept {
52 const Location copy{*this};
53 Back();
54 return copy;
55 }
56
57 constexpr Location operator--(int) noexcept {
58 Back();
59 return *this;
60 }
61
62 constexpr Location operator+(int number) const {
63 Location new_pc{*this};
64 while (number > 0) {
65 --number;
66 ++new_pc;
67 }
68 while (number < 0) {
69 ++number;
70 --new_pc;
71 }
72 return new_pc;
73 }
74
75 constexpr Location operator-(int number) const {
76 return operator+(-number);
77 }
78
79private:
80 constexpr void Align() {
81 offset += offset % 32 == 0 ? 8 : 0;
82 }
83
84 constexpr void Step() {
85 offset += 8 + (offset % 32 == 24 ? 8 : 0);
86 }
87
88 constexpr void Back() {
89 offset -= 8 + (offset % 32 == 8 ? 8 : 0);
90 }
91
92 u32 offset{VIRTUAL_OFFSET};
93};
94
95} // namespace Shader::Maxwell
96
97template <>
98struct fmt::formatter<Shader::Maxwell::Location> {
99 constexpr auto parse(format_parse_context& ctx) {
100 return ctx.begin();
101 }
102 template <typename FormatContext>
103 auto format(const Shader::Maxwell::Location& location, FormatContext& ctx) {
104 return fmt::format_to(ctx.out(), "{:04x}", location.Offset());
105 }
106};
diff --git a/src/shader_recompiler/frontend/maxwell/maxwell.inc b/src/shader_recompiler/frontend/maxwell/maxwell.inc
new file mode 100644
index 000000000..1515285bf
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/maxwell.inc
@@ -0,0 +1,285 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5INST(AL2P, "AL2P", "1110 1111 1010 0---")
6INST(ALD, "ALD", "1110 1111 1101 1---")
7INST(AST, "AST", "1110 1111 1111 0---")
8INST(ATOM_cas, "ATOM (cas)", "1110 1110 1111 ----")
9INST(ATOM, "ATOM", "1110 1101 ---- ----")
10INST(ATOMS_cas, "ATOMS (cas)", "1110 1110 ---- ----")
11INST(ATOMS, "ATOMS", "1110 1100 ---- ----")
12INST(B2R, "B2R", "1111 0000 1011 1---")
13INST(BAR, "BAR", "1111 0000 1010 1---")
14INST(BFE_reg, "BFE (reg)", "0101 1100 0000 0---")
15INST(BFE_cbuf, "BFE (cbuf)", "0100 1100 0000 0---")
16INST(BFE_imm, "BFE (imm)", "0011 100- 0000 0---")
17INST(BFI_reg, "BFI (reg)", "0101 1011 1111 0---")
18INST(BFI_rc, "BFI (rc)", "0101 0011 1111 0---")
19INST(BFI_cr, "BFI (cr)", "0100 1011 1111 0---")
20INST(BFI_imm, "BFI (imm)", "0011 011- 1111 0---")
21INST(BPT, "BPT", "1110 0011 1010 ----")
22INST(BRA, "BRA", "1110 0010 0100 ----")
23INST(BRK, "BRK", "1110 0011 0100 ----")
24INST(BRX, "BRX", "1110 0010 0101 ----")
25INST(CAL, "CAL", "1110 0010 0110 ----")
26INST(CCTL, "CCTL", "1110 1111 011- ----")
27INST(CCTLL, "CCTLL", "1110 1111 100- ----")
28INST(CONT, "CONT", "1110 0011 0101 ----")
29INST(CS2R, "CS2R", "0101 0000 1100 1---")
30INST(CSET, "CSET", "0101 0000 1001 1---")
31INST(CSETP, "CSETP", "0101 0000 1010 0---")
32INST(DADD_reg, "DADD (reg)", "0101 1100 0111 0---")
33INST(DADD_cbuf, "DADD (cbuf)", "0100 1100 0111 0---")
34INST(DADD_imm, "DADD (imm)", "0011 100- 0111 0---")
35INST(DEPBAR, "DEPBAR", "1111 0000 1111 0---")
36INST(DFMA_reg, "DFMA (reg)", "0101 1011 0111 ----")
37INST(DFMA_rc, "DFMA (rc)", "0101 0011 0111 ----")
38INST(DFMA_cr, "DFMA (cr)", "0010 1011 0111 ----")
39INST(DFMA_imm, "DFMA (imm)", "0011 011- 0111 ----")
40INST(DMNMX_reg, "DMNMX (reg)", "0100 1100 0101 0---")
41INST(DMNMX_cbuf, "DMNMX (cbuf)", "0101 1100 0101 0---")
42INST(DMNMX_imm, "DMNMX (imm)", "0011 100- 0101 0---")
43INST(DMUL_reg, "DMUL (reg)", "0101 1100 1000 0---")
44INST(DMUL_cbuf, "DMUL (cbuf)", "0100 1100 1000 0---")
45INST(DMUL_imm, "DMUL (imm)", "0011 100- 1000 0---")
46INST(DSET_reg, "DSET (reg)", "0101 1001 0--- ----")
47INST(DSET_cbuf, "DSET (cbuf)", "0100 1001 0--- ----")
48INST(DSET_imm, "DSET (imm)", "0011 001- 0--- ----")
49INST(DSETP_reg, "DSETP (reg)", "0101 1011 1000 ----")
50INST(DSETP_cbuf, "DSETP (cbuf)", "0100 1011 1000 ----")
51INST(DSETP_imm, "DSETP (imm)", "0011 011- 1000 ----")
52INST(EXIT, "EXIT", "1110 0011 0000 ----")
53INST(F2F_reg, "F2F (reg)", "0101 1100 1010 1---")
54INST(F2F_cbuf, "F2F (cbuf)", "0100 1100 1010 1---")
55INST(F2F_imm, "F2F (imm)", "0011 100- 1010 1---")
56INST(F2I_reg, "F2I (reg)", "0101 1100 1011 0---")
57INST(F2I_cbuf, "F2I (cbuf)", "0100 1100 1011 0---")
58INST(F2I_imm, "F2I (imm)", "0011 100- 1011 0---")
59INST(FADD_reg, "FADD (reg)", "0101 1100 0101 1---")
60INST(FADD_cbuf, "FADD (cbuf)", "0100 1100 0101 1---")
61INST(FADD_imm, "FADD (imm)", "0011 100- 0101 1---")
62INST(FADD32I, "FADD32I", "0000 10-- ---- ----")
63INST(FCHK_reg, "FCHK (reg)", "0101 1100 1000 1---")
64INST(FCHK_cbuf, "FCHK (cbuf)", "0100 1100 1000 1---")
65INST(FCHK_imm, "FCHK (imm)", "0011 100- 1000 1---")
66INST(FCMP_reg, "FCMP (reg)", "0101 1011 1010 ----")
67INST(FCMP_rc, "FCMP (rc)", "0101 0011 1010 ----")
68INST(FCMP_cr, "FCMP (cr)", "0100 1011 1010 ----")
69INST(FCMP_imm, "FCMP (imm)", "0011 011- 1010 ----")
70INST(FFMA_reg, "FFMA (reg)", "0101 1001 1--- ----")
71INST(FFMA_rc, "FFMA (rc)", "0101 0001 1--- ----")
72INST(FFMA_cr, "FFMA (cr)", "0100 1001 1--- ----")
73INST(FFMA_imm, "FFMA (imm)", "0011 001- 1--- ----")
74INST(FFMA32I, "FFMA32I", "0000 11-- ---- ----")
75INST(FLO_reg, "FLO (reg)", "0101 1100 0011 0---")
76INST(FLO_cbuf, "FLO (cbuf)", "0100 1100 0011 0---")
77INST(FLO_imm, "FLO (imm)", "0011 100- 0011 0---")
78INST(FMNMX_reg, "FMNMX (reg)", "0101 1100 0110 0---")
79INST(FMNMX_cbuf, "FMNMX (cbuf)", "0100 1100 0110 0---")
80INST(FMNMX_imm, "FMNMX (imm)", "0011 100- 0110 0---")
81INST(FMUL_reg, "FMUL (reg)", "0101 1100 0110 1---")
82INST(FMUL_cbuf, "FMUL (cbuf)", "0100 1100 0110 1---")
83INST(FMUL_imm, "FMUL (imm)", "0011 100- 0110 1---")
84INST(FMUL32I, "FMUL32I", "0001 1110 ---- ----")
85INST(FSET_reg, "FSET (reg)", "0101 1000 ---- ----")
86INST(FSET_cbuf, "FSET (cbuf)", "0100 1000 ---- ----")
87INST(FSET_imm, "FSET (imm)", "0011 000- ---- ----")
88INST(FSETP_reg, "FSETP (reg)", "0101 1011 1011 ----")
89INST(FSETP_cbuf, "FSETP (cbuf)", "0100 1011 1011 ----")
90INST(FSETP_imm, "FSETP (imm)", "0011 011- 1011 ----")
91INST(FSWZADD, "FSWZADD", "0101 0000 1111 1---")
92INST(GETCRSPTR, "GETCRSPTR", "1110 0010 1100 ----")
93INST(GETLMEMBASE, "GETLMEMBASE", "1110 0010 1101 ----")
94INST(HADD2_reg, "HADD2 (reg)", "0101 1101 0001 0---")
95INST(HADD2_cbuf, "HADD2 (cbuf)", "0111 101- 1--- ----")
96INST(HADD2_imm, "HADD2 (imm)", "0111 101- 0--- ----")
97INST(HADD2_32I, "HADD2_32I", "0010 110- ---- ----")
98INST(HFMA2_reg, "HFMA2 (reg)", "0101 1101 0000 0---")
99INST(HFMA2_rc, "HFMA2 (rc)", "0110 0--- 1--- ----")
100INST(HFMA2_cr, "HFMA2 (cr)", "0111 0--- 1--- ----")
101INST(HFMA2_imm, "HFMA2 (imm)", "0111 0--- 0--- ----")
102INST(HFMA2_32I, "HFMA2_32I", "0010 100- ---- ----")
103INST(HMUL2_reg, "HMUL2 (reg)", "0101 1101 0000 1---")
104INST(HMUL2_cbuf, "HMUL2 (cbuf)", "0111 100- 1--- ----")
105INST(HMUL2_imm, "HMUL2 (imm)", "0111 100- 0--- ----")
106INST(HMUL2_32I, "HMUL2_32I", "0010 101- ---- ----")
107INST(HSET2_reg, "HSET2 (reg)", "0101 1101 0001 1---")
108INST(HSET2_cbuf, "HSET2 (cbuf)", "0111 1100 1--- ----")
109INST(HSET2_imm, "HSET2 (imm)", "0111 1100 0--- ----")
110INST(HSETP2_reg, "HSETP2 (reg)", "0101 1101 0010 0---")
111INST(HSETP2_cbuf, "HSETP2 (cbuf)", "0111 111- 1--- ----")
112INST(HSETP2_imm, "HSETP2 (imm)", "0111 111- 0--- ----")
113INST(I2F_reg, "I2F (reg)", "0101 1100 1011 1---")
114INST(I2F_cbuf, "I2F (cbuf)", "0100 1100 1011 1---")
115INST(I2F_imm, "I2F (imm)", "0011 100- 1011 1---")
116INST(I2I_reg, "I2I (reg)", "0101 1100 1110 0---")
117INST(I2I_cbuf, "I2I (cbuf)", "0100 1100 1110 0---")
118INST(I2I_imm, "I2I (imm)", "0011 100- 1110 0---")
119INST(IADD_reg, "IADD (reg)", "0101 1100 0001 0---")
120INST(IADD_cbuf, "IADD (cbuf)", "0100 1100 0001 0---")
121INST(IADD_imm, "IADD (imm)", "0011 100- 0001 0---")
122INST(IADD3_reg, "IADD3 (reg)", "0101 1100 1100 ----")
123INST(IADD3_cbuf, "IADD3 (cbuf)", "0100 1100 1100 ----")
124INST(IADD3_imm, "IADD3 (imm)", "0011 100- 1100 ----")
125INST(IADD32I, "IADD32I", "0001 110- ---- ----")
126INST(ICMP_reg, "ICMP (reg)", "0101 1011 0100 ----")
127INST(ICMP_rc, "ICMP (rc)", "0101 0011 0100 ----")
128INST(ICMP_cr, "ICMP (cr)", "0100 1011 0100 ----")
129INST(ICMP_imm, "ICMP (imm)", "0011 011- 0100 ----")
130INST(IDE, "IDE", "1110 0011 1001 ----")
131INST(IDP_reg, "IDP (reg)", "0101 0011 1111 1---")
132INST(IDP_imm, "IDP (imm)", "0101 0011 1101 1---")
133INST(IMAD_reg, "IMAD (reg)", "0101 1010 0--- ----")
134INST(IMAD_rc, "IMAD (rc)", "0101 0010 0--- ----")
135INST(IMAD_cr, "IMAD (cr)", "0100 1010 0--- ----")
136INST(IMAD_imm, "IMAD (imm)", "0011 010- 0--- ----")
137INST(IMAD32I, "IMAD32I", "1000 00-- ---- ----")
138INST(IMADSP_reg, "IMADSP (reg)", "0101 1010 1--- ----")
139INST(IMADSP_rc, "IMADSP (rc)", "0101 0010 1--- ----")
140INST(IMADSP_cr, "IMADSP (cr)", "0100 1010 1--- ----")
141INST(IMADSP_imm, "IMADSP (imm)", "0011 010- 1--- ----")
142INST(IMNMX_reg, "IMNMX (reg)", "0101 1100 0010 0---")
143INST(IMNMX_cbuf, "IMNMX (cbuf)", "0100 1100 0010 0---")
144INST(IMNMX_imm, "IMNMX (imm)", "0011 100- 0010 0---")
145INST(IMUL_reg, "IMUL (reg)", "0101 1100 0011 1---")
146INST(IMUL_cbuf, "IMUL (cbuf)", "0100 1100 0011 1---")
147INST(IMUL_imm, "IMUL (imm)", "0011 100- 0011 1---")
148INST(IMUL32I, "IMUL32I", "0001 1111 ---- ----")
149INST(IPA, "IPA", "1110 0000 ---- ----")
150INST(ISBERD, "ISBERD", "1110 1111 1101 0---")
151INST(ISCADD_reg, "ISCADD (reg)", "0101 1100 0001 1---")
152INST(ISCADD_cbuf, "ISCADD (cbuf)", "0100 1100 0001 1---")
153INST(ISCADD_imm, "ISCADD (imm)", "0011 100- 0001 1---")
154INST(ISCADD32I, "ISCADD32I", "0001 01-- ---- ----")
155INST(ISET_reg, "ISET (reg)", "0101 1011 0101 ----")
156INST(ISET_cbuf, "ISET (cbuf)", "0100 1011 0101 ----")
157INST(ISET_imm, "ISET (imm)", "0011 011- 0101 ----")
158INST(ISETP_reg, "ISETP (reg)", "0101 1011 0110 ----")
159INST(ISETP_cbuf, "ISETP (cbuf)", "0100 1011 0110 ----")
160INST(ISETP_imm, "ISETP (imm)", "0011 011- 0110 ----")
161INST(JCAL, "JCAL", "1110 0010 0010 ----")
162INST(JMP, "JMP", "1110 0010 0001 ----")
163INST(JMX, "JMX", "1110 0010 0000 ----")
164INST(KIL, "KIL", "1110 0011 0011 ----")
165INST(LD, "LD", "100- ---- ---- ----")
166INST(LDC, "LDC", "1110 1111 1001 0---")
167INST(LDG, "LDG", "1110 1110 1101 0---")
168INST(LDL, "LDL", "1110 1111 0100 0---")
169INST(LDS, "LDS", "1110 1111 0100 1---")
170INST(LEA_hi_reg, "LEA (hi reg)", "0101 1011 1101 1---")
171INST(LEA_hi_cbuf, "LEA (hi cbuf)", "0001 10-- ---- ----")
172INST(LEA_lo_reg, "LEA (lo reg)", "0101 1011 1101 0---")
173INST(LEA_lo_cbuf, "LEA (lo cbuf)", "0100 1011 1101 ----")
174INST(LEA_lo_imm, "LEA (lo imm)", "0011 011- 1101 0---")
175INST(LEPC, "LEPC", "0101 0000 1101 0---")
176INST(LONGJMP, "LONGJMP", "1110 0011 0001 ----")
177INST(LOP_reg, "LOP (reg)", "0101 1100 0100 0---")
178INST(LOP_cbuf, "LOP (cbuf)", "0100 1100 0100 0---")
179INST(LOP_imm, "LOP (imm)", "0011 100- 0100 0---")
180INST(LOP3_reg, "LOP3 (reg)", "0101 1011 1110 0---")
181INST(LOP3_cbuf, "LOP3 (cbuf)", "0011 11-- ---- ----")
182INST(LOP3_imm, "LOP3 (imm)", "0000 001- ---- ----")
183INST(LOP32I, "LOP32I", "0000 01-- ---- ----")
184INST(MEMBAR, "MEMBAR", "1110 1111 1001 1---")
185INST(MOV_reg, "MOV (reg)", "0101 1100 1001 1---")
186INST(MOV_cbuf, "MOV (cbuf)", "0100 1100 1001 1---")
187INST(MOV_imm, "MOV (imm)", "0011 100- 1001 1---")
188INST(MOV32I, "MOV32I", "0000 0001 0000 ----")
189INST(MUFU, "MUFU", "0101 0000 1000 0---")
190INST(NOP, "NOP", "0101 0000 1011 0---")
191INST(OUT_reg, "OUT (reg)", "1111 1011 1110 0---")
192INST(OUT_cbuf, "OUT (cbuf)", "1110 1011 1110 0---")
193INST(OUT_imm, "OUT (imm)", "1111 011- 1110 0---")
194INST(P2R_reg, "P2R (reg)", "0101 1100 1110 1---")
195INST(P2R_cbuf, "P2R (cbuf)", "0100 1100 1110 1---")
196INST(P2R_imm, "P2R (imm)", "0011 1000 1110 1---")
197INST(PBK, "PBK", "1110 0010 1010 ----")
198INST(PCNT, "PCNT", "1110 0010 1011 ----")
199INST(PEXIT, "PEXIT", "1110 0010 0011 ----")
200INST(PIXLD, "PIXLD", "1110 1111 1110 1---")
201INST(PLONGJMP, "PLONGJMP", "1110 0010 1000 ----")
202INST(POPC_reg, "POPC (reg)", "0101 1100 0000 1---")
203INST(POPC_cbuf, "POPC (cbuf)", "0100 1100 0000 1---")
204INST(POPC_imm, "POPC (imm)", "0011 100- 0000 1---")
205INST(PRET, "PRET", "1110 0010 0111 ----")
206INST(PRMT_reg, "PRMT (reg)", "0101 1011 1100 ----")
207INST(PRMT_rc, "PRMT (rc)", "0101 0011 1100 ----")
208INST(PRMT_cr, "PRMT (cr)", "0100 1011 1100 ----")
209INST(PRMT_imm, "PRMT (imm)", "0011 011- 1100 ----")
210INST(PSET, "PSET", "0101 0000 1000 1---")
211INST(PSETP, "PSETP", "0101 0000 1001 0---")
212INST(R2B, "R2B", "1111 0000 1100 0---")
213INST(R2P_reg, "R2P (reg)", "0101 1100 1111 0---")
214INST(R2P_cbuf, "R2P (cbuf)", "0100 1100 1111 0---")
215INST(R2P_imm, "R2P (imm)", "0011 100- 1111 0---")
216INST(RAM, "RAM", "1110 0011 1000 ----")
217INST(RED, "RED", "1110 1011 1111 1---")
218INST(RET, "RET", "1110 0011 0010 ----")
219INST(RRO_reg, "RRO (reg)", "0101 1100 1001 0---")
220INST(RRO_cbuf, "RRO (cbuf)", "0100 1100 1001 0---")
221INST(RRO_imm, "RRO (imm)", "0011 100- 1001 0---")
222INST(RTT, "RTT", "1110 0011 0110 ----")
223INST(S2R, "S2R", "1111 0000 1100 1---")
224INST(SAM, "SAM", "1110 0011 0111 ----")
225INST(SEL_reg, "SEL (reg)", "0101 1100 1010 0---")
226INST(SEL_cbuf, "SEL (cbuf)", "0100 1100 1010 0---")
227INST(SEL_imm, "SEL (imm)", "0011 100- 1010 0---")
228INST(SETCRSPTR, "SETCRSPTR", "1110 0010 1110 ----")
229INST(SETLMEMBASE, "SETLMEMBASE", "1110 0010 1111 ----")
230INST(SHF_l_reg, "SHF (l reg)", "0101 1011 1111 1---")
231INST(SHF_l_imm, "SHF (l imm)", "0011 011- 1111 1---")
232INST(SHF_r_reg, "SHF (r reg)", "0101 1100 1111 1---")
233INST(SHF_r_imm, "SHF (r imm)", "0011 100- 1111 1---")
234INST(SHFL, "SHFL", "1110 1111 0001 0---")
235INST(SHL_reg, "SHL (reg)", "0101 1100 0100 1---")
236INST(SHL_cbuf, "SHL (cbuf)", "0100 1100 0100 1---")
237INST(SHL_imm, "SHL (imm)", "0011 100- 0100 1---")
238INST(SHR_reg, "SHR (reg)", "0101 1100 0010 1---")
239INST(SHR_cbuf, "SHR (cbuf)", "0100 1100 0010 1---")
240INST(SHR_imm, "SHR (imm)", "0011 100- 0010 1---")
241INST(SSY, "SSY", "1110 0010 1001 ----")
242INST(ST, "ST", "101- ---- ---- ----")
243INST(STG, "STG", "1110 1110 1101 1---")
244INST(STL, "STL", "1110 1111 0101 0---")
245INST(STP, "STP", "1110 1110 1010 0---")
246INST(STS, "STS", "1110 1111 0101 1---")
247INST(SUATOM_cas, "SUATOM", "1110 1010 ---- ----")
248INST(SULD, "SULD", "1110 1011 000- ----")
249INST(SURED, "SURED", "1110 1011 010- ----")
250INST(SUST, "SUST", "1110 1011 001- ----")
251INST(SYNC, "SYNC", "1111 0000 1111 1---")
252INST(TEX, "TEX", "1100 00-- --11 1---")
253INST(TEX_b, "TEX (b)", "1101 1110 1011 1---")
254INST(TEXS, "TEXS", "1101 -00- ---- ----")
255INST(TLD, "TLD", "1101 1100 --11 1---")
256INST(TLD_b, "TLD (b)", "1101 1101 --11 1---")
257INST(TLD4, "TLD4", "1100 10-- --11 1---")
258INST(TLD4_b, "TLD4 (b)", "1101 1110 1111 1---")
259INST(TLD4S, "TLD4S", "1101 1111 -0-- ----")
260INST(TLDS, "TLDS", "1101 -01- ---- ----")
261INST(TMML, "TMML", "1101 1111 0101 1---")
262INST(TMML_b, "TMML (b)", "1101 1111 0110 0---")
263INST(TXA, "TXA", "1101 1111 0100 0---")
264INST(TXD, "TXD", "1101 1110 0011 10--")
265INST(TXD_b, "TXD (b)", "1101 1110 0111 10--")
266INST(TXQ, "TXQ", "1101 1111 0100 1---")
267INST(TXQ_b, "TXQ (b)", "1101 1111 0101 0---")
268INST(VABSDIFF, "VABSDIFF", "0101 0100 ---- ----")
269INST(VABSDIFF4, "VABSDIFF4", "0101 0000 0--- ----")
270INST(VADD, "VADD", "0010 00-- ---- ----")
271INST(VMAD, "VMAD", "0101 1111 ---- ----")
272INST(VMNMX, "VMNMX", "0011 101- ---- ----")
273INST(VOTE, "VOTE", "0101 0000 1101 1---")
274INST(VOTE_vtg, "VOTE (vtg)", "0101 0000 1110 0---")
275INST(VSET, "VSET", "0100 000- ---- ----")
276INST(VSETP, "VSETP", "0101 0000 1111 0---")
277INST(VSHL, "VSHL", "0101 0111 ---- ----")
278INST(VSHR, "VSHR", "0101 0110 ---- ----")
279INST(XMAD_reg, "XMAD (reg)", "0101 1011 00-- ----")
280INST(XMAD_rc, "XMAD (rc)", "0101 0001 0--- ----")
281INST(XMAD_cr, "XMAD (cr)", "0100 111- ---- ----")
282INST(XMAD_imm, "XMAD (imm)", "0011 011- 00-- ----")
283
284// Removed due to its weird formatting making fast tables larger
285// INST(CCTLT, "CCTLT", "1110 1011 1111 0--0")
diff --git a/src/shader_recompiler/frontend/maxwell/opcode.cpp b/src/shader_recompiler/frontend/maxwell/opcode.cpp
new file mode 100644
index 000000000..8a7bdb611
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/opcode.cpp
@@ -0,0 +1,26 @@
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 <array>
6
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9
10namespace Shader::Maxwell {
11namespace {
12constexpr std::array NAME_TABLE{
13#define INST(name, cute, encode) #cute,
14#include "maxwell.inc"
15#undef INST
16};
17} // Anonymous namespace
18
19const char* NameOf(Opcode opcode) {
20 if (static_cast<size_t>(opcode) >= NAME_TABLE.size()) {
21 throw InvalidArgument("Invalid opcode with raw value {}", static_cast<int>(opcode));
22 }
23 return NAME_TABLE[static_cast<size_t>(opcode)];
24}
25
26} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/opcode.h b/src/shader_recompiler/frontend/maxwell/opcode.h
new file mode 100644
index 000000000..cd574f29d
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/opcode.h
@@ -0,0 +1,30 @@
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 <fmt/format.h>
8
9namespace Shader::Maxwell {
10
11enum class Opcode {
12#define INST(name, cute, encode) name,
13#include "maxwell.inc"
14#undef INST
15};
16
17const char* NameOf(Opcode opcode);
18
19} // namespace Shader::Maxwell
20
21template <>
22struct fmt::formatter<Shader::Maxwell::Opcode> {
23 constexpr auto parse(format_parse_context& ctx) {
24 return ctx.begin();
25 }
26 template <typename FormatContext>
27 auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) {
28 return format_to(ctx.out(), "{}", NameOf(opcode));
29 }
30};
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp
new file mode 100644
index 000000000..67a98ba57
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/program.cpp
@@ -0,0 +1,69 @@
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 <algorithm>
6#include <memory>
7
8#include "shader_recompiler/frontend/maxwell/program.h"
9#include "shader_recompiler/frontend/maxwell/termination_code.h"
10#include "shader_recompiler/frontend/maxwell/translate/translate.h"
11
12namespace Shader::Maxwell {
13
14Program::Function::~Function() {
15 std::ranges::for_each(blocks, &std::destroy_at<IR::Block>);
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 for (const Flow::Function& cfg_function : cfg.Functions()) {
23 Function& function{functions.emplace_back()};
24
25 const size_t num_blocks{cfg_function.blocks.size()};
26 IR::Block* block_memory{block_alloc_pool.allocate(num_blocks)};
27 function.blocks.reserve(num_blocks);
28
29 block_map.resize(cfg_function.blocks_data.size());
30
31 // Visit the instructions of all blocks
32 for (const Flow::BlockId block_id : cfg_function.blocks) {
33 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
34
35 IR::Block* const block{std::construct_at(block_memory, Translate(env, flow_block))};
36 ++block_memory;
37 function.blocks.push_back(block);
38 block_map[flow_block.id] = block;
39 }
40 // Now that all blocks are defined, emit the termination instructions
41 for (const Flow::BlockId block_id : cfg_function.blocks) {
42 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
43 EmitTerminationCode(flow_block, block_map);
44 }
45 }
46}
47
48std::string DumpProgram(const Program& program) {
49 size_t index{0};
50 std::map<const IR::Inst*, size_t> inst_to_index;
51 std::map<const IR::Block*, size_t> block_to_index;
52
53 for (const Program::Function& function : program.functions) {
54 for (const IR::Block* const block : function.blocks) {
55 block_to_index.emplace(block, index);
56 ++index;
57 }
58 }
59 std::string ret;
60 for (const Program::Function& function : program.functions) {
61 ret += fmt::format("Function\n");
62 for (const IR::Block* const block : function.blocks) {
63 ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
64 }
65 }
66 return ret;
67}
68
69} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/program.h b/src/shader_recompiler/frontend/maxwell/program.h
new file mode 100644
index 000000000..7814b2c01
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/program.h
@@ -0,0 +1,39 @@
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 <string>
8#include <vector>
9
10#include <boost/pool/pool_alloc.hpp>
11
12#include "shader_recompiler/environment.h"
13#include "shader_recompiler/frontend/ir/basic_block.h"
14#include "shader_recompiler/frontend/maxwell/control_flow.h"
15
16namespace Shader::Maxwell {
17
18class Program {
19 friend std::string DumpProgram(const Program& program);
20
21public:
22 explicit Program(Environment& env, const Flow::CFG& cfg);
23
24private:
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,
32 boost::details::pool::null_mutex>
33 block_alloc_pool;
34 std::vector<Function> functions;
35};
36
37[[nodiscard]] std::string DumpProgram(const Program& program);
38
39} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.cpp b/src/shader_recompiler/frontend/maxwell/termination_code.cpp
new file mode 100644
index 000000000..a4ea5c5e3
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/termination_code.cpp
@@ -0,0 +1,79 @@
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 <span>
6
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/ir_emitter.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11#include "shader_recompiler/frontend/maxwell/termination_code.h"
12
13namespace Shader::Maxwell {
14
15static void EmitExit(IR::IREmitter& ir) {
16 ir.Exit();
17}
18
19static IR::U1 GetFlowTest(IR::FlowTest flow_test, IR::IREmitter& ir) {
20 switch (flow_test) {
21 case IR::FlowTest::T:
22 return ir.Imm1(true);
23 case IR::FlowTest::F:
24 return ir.Imm1(false);
25 case IR::FlowTest::NE:
26 // FIXME: Verify this
27 return ir.LogicalNot(ir.GetZFlag());
28 case IR::FlowTest::NaN:
29 // FIXME: Verify this
30 return ir.LogicalAnd(ir.GetSFlag(), ir.GetZFlag());
31 default:
32 throw NotImplementedException("Flow test {}", flow_test);
33 }
34}
35
36static IR::U1 GetCond(IR::Condition cond, IR::IREmitter& ir) {
37 const IR::FlowTest flow_test{cond.FlowTest()};
38 const auto [pred, pred_negated]{cond.Pred()};
39 if (pred == IR::Pred::PT && !pred_negated) {
40 return GetFlowTest(flow_test, ir);
41 }
42 if (flow_test == IR::FlowTest::T) {
43 return ir.GetPred(pred, pred_negated);
44 }
45 return ir.LogicalAnd(ir.GetPred(pred, pred_negated), GetFlowTest(flow_test, ir));
46}
47
48static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map,
49 IR::IREmitter& ir) {
50 if (flow_block.cond == true) {
51 return ir.Branch(block_map[flow_block.branch_true]);
52 }
53 if (flow_block.cond == false) {
54 return ir.Branch(block_map[flow_block.branch_false]);
55 }
56 return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true],
57 block_map[flow_block.branch_false]);
58}
59
60void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map) {
61 IR::Block* const block{block_map[flow_block.id]};
62 IR::IREmitter ir(*block);
63 switch (flow_block.end_class) {
64 case Flow::EndClass::Branch:
65 EmitBranch(flow_block, block_map, ir);
66 break;
67 case Flow::EndClass::Exit:
68 EmitExit(ir);
69 break;
70 case Flow::EndClass::Return:
71 ir.Return();
72 break;
73 case Flow::EndClass::Unreachable:
74 ir.Unreachable();
75 break;
76 }
77}
78
79} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.h b/src/shader_recompiler/frontend/maxwell/termination_code.h
new file mode 100644
index 000000000..b0d667942
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/termination_code.h
@@ -0,0 +1,16 @@
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 <span>
8
9#include "shader_recompiler/frontend/ir/basic_block.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11
12namespace Shader::Maxwell {
13
14void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map);
15
16} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp
new file mode 100644
index 000000000..e98bbd0d1
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp
@@ -0,0 +1,15 @@
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 "common/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
8
9namespace Shader::Maxwell {
10
11void TranslatorVisitor::EXIT(u64) {
12 ir.Exit();
13}
14
15} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
new file mode 100644
index 000000000..c4288d9a8
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
@@ -0,0 +1,133 @@
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 "common/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/opcode.h"
8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
9
10namespace Shader::Maxwell {
11namespace {
12enum class DestFormat : u64 {
13 Invalid,
14 I16,
15 I32,
16 I64,
17};
18enum class SrcFormat : u64 {
19 Invalid,
20 F16,
21 F32,
22 F64,
23};
24enum class Rounding : u64 {
25 Round,
26 Floor,
27 Ceil,
28 Trunc,
29};
30
31union F2I {
32 u64 raw;
33 BitField<0, 8, IR::Reg> dest_reg;
34 BitField<8, 2, DestFormat> dest_format;
35 BitField<10, 2, SrcFormat> src_format;
36 BitField<12, 1, u64> is_signed;
37 BitField<39, 1, Rounding> rounding;
38 BitField<49, 1, u64> half;
39 BitField<44, 1, u64> ftz;
40 BitField<45, 1, u64> abs;
41 BitField<47, 1, u64> cc;
42 BitField<49, 1, u64> neg;
43};
44
45size_t BitSize(DestFormat dest_format) {
46 switch (dest_format) {
47 case DestFormat::I16:
48 return 16;
49 case DestFormat::I32:
50 return 32;
51 case DestFormat::I64:
52 return 64;
53 default:
54 throw NotImplementedException("Invalid destination format {}", dest_format);
55 }
56}
57
58void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::U16U32U64& op_a) {
59 // F2I is used to convert from a floating point value to an integer
60 const F2I f2i{insn};
61
62 const IR::U16U32U64 float_value{v.ir.FPAbsNeg(op_a, f2i.abs != 0, f2i.neg != 0)};
63 const IR::U16U32U64 rounded_value{[&] {
64 switch (f2i.rounding) {
65 case Rounding::Round:
66 return v.ir.FPRoundEven(float_value);
67 case Rounding::Floor:
68 return v.ir.FPFloor(float_value);
69 case Rounding::Ceil:
70 return v.ir.FPCeil(float_value);
71 case Rounding::Trunc:
72 return v.ir.FPTrunc(float_value);
73 default:
74 throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value());
75 }
76 }()};
77
78 // TODO: Handle out of bounds conversions.
79 // For example converting F32 65537.0 to U16, the expected value is 0xffff,
80
81 const bool is_signed{f2i.is_signed != 0};
82 const size_t bitsize{BitSize(f2i.dest_format)};
83 const IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, rounded_value)};
84
85 v.X(f2i.dest_reg, result);
86
87 if (f2i.cc != 0) {
88 v.SetZFlag(v.ir.GetZeroFromOp(result));
89 if (is_signed) {
90 v.SetSFlag(v.ir.GetSignFromOp(result));
91 } else {
92 v.ResetSFlag();
93 }
94 v.ResetCFlag();
95
96 // TODO: Investigate if out of bound conversions sets the overflow flag
97 v.ResetOFlag();
98 }
99}
100} // Anonymous namespace
101
102void TranslatorVisitor::F2I_reg(u64 insn) {
103 union {
104 F2I base;
105 BitField<20, 8, IR::Reg> src_reg;
106 } const f2i{insn};
107
108 const IR::U16U32U64 op_a{[&]() -> IR::U16U32U64 {
109 switch (f2i.base.src_format) {
110 case SrcFormat::F16:
111 return ir.CompositeExtract(ir.UnpackFloat2x16(X(f2i.src_reg)), f2i.base.half);
112 case SrcFormat::F32:
113 return X(f2i.src_reg);
114 case SrcFormat::F64:
115 return ir.PackDouble2x32(ir.CompositeConstruct(X(f2i.src_reg), X(f2i.src_reg + 1)));
116 default:
117 throw NotImplementedException("Invalid F2I source format {}",
118 f2i.base.src_format.Value());
119 }
120 }()};
121
122 TranslateF2I(*this, insn, op_a);
123}
124
125void TranslatorVisitor::F2I_cbuf(u64) {
126 throw NotImplementedException("{}", Opcode::F2I_cbuf);
127}
128
129void TranslatorVisitor::F2I_imm(u64) {
130 throw NotImplementedException("{}", Opcode::F2I_imm);
131}
132
133} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
new file mode 100644
index 000000000..e2ab0dab2
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
@@ -0,0 +1,71 @@
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 "common/bit_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class Operation {
14 Cos = 0,
15 Sin = 1,
16 Ex2 = 2, // Base 2 exponent
17 Lg2 = 3, // Base 2 logarithm
18 Rcp = 4, // Reciprocal
19 Rsq = 5, // Reciprocal square root
20 Rcp64H = 6, // 64-bit reciprocal
21 Rsq64H = 7, // 64-bit reciprocal square root
22 Sqrt = 8,
23};
24} // Anonymous namespace
25
26void TranslatorVisitor::MUFU(u64 insn) {
27 // MUFU is used to implement a bunch of special functions. See Operation.
28 union {
29 u64 raw;
30 BitField<0, 8, IR::Reg> dest_reg;
31 BitField<8, 8, IR::Reg> src_reg;
32 BitField<20, 4, Operation> operation;
33 BitField<46, 1, u64> abs;
34 BitField<48, 1, u64> neg;
35 BitField<50, 1, u64> sat;
36 } const mufu{insn};
37
38 const IR::U32 op_a{ir.FPAbsNeg(X(mufu.src_reg), mufu.abs != 0, mufu.neg != 0)};
39 IR::U32 value{[&]() -> IR::U32 {
40 switch (mufu.operation) {
41 case Operation::Cos:
42 return ir.FPCosNotReduced(op_a);
43 case Operation::Sin:
44 return ir.FPSinNotReduced(op_a);
45 case Operation::Ex2:
46 return ir.FPExp2NotReduced(op_a);
47 case Operation::Lg2:
48 return ir.FPLog2(op_a);
49 case Operation::Rcp:
50 return ir.FPRecip(op_a);
51 case Operation::Rsq:
52 return ir.FPRecipSqrt(op_a);
53 case Operation::Rcp64H:
54 throw NotImplementedException("MUFU.RCP64H");
55 case Operation::Rsq64H:
56 throw NotImplementedException("MUFU.RSQ64H");
57 case Operation::Sqrt:
58 return ir.FPSqrt(op_a);
59 default:
60 throw NotImplementedException("Invalid MUFU operation {}", mufu.operation.Value());
61 }
62 }()};
63
64 if (mufu.sat) {
65 value = ir.FPSaturate(value);
66 }
67
68 X(mufu.dest_reg, value);
69}
70
71} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
new file mode 100644
index 000000000..7bc7ce9f2
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
@@ -0,0 +1,79 @@
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 "common/bit_field.h"
6#include "shader_recompiler/frontend/ir/ir_emitter.h"
7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
8
9namespace Shader::Maxwell {
10
11IR::U32 TranslatorVisitor::X(IR::Reg reg) {
12 return ir.GetReg(reg);
13}
14
15void TranslatorVisitor::X(IR::Reg dest_reg, const IR::U32& value) {
16 ir.SetReg(dest_reg, value);
17}
18
19IR::U32 TranslatorVisitor::GetCbuf(u64 insn) {
20 union {
21 u64 raw;
22 BitField<20, 14, s64> offset;
23 BitField<34, 5, u64> binding;
24 } const cbuf{insn};
25 if (cbuf.binding >= 18) {
26 throw NotImplementedException("Out of bounds constant buffer binding {}", cbuf.binding);
27 }
28 if (cbuf.offset >= 0x10'000 || cbuf.offset < 0) {
29 throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset);
30 }
31 const IR::U32 binding{ir.Imm32(static_cast<u32>(cbuf.binding))};
32 const IR::U32 byte_offset{ir.Imm32(static_cast<u32>(cbuf.offset) * 4)};
33 return ir.GetCbuf(binding, byte_offset);
34}
35
36IR::U32 TranslatorVisitor::GetImm(u64 insn) {
37 union {
38 u64 raw;
39 BitField<20, 19, u64> value;
40 BitField<56, 1, u64> is_negative;
41 } const imm{insn};
42 const s32 positive_value{static_cast<s32>(imm.value)};
43 const s32 value{imm.is_negative != 0 ? -positive_value : positive_value};
44 return ir.Imm32(value);
45}
46
47void TranslatorVisitor::SetZFlag(const IR::U1& value) {
48 ir.SetZFlag(value);
49}
50
51void TranslatorVisitor::SetSFlag(const IR::U1& value) {
52 ir.SetSFlag(value);
53}
54
55void TranslatorVisitor::SetCFlag(const IR::U1& value) {
56 ir.SetCFlag(value);
57}
58
59void TranslatorVisitor::SetOFlag(const IR::U1& value) {
60 ir.SetOFlag(value);
61}
62
63void TranslatorVisitor::ResetZero() {
64 SetZFlag(ir.Imm1(false));
65}
66
67void TranslatorVisitor::ResetSFlag() {
68 SetSFlag(ir.Imm1(false));
69}
70
71void TranslatorVisitor::ResetCFlag() {
72 SetCFlag(ir.Imm1(false));
73}
74
75void TranslatorVisitor::ResetOFlag() {
76 SetOFlag(ir.Imm1(false));
77}
78
79} // 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
new file mode 100644
index 000000000..bc607b002
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
@@ -0,0 +1,316 @@
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/environment.h"
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/ir/ir_emitter.h"
8#include "shader_recompiler/frontend/maxwell/instruction.h"
9
10namespace Shader::Maxwell {
11
12class TranslatorVisitor {
13public:
14 explicit TranslatorVisitor(Environment& env_, IR::Block& block) : env{env_} ,ir(block) {}
15
16 Environment& env;
17 IR::IREmitter ir;
18
19 void AL2P(u64 insn);
20 void ALD(u64 insn);
21 void AST(u64 insn);
22 void ATOM_cas(u64 insn);
23 void ATOM(u64 insn);
24 void ATOMS_cas(u64 insn);
25 void ATOMS(u64 insn);
26 void B2R(u64 insn);
27 void BAR(u64 insn);
28 void BFE_reg(u64 insn);
29 void BFE_cbuf(u64 insn);
30 void BFE_imm(u64 insn);
31 void BFI_reg(u64 insn);
32 void BFI_rc(u64 insn);
33 void BFI_cr(u64 insn);
34 void BFI_imm(u64 insn);
35 void BPT(u64 insn);
36 void BRA(u64 insn);
37 void BRK(u64 insn);
38 void BRX(u64 insn);
39 void CAL(u64 insn);
40 void CCTL(u64 insn);
41 void CCTLL(u64 insn);
42 void CONT(u64 insn);
43 void CS2R(u64 insn);
44 void CSET(u64 insn);
45 void CSETP(u64 insn);
46 void DADD_reg(u64 insn);
47 void DADD_cbuf(u64 insn);
48 void DADD_imm(u64 insn);
49 void DEPBAR(u64 insn);
50 void DFMA_reg(u64 insn);
51 void DFMA_rc(u64 insn);
52 void DFMA_cr(u64 insn);
53 void DFMA_imm(u64 insn);
54 void DMNMX_reg(u64 insn);
55 void DMNMX_cbuf(u64 insn);
56 void DMNMX_imm(u64 insn);
57 void DMUL_reg(u64 insn);
58 void DMUL_cbuf(u64 insn);
59 void DMUL_imm(u64 insn);
60 void DSET_reg(u64 insn);
61 void DSET_cbuf(u64 insn);
62 void DSET_imm(u64 insn);
63 void DSETP_reg(u64 insn);
64 void DSETP_cbuf(u64 insn);
65 void DSETP_imm(u64 insn);
66 void EXIT(u64 insn);
67 void F2F_reg(u64 insn);
68 void F2F_cbuf(u64 insn);
69 void F2F_imm(u64 insn);
70 void F2I_reg(u64 insn);
71 void F2I_cbuf(u64 insn);
72 void F2I_imm(u64 insn);
73 void FADD_reg(u64 insn);
74 void FADD_cbuf(u64 insn);
75 void FADD_imm(u64 insn);
76 void FADD32I(u64 insn);
77 void FCHK_reg(u64 insn);
78 void FCHK_cbuf(u64 insn);
79 void FCHK_imm(u64 insn);
80 void FCMP_reg(u64 insn);
81 void FCMP_rc(u64 insn);
82 void FCMP_cr(u64 insn);
83 void FCMP_imm(u64 insn);
84 void FFMA_reg(u64 insn);
85 void FFMA_rc(u64 insn);
86 void FFMA_cr(u64 insn);
87 void FFMA_imm(u64 insn);
88 void FFMA32I(u64 insn);
89 void FLO_reg(u64 insn);
90 void FLO_cbuf(u64 insn);
91 void FLO_imm(u64 insn);
92 void FMNMX_reg(u64 insn);
93 void FMNMX_cbuf(u64 insn);
94 void FMNMX_imm(u64 insn);
95 void FMUL_reg(u64 insn);
96 void FMUL_cbuf(u64 insn);
97 void FMUL_imm(u64 insn);
98 void FMUL32I(u64 insn);
99 void FSET_reg(u64 insn);
100 void FSET_cbuf(u64 insn);
101 void FSET_imm(u64 insn);
102 void FSETP_reg(u64 insn);
103 void FSETP_cbuf(u64 insn);
104 void FSETP_imm(u64 insn);
105 void FSWZADD(u64 insn);
106 void GETCRSPTR(u64 insn);
107 void GETLMEMBASE(u64 insn);
108 void HADD2_reg(u64 insn);
109 void HADD2_cbuf(u64 insn);
110 void HADD2_imm(u64 insn);
111 void HADD2_32I(u64 insn);
112 void HFMA2_reg(u64 insn);
113 void HFMA2_rc(u64 insn);
114 void HFMA2_cr(u64 insn);
115 void HFMA2_imm(u64 insn);
116 void HFMA2_32I(u64 insn);
117 void HMUL2_reg(u64 insn);
118 void HMUL2_cbuf(u64 insn);
119 void HMUL2_imm(u64 insn);
120 void HMUL2_32I(u64 insn);
121 void HSET2_reg(u64 insn);
122 void HSET2_cbuf(u64 insn);
123 void HSET2_imm(u64 insn);
124 void HSETP2_reg(u64 insn);
125 void HSETP2_cbuf(u64 insn);
126 void HSETP2_imm(u64 insn);
127 void I2F_reg(u64 insn);
128 void I2F_cbuf(u64 insn);
129 void I2F_imm(u64 insn);
130 void I2I_reg(u64 insn);
131 void I2I_cbuf(u64 insn);
132 void I2I_imm(u64 insn);
133 void IADD_reg(u64 insn);
134 void IADD_cbuf(u64 insn);
135 void IADD_imm(u64 insn);
136 void IADD3_reg(u64 insn);
137 void IADD3_cbuf(u64 insn);
138 void IADD3_imm(u64 insn);
139 void IADD32I(u64 insn);
140 void ICMP_reg(u64 insn);
141 void ICMP_rc(u64 insn);
142 void ICMP_cr(u64 insn);
143 void ICMP_imm(u64 insn);
144 void IDE(u64 insn);
145 void IDP_reg(u64 insn);
146 void IDP_imm(u64 insn);
147 void IMAD_reg(u64 insn);
148 void IMAD_rc(u64 insn);
149 void IMAD_cr(u64 insn);
150 void IMAD_imm(u64 insn);
151 void IMAD32I(u64 insn);
152 void IMADSP_reg(u64 insn);
153 void IMADSP_rc(u64 insn);
154 void IMADSP_cr(u64 insn);
155 void IMADSP_imm(u64 insn);
156 void IMNMX_reg(u64 insn);
157 void IMNMX_cbuf(u64 insn);
158 void IMNMX_imm(u64 insn);
159 void IMUL_reg(u64 insn);
160 void IMUL_cbuf(u64 insn);
161 void IMUL_imm(u64 insn);
162 void IMUL32I(u64 insn);
163 void IPA(u64 insn);
164 void ISBERD(u64 insn);
165 void ISCADD_reg(u64 insn);
166 void ISCADD_cbuf(u64 insn);
167 void ISCADD_imm(u64 insn);
168 void ISCADD32I(u64 insn);
169 void ISET_reg(u64 insn);
170 void ISET_cbuf(u64 insn);
171 void ISET_imm(u64 insn);
172 void ISETP_reg(u64 insn);
173 void ISETP_cbuf(u64 insn);
174 void ISETP_imm(u64 insn);
175 void JCAL(u64 insn);
176 void JMP(u64 insn);
177 void JMX(u64 insn);
178 void KIL(u64 insn);
179 void LD(u64 insn);
180 void LDC(u64 insn);
181 void LDG(u64 insn);
182 void LDL(u64 insn);
183 void LDS(u64 insn);
184 void LEA_hi_reg(u64 insn);
185 void LEA_hi_cbuf(u64 insn);
186 void LEA_lo_reg(u64 insn);
187 void LEA_lo_cbuf(u64 insn);
188 void LEA_lo_imm(u64 insn);
189 void LEPC(u64 insn);
190 void LONGJMP(u64 insn);
191 void LOP_reg(u64 insn);
192 void LOP_cbuf(u64 insn);
193 void LOP_imm(u64 insn);
194 void LOP3_reg(u64 insn);
195 void LOP3_cbuf(u64 insn);
196 void LOP3_imm(u64 insn);
197 void LOP32I(u64 insn);
198 void MEMBAR(u64 insn);
199 void MOV_reg(u64 insn);
200 void MOV_cbuf(u64 insn);
201 void MOV_imm(u64 insn);
202 void MOV32I(u64 insn);
203 void MUFU(u64 insn);
204 void NOP(u64 insn);
205 void OUT_reg(u64 insn);
206 void OUT_cbuf(u64 insn);
207 void OUT_imm(u64 insn);
208 void P2R_reg(u64 insn);
209 void P2R_cbuf(u64 insn);
210 void P2R_imm(u64 insn);
211 void PBK(u64 insn);
212 void PCNT(u64 insn);
213 void PEXIT(u64 insn);
214 void PIXLD(u64 insn);
215 void PLONGJMP(u64 insn);
216 void POPC_reg(u64 insn);
217 void POPC_cbuf(u64 insn);
218 void POPC_imm(u64 insn);
219 void PRET(u64 insn);
220 void PRMT_reg(u64 insn);
221 void PRMT_rc(u64 insn);
222 void PRMT_cr(u64 insn);
223 void PRMT_imm(u64 insn);
224 void PSET(u64 insn);
225 void PSETP(u64 insn);
226 void R2B(u64 insn);
227 void R2P_reg(u64 insn);
228 void R2P_cbuf(u64 insn);
229 void R2P_imm(u64 insn);
230 void RAM(u64 insn);
231 void RED(u64 insn);
232 void RET(u64 insn);
233 void RRO_reg(u64 insn);
234 void RRO_cbuf(u64 insn);
235 void RRO_imm(u64 insn);
236 void RTT(u64 insn);
237 void S2R(u64 insn);
238 void SAM(u64 insn);
239 void SEL_reg(u64 insn);
240 void SEL_cbuf(u64 insn);
241 void SEL_imm(u64 insn);
242 void SETCRSPTR(u64 insn);
243 void SETLMEMBASE(u64 insn);
244 void SHF_l_reg(u64 insn);
245 void SHF_l_imm(u64 insn);
246 void SHF_r_reg(u64 insn);
247 void SHF_r_imm(u64 insn);
248 void SHFL(u64 insn);
249 void SHL_reg(u64 insn);
250 void SHL_cbuf(u64 insn);
251 void SHL_imm(u64 insn);
252 void SHR_reg(u64 insn);
253 void SHR_cbuf(u64 insn);
254 void SHR_imm(u64 insn);
255 void SSY(u64 insn);
256 void ST(u64 insn);
257 void STG(u64 insn);
258 void STL(u64 insn);
259 void STP(u64 insn);
260 void STS(u64 insn);
261 void SUATOM_cas(u64 insn);
262 void SULD(u64 insn);
263 void SURED(u64 insn);
264 void SUST(u64 insn);
265 void SYNC(u64 insn);
266 void TEX(u64 insn);
267 void TEX_b(u64 insn);
268 void TEXS(u64 insn);
269 void TLD(u64 insn);
270 void TLD_b(u64 insn);
271 void TLD4(u64 insn);
272 void TLD4_b(u64 insn);
273 void TLD4S(u64 insn);
274 void TLDS(u64 insn);
275 void TMML(u64 insn);
276 void TMML_b(u64 insn);
277 void TXA(u64 insn);
278 void TXD(u64 insn);
279 void TXD_b(u64 insn);
280 void TXQ(u64 insn);
281 void TXQ_b(u64 insn);
282 void VABSDIFF(u64 insn);
283 void VABSDIFF4(u64 insn);
284 void VADD(u64 insn);
285 void VMAD(u64 insn);
286 void VMNMX(u64 insn);
287 void VOTE(u64 insn);
288 void VOTE_vtg(u64 insn);
289 void VSET(u64 insn);
290 void VSETP(u64 insn);
291 void VSHL(u64 insn);
292 void VSHR(u64 insn);
293 void XMAD_reg(u64 insn);
294 void XMAD_rc(u64 insn);
295 void XMAD_cr(u64 insn);
296 void XMAD_imm(u64 insn);
297
298 [[nodiscard]] IR::U32 X(IR::Reg reg);
299 void X(IR::Reg dest_reg, const IR::U32& value);
300
301 [[nodiscard]] IR::U32 GetCbuf(u64 insn);
302
303 [[nodiscard]] IR::U32 GetImm(u64 insn);
304
305 void SetZFlag(const IR::U1& value);
306 void SetSFlag(const IR::U1& value);
307 void SetCFlag(const IR::U1& value);
308 void SetOFlag(const IR::U1& value);
309
310 void ResetZero();
311 void ResetSFlag();
312 void ResetCFlag();
313 void ResetOFlag();
314};
315
316} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
new file mode 100644
index 000000000..23512db1a
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
@@ -0,0 +1,92 @@
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 "common/bit_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class InterpolationMode : u64 {
14 Pass = 0,
15 Multiply = 1,
16 Constant = 2,
17 Sc = 3,
18};
19
20enum class SampleMode : u64 {
21 Default = 0,
22 Centroid = 1,
23 Offset = 2,
24};
25} // Anonymous namespace
26
27void TranslatorVisitor::IPA(u64 insn) {
28 // IPA is the instruction used to read varyings from a fragment shader.
29 // gl_FragCoord is mapped to the gl_Position attribute.
30 // It yields unknown results when used outside of the fragment shader stage.
31 union {
32 u64 raw;
33 BitField<0, 8, IR::Reg> dest_reg;
34 BitField<8, 8, IR::Reg> index_reg;
35 BitField<20, 8, IR::Reg> multiplier;
36 BitField<30, 8, IR::Attribute> attribute;
37 BitField<38, 1, u64> idx;
38 BitField<51, 1, u64> sat;
39 BitField<52, 2, SampleMode> sample_mode;
40 BitField<54, 2, InterpolationMode> interpolation_mode;
41 } const ipa{insn};
42
43 // Indexed IPAs are used for indexed varyings.
44 // For example:
45 //
46 // in vec4 colors[4];
47 // uniform int idx;
48 // void main() {
49 // gl_FragColor = colors[idx];
50 // }
51 const bool is_indexed{ipa.idx != 0 && ipa.index_reg != IR::Reg::RZ};
52 if (is_indexed) {
53 throw NotImplementedException("IPA.IDX");
54 }
55
56 const IR::Attribute attribute{ipa.attribute};
57 IR::U32 value{ir.GetAttribute(attribute)};
58 if (IR::IsGeneric(attribute)) {
59 // const bool is_perspective{UnimplementedReadHeader(GenericAttributeIndex(attribute))};
60 const bool is_perspective{false};
61 if (is_perspective) {
62 const IR::U32 rcp_position_w{ir.FPRecip(ir.GetAttribute(IR::Attribute::PositionW))};
63 value = ir.FPMul(value, rcp_position_w);
64 }
65 }
66
67 switch (ipa.interpolation_mode) {
68 case InterpolationMode::Pass:
69 break;
70 case InterpolationMode::Multiply:
71 value = ir.FPMul(value, ir.GetReg(ipa.multiplier));
72 break;
73 case InterpolationMode::Constant:
74 throw NotImplementedException("IPA.CONSTANT");
75 case InterpolationMode::Sc:
76 throw NotImplementedException("IPA.SC");
77 }
78
79 // Saturated IPAs are generally generated out of clamped varyings.
80 // For example: clamp(some_varying, 0.0, 1.0)
81 const bool is_saturated{ipa.sat != 0};
82 if (is_saturated) {
83 if (attribute == IR::Attribute::FrontFace) {
84 throw NotImplementedException("IPA.SAT on FrontFace");
85 }
86 value = ir.FPSaturate(value);
87 }
88
89 ir.SetReg(ipa.dest_reg, value);
90}
91
92} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
new file mode 100644
index 000000000..d8fd387cf
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
@@ -0,0 +1,90 @@
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 "common/bit_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class StoreSize : u64 {
14 U8,
15 S8,
16 U16,
17 S16,
18 B32,
19 B64,
20 B128,
21};
22
23// See Table 28 in https://docs.nvidia.com/cuda/parallel-thread-execution/index.html
24enum class StoreCache : u64 {
25 WB, // Cache write-back all coherent levels
26 CG, // Cache at global level
27 CS, // Cache streaming, likely to be accessed once
28 WT, // Cache write-through (to system memory)
29};
30} // Anonymous namespace
31
32void TranslatorVisitor::STG(u64 insn) {
33 // STG stores registers into global memory.
34 union {
35 u64 raw;
36 BitField<0, 8, IR::Reg> data_reg;
37 BitField<8, 8, IR::Reg> addr_reg;
38 BitField<45, 1, u64> e;
39 BitField<46, 2, StoreCache> cache;
40 BitField<48, 3, StoreSize> size;
41 } const stg{insn};
42
43 const IR::U64 address{[&]() -> IR::U64 {
44 if (stg.e == 0) {
45 // STG without .E uses a 32-bit pointer, zero-extend it
46 return ir.ConvertU(64, X(stg.addr_reg));
47 }
48 if (!IR::IsAligned(stg.addr_reg, 2)) {
49 throw NotImplementedException("Unaligned address register");
50 }
51 // Pack two registers to build the 32-bit address
52 return ir.PackUint2x32(ir.CompositeConstruct(X(stg.addr_reg), X(stg.addr_reg + 1)));
53 }()};
54
55 switch (stg.size) {
56 case StoreSize::U8:
57 ir.WriteGlobalU8(address, X(stg.data_reg));
58 break;
59 case StoreSize::S8:
60 ir.WriteGlobalS8(address, X(stg.data_reg));
61 break;
62 case StoreSize::U16:
63 ir.WriteGlobalU16(address, X(stg.data_reg));
64 break;
65 case StoreSize::S16:
66 ir.WriteGlobalS16(address, X(stg.data_reg));
67 break;
68 case StoreSize::B32:
69 ir.WriteGlobal32(address, X(stg.data_reg));
70 break;
71 case StoreSize::B64: {
72 if (!IR::IsAligned(stg.data_reg, 2)) {
73 throw NotImplementedException("Unaligned data registers");
74 }
75 const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1))};
76 ir.WriteGlobal64(address, vector);
77 break;
78 }
79 case StoreSize::B128:
80 if (!IR::IsAligned(stg.data_reg, 4)) {
81 throw NotImplementedException("Unaligned data registers");
82 }
83 const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1),
84 X(stg.data_reg + 2), X(stg.data_reg + 3))};
85 ir.WriteGlobal128(address, vector);
86 break;
87 }
88}
89
90} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
new file mode 100644
index 000000000..c907c1ffb
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -0,0 +1,1105 @@
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 "common/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/opcode.h"
8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
9
10#include "shader_recompiler/ir_opt/passes.h"
11
12namespace Shader::Maxwell {
13
14[[maybe_unused]] static inline void DumpOptimized(IR::Block& block) {
15 auto raw{IR::DumpBlock(block)};
16
17 Optimization::GetSetElimination(block);
18 Optimization::DeadCodeEliminationPass(block);
19 Optimization::IdentityRemovalPass(block);
20 auto dumped{IR::DumpBlock(block)};
21
22 fmt::print(stderr, "{}", dumped);
23}
24
25[[noreturn]] static void ThrowNotImplemented(Opcode opcode) {
26 throw NotImplementedException("Instruction {} is not implemented", opcode);
27}
28
29void TranslatorVisitor::AL2P(u64) {
30 ThrowNotImplemented(Opcode::AL2P);
31}
32
33void TranslatorVisitor::ALD(u64) {
34 ThrowNotImplemented(Opcode::ALD);
35}
36
37void TranslatorVisitor::AST(u64) {
38 ThrowNotImplemented(Opcode::AST);
39}
40
41void TranslatorVisitor::ATOM_cas(u64) {
42 ThrowNotImplemented(Opcode::ATOM_cas);
43}
44
45void TranslatorVisitor::ATOM(u64) {
46 ThrowNotImplemented(Opcode::ATOM);
47}
48
49void TranslatorVisitor::ATOMS_cas(u64) {
50 ThrowNotImplemented(Opcode::ATOMS_cas);
51}
52
53void TranslatorVisitor::ATOMS(u64) {
54 ThrowNotImplemented(Opcode::ATOMS);
55}
56
57void TranslatorVisitor::B2R(u64) {
58 ThrowNotImplemented(Opcode::B2R);
59}
60
61void TranslatorVisitor::BAR(u64) {
62 ThrowNotImplemented(Opcode::BAR);
63}
64
65void TranslatorVisitor::BFE_reg(u64) {
66 ThrowNotImplemented(Opcode::BFE_reg);
67}
68
69void TranslatorVisitor::BFE_cbuf(u64) {
70 ThrowNotImplemented(Opcode::BFE_cbuf);
71}
72
73void TranslatorVisitor::BFE_imm(u64) {
74 ThrowNotImplemented(Opcode::BFE_imm);
75}
76
77void TranslatorVisitor::BFI_reg(u64) {
78 ThrowNotImplemented(Opcode::BFI_reg);
79}
80
81void TranslatorVisitor::BFI_rc(u64) {
82 ThrowNotImplemented(Opcode::BFI_rc);
83}
84
85void TranslatorVisitor::BFI_cr(u64) {
86 ThrowNotImplemented(Opcode::BFI_cr);
87}
88
89void TranslatorVisitor::BFI_imm(u64) {
90 ThrowNotImplemented(Opcode::BFI_imm);
91}
92
93void TranslatorVisitor::BPT(u64) {
94 ThrowNotImplemented(Opcode::BPT);
95}
96
97void TranslatorVisitor::BRA(u64) {
98 ThrowNotImplemented(Opcode::BRA);
99}
100
101void TranslatorVisitor::BRK(u64) {
102 ThrowNotImplemented(Opcode::BRK);
103}
104
105void TranslatorVisitor::BRX(u64) {
106 ThrowNotImplemented(Opcode::BRX);
107}
108
109void TranslatorVisitor::CAL(u64) {
110 ThrowNotImplemented(Opcode::CAL);
111}
112
113void TranslatorVisitor::CCTL(u64) {
114 ThrowNotImplemented(Opcode::CCTL);
115}
116
117void TranslatorVisitor::CCTLL(u64) {
118 ThrowNotImplemented(Opcode::CCTLL);
119}
120
121void TranslatorVisitor::CONT(u64) {
122 ThrowNotImplemented(Opcode::CONT);
123}
124
125void TranslatorVisitor::CS2R(u64) {
126 ThrowNotImplemented(Opcode::CS2R);
127}
128
129void TranslatorVisitor::CSET(u64) {
130 ThrowNotImplemented(Opcode::CSET);
131}
132
133void TranslatorVisitor::CSETP(u64) {
134 ThrowNotImplemented(Opcode::CSETP);
135}
136
137void TranslatorVisitor::DADD_reg(u64) {
138 ThrowNotImplemented(Opcode::DADD_reg);
139}
140
141void TranslatorVisitor::DADD_cbuf(u64) {
142 ThrowNotImplemented(Opcode::DADD_cbuf);
143}
144
145void TranslatorVisitor::DADD_imm(u64) {
146 ThrowNotImplemented(Opcode::DADD_imm);
147}
148
149void TranslatorVisitor::DEPBAR(u64) {
150 ThrowNotImplemented(Opcode::DEPBAR);
151}
152
153void TranslatorVisitor::DFMA_reg(u64) {
154 ThrowNotImplemented(Opcode::DFMA_reg);
155}
156
157void TranslatorVisitor::DFMA_rc(u64) {
158 ThrowNotImplemented(Opcode::DFMA_rc);
159}
160
161void TranslatorVisitor::DFMA_cr(u64) {
162 ThrowNotImplemented(Opcode::DFMA_cr);
163}
164
165void TranslatorVisitor::DFMA_imm(u64) {
166 ThrowNotImplemented(Opcode::DFMA_imm);
167}
168
169void TranslatorVisitor::DMNMX_reg(u64) {
170 ThrowNotImplemented(Opcode::DMNMX_reg);
171}
172
173void TranslatorVisitor::DMNMX_cbuf(u64) {
174 ThrowNotImplemented(Opcode::DMNMX_cbuf);
175}
176
177void TranslatorVisitor::DMNMX_imm(u64) {
178 ThrowNotImplemented(Opcode::DMNMX_imm);
179}
180
181void TranslatorVisitor::DMUL_reg(u64) {
182 ThrowNotImplemented(Opcode::DMUL_reg);
183}
184
185void TranslatorVisitor::DMUL_cbuf(u64) {
186 ThrowNotImplemented(Opcode::DMUL_cbuf);
187}
188
189void TranslatorVisitor::DMUL_imm(u64) {
190 ThrowNotImplemented(Opcode::DMUL_imm);
191}
192
193void TranslatorVisitor::DSET_reg(u64) {
194 ThrowNotImplemented(Opcode::DSET_reg);
195}
196
197void TranslatorVisitor::DSET_cbuf(u64) {
198 ThrowNotImplemented(Opcode::DSET_cbuf);
199}
200
201void TranslatorVisitor::DSET_imm(u64) {
202 ThrowNotImplemented(Opcode::DSET_imm);
203}
204
205void TranslatorVisitor::DSETP_reg(u64) {
206 ThrowNotImplemented(Opcode::DSETP_reg);
207}
208
209void TranslatorVisitor::DSETP_cbuf(u64) {
210 ThrowNotImplemented(Opcode::DSETP_cbuf);
211}
212
213void TranslatorVisitor::DSETP_imm(u64) {
214 ThrowNotImplemented(Opcode::DSETP_imm);
215}
216
217void TranslatorVisitor::EXIT(u64) {
218 throw LogicError("Visting EXIT instruction");
219}
220
221void TranslatorVisitor::F2F_reg(u64) {
222 ThrowNotImplemented(Opcode::F2F_reg);
223}
224
225void TranslatorVisitor::F2F_cbuf(u64) {
226 ThrowNotImplemented(Opcode::F2F_cbuf);
227}
228
229void TranslatorVisitor::F2F_imm(u64) {
230 ThrowNotImplemented(Opcode::F2F_imm);
231}
232
233void TranslatorVisitor::FADD_reg(u64) {
234 ThrowNotImplemented(Opcode::FADD_reg);
235}
236
237void TranslatorVisitor::FADD_cbuf(u64) {
238 ThrowNotImplemented(Opcode::FADD_cbuf);
239}
240
241void TranslatorVisitor::FADD_imm(u64) {
242 ThrowNotImplemented(Opcode::FADD_imm);
243}
244
245void TranslatorVisitor::FADD32I(u64) {
246 ThrowNotImplemented(Opcode::FADD32I);
247}
248
249void TranslatorVisitor::FCHK_reg(u64) {
250 ThrowNotImplemented(Opcode::FCHK_reg);
251}
252
253void TranslatorVisitor::FCHK_cbuf(u64) {
254 ThrowNotImplemented(Opcode::FCHK_cbuf);
255}
256
257void TranslatorVisitor::FCHK_imm(u64) {
258 ThrowNotImplemented(Opcode::FCHK_imm);
259}
260
261void TranslatorVisitor::FCMP_reg(u64) {
262 ThrowNotImplemented(Opcode::FCMP_reg);
263}
264
265void TranslatorVisitor::FCMP_rc(u64) {
266 ThrowNotImplemented(Opcode::FCMP_rc);
267}
268
269void TranslatorVisitor::FCMP_cr(u64) {
270 ThrowNotImplemented(Opcode::FCMP_cr);
271}
272
273void TranslatorVisitor::FCMP_imm(u64) {
274 ThrowNotImplemented(Opcode::FCMP_imm);
275}
276
277void TranslatorVisitor::FFMA_reg(u64) {
278 ThrowNotImplemented(Opcode::FFMA_reg);
279}
280
281void TranslatorVisitor::FFMA_rc(u64) {
282 ThrowNotImplemented(Opcode::FFMA_rc);
283}
284
285void TranslatorVisitor::FFMA_cr(u64) {
286 ThrowNotImplemented(Opcode::FFMA_cr);
287}
288
289void TranslatorVisitor::FFMA_imm(u64) {
290 ThrowNotImplemented(Opcode::FFMA_imm);
291}
292
293void TranslatorVisitor::FFMA32I(u64) {
294 ThrowNotImplemented(Opcode::FFMA32I);
295}
296
297void TranslatorVisitor::FLO_reg(u64) {
298 ThrowNotImplemented(Opcode::FLO_reg);
299}
300
301void TranslatorVisitor::FLO_cbuf(u64) {
302 ThrowNotImplemented(Opcode::FLO_cbuf);
303}
304
305void TranslatorVisitor::FLO_imm(u64) {
306 ThrowNotImplemented(Opcode::FLO_imm);
307}
308
309void TranslatorVisitor::FMNMX_reg(u64) {
310 ThrowNotImplemented(Opcode::FMNMX_reg);
311}
312
313void TranslatorVisitor::FMNMX_cbuf(u64) {
314 ThrowNotImplemented(Opcode::FMNMX_cbuf);
315}
316
317void TranslatorVisitor::FMNMX_imm(u64) {
318 ThrowNotImplemented(Opcode::FMNMX_imm);
319}
320
321void TranslatorVisitor::FMUL_reg(u64) {
322 ThrowNotImplemented(Opcode::FMUL_reg);
323}
324
325void TranslatorVisitor::FMUL_cbuf(u64) {
326 ThrowNotImplemented(Opcode::FMUL_cbuf);
327}
328
329void TranslatorVisitor::FMUL_imm(u64) {
330 ThrowNotImplemented(Opcode::FMUL_imm);
331}
332
333void TranslatorVisitor::FMUL32I(u64) {
334 ThrowNotImplemented(Opcode::FMUL32I);
335}
336
337void TranslatorVisitor::FSET_reg(u64) {
338 ThrowNotImplemented(Opcode::FSET_reg);
339}
340
341void TranslatorVisitor::FSET_cbuf(u64) {
342 ThrowNotImplemented(Opcode::FSET_cbuf);
343}
344
345void TranslatorVisitor::FSET_imm(u64) {
346 ThrowNotImplemented(Opcode::FSET_imm);
347}
348
349void TranslatorVisitor::FSETP_reg(u64) {
350 ThrowNotImplemented(Opcode::FSETP_reg);
351}
352
353void TranslatorVisitor::FSETP_cbuf(u64) {
354 ThrowNotImplemented(Opcode::FSETP_cbuf);
355}
356
357void TranslatorVisitor::FSETP_imm(u64) {
358 ThrowNotImplemented(Opcode::FSETP_imm);
359}
360
361void TranslatorVisitor::FSWZADD(u64) {
362 ThrowNotImplemented(Opcode::FSWZADD);
363}
364
365void TranslatorVisitor::GETCRSPTR(u64) {
366 ThrowNotImplemented(Opcode::GETCRSPTR);
367}
368
369void TranslatorVisitor::GETLMEMBASE(u64) {
370 ThrowNotImplemented(Opcode::GETLMEMBASE);
371}
372
373void TranslatorVisitor::HADD2_reg(u64) {
374 ThrowNotImplemented(Opcode::HADD2_reg);
375}
376
377void TranslatorVisitor::HADD2_cbuf(u64) {
378 ThrowNotImplemented(Opcode::HADD2_cbuf);
379}
380
381void TranslatorVisitor::HADD2_imm(u64) {
382 ThrowNotImplemented(Opcode::HADD2_imm);
383}
384
385void TranslatorVisitor::HADD2_32I(u64) {
386 ThrowNotImplemented(Opcode::HADD2_32I);
387}
388
389void TranslatorVisitor::HFMA2_reg(u64) {
390 ThrowNotImplemented(Opcode::HFMA2_reg);
391}
392
393void TranslatorVisitor::HFMA2_rc(u64) {
394 ThrowNotImplemented(Opcode::HFMA2_rc);
395}
396
397void TranslatorVisitor::HFMA2_cr(u64) {
398 ThrowNotImplemented(Opcode::HFMA2_cr);
399}
400
401void TranslatorVisitor::HFMA2_imm(u64) {
402 ThrowNotImplemented(Opcode::HFMA2_imm);
403}
404
405void TranslatorVisitor::HFMA2_32I(u64) {
406 ThrowNotImplemented(Opcode::HFMA2_32I);
407}
408
409void TranslatorVisitor::HMUL2_reg(u64) {
410 ThrowNotImplemented(Opcode::HMUL2_reg);
411}
412
413void TranslatorVisitor::HMUL2_cbuf(u64) {
414 ThrowNotImplemented(Opcode::HMUL2_cbuf);
415}
416
417void TranslatorVisitor::HMUL2_imm(u64) {
418 ThrowNotImplemented(Opcode::HMUL2_imm);
419}
420
421void TranslatorVisitor::HMUL2_32I(u64) {
422 ThrowNotImplemented(Opcode::HMUL2_32I);
423}
424
425void TranslatorVisitor::HSET2_reg(u64) {
426 ThrowNotImplemented(Opcode::HSET2_reg);
427}
428
429void TranslatorVisitor::HSET2_cbuf(u64) {
430 ThrowNotImplemented(Opcode::HSET2_cbuf);
431}
432
433void TranslatorVisitor::HSET2_imm(u64) {
434 ThrowNotImplemented(Opcode::HSET2_imm);
435}
436
437void TranslatorVisitor::HSETP2_reg(u64) {
438 ThrowNotImplemented(Opcode::HSETP2_reg);
439}
440
441void TranslatorVisitor::HSETP2_cbuf(u64) {
442 ThrowNotImplemented(Opcode::HSETP2_cbuf);
443}
444
445void TranslatorVisitor::HSETP2_imm(u64) {
446 ThrowNotImplemented(Opcode::HSETP2_imm);
447}
448
449void TranslatorVisitor::I2F_reg(u64) {
450 ThrowNotImplemented(Opcode::I2F_reg);
451}
452
453void TranslatorVisitor::I2F_cbuf(u64) {
454 ThrowNotImplemented(Opcode::I2F_cbuf);
455}
456
457void TranslatorVisitor::I2F_imm(u64) {
458 ThrowNotImplemented(Opcode::I2F_imm);
459}
460
461void TranslatorVisitor::I2I_reg(u64) {
462 ThrowNotImplemented(Opcode::I2I_reg);
463}
464
465void TranslatorVisitor::I2I_cbuf(u64) {
466 ThrowNotImplemented(Opcode::I2I_cbuf);
467}
468
469void TranslatorVisitor::I2I_imm(u64) {
470 ThrowNotImplemented(Opcode::I2I_imm);
471}
472
473void TranslatorVisitor::IADD_reg(u64) {
474 ThrowNotImplemented(Opcode::IADD_reg);
475}
476
477void TranslatorVisitor::IADD_cbuf(u64) {
478 ThrowNotImplemented(Opcode::IADD_cbuf);
479}
480
481void TranslatorVisitor::IADD_imm(u64) {
482 ThrowNotImplemented(Opcode::IADD_imm);
483}
484
485void TranslatorVisitor::IADD3_reg(u64) {
486 ThrowNotImplemented(Opcode::IADD3_reg);
487}
488
489void TranslatorVisitor::IADD3_cbuf(u64) {
490 ThrowNotImplemented(Opcode::IADD3_cbuf);
491}
492
493void TranslatorVisitor::IADD3_imm(u64) {
494 ThrowNotImplemented(Opcode::IADD3_imm);
495}
496
497void TranslatorVisitor::IADD32I(u64) {
498 ThrowNotImplemented(Opcode::IADD32I);
499}
500
501void TranslatorVisitor::ICMP_reg(u64) {
502 ThrowNotImplemented(Opcode::ICMP_reg);
503}
504
505void TranslatorVisitor::ICMP_rc(u64) {
506 ThrowNotImplemented(Opcode::ICMP_rc);
507}
508
509void TranslatorVisitor::ICMP_cr(u64) {
510 ThrowNotImplemented(Opcode::ICMP_cr);
511}
512
513void TranslatorVisitor::ICMP_imm(u64) {
514 ThrowNotImplemented(Opcode::ICMP_imm);
515}
516
517void TranslatorVisitor::IDE(u64) {
518 ThrowNotImplemented(Opcode::IDE);
519}
520
521void TranslatorVisitor::IDP_reg(u64) {
522 ThrowNotImplemented(Opcode::IDP_reg);
523}
524
525void TranslatorVisitor::IDP_imm(u64) {
526 ThrowNotImplemented(Opcode::IDP_imm);
527}
528
529void TranslatorVisitor::IMAD_reg(u64) {
530 ThrowNotImplemented(Opcode::IMAD_reg);
531}
532
533void TranslatorVisitor::IMAD_rc(u64) {
534 ThrowNotImplemented(Opcode::IMAD_rc);
535}
536
537void TranslatorVisitor::IMAD_cr(u64) {
538 ThrowNotImplemented(Opcode::IMAD_cr);
539}
540
541void TranslatorVisitor::IMAD_imm(u64) {
542 ThrowNotImplemented(Opcode::IMAD_imm);
543}
544
545void TranslatorVisitor::IMAD32I(u64) {
546 ThrowNotImplemented(Opcode::IMAD32I);
547}
548
549void TranslatorVisitor::IMADSP_reg(u64) {
550 ThrowNotImplemented(Opcode::IMADSP_reg);
551}
552
553void TranslatorVisitor::IMADSP_rc(u64) {
554 ThrowNotImplemented(Opcode::IMADSP_rc);
555}
556
557void TranslatorVisitor::IMADSP_cr(u64) {
558 ThrowNotImplemented(Opcode::IMADSP_cr);
559}
560
561void TranslatorVisitor::IMADSP_imm(u64) {
562 ThrowNotImplemented(Opcode::IMADSP_imm);
563}
564
565void TranslatorVisitor::IMNMX_reg(u64) {
566 ThrowNotImplemented(Opcode::IMNMX_reg);
567}
568
569void TranslatorVisitor::IMNMX_cbuf(u64) {
570 ThrowNotImplemented(Opcode::IMNMX_cbuf);
571}
572
573void TranslatorVisitor::IMNMX_imm(u64) {
574 ThrowNotImplemented(Opcode::IMNMX_imm);
575}
576
577void TranslatorVisitor::IMUL_reg(u64) {
578 ThrowNotImplemented(Opcode::IMUL_reg);
579}
580
581void TranslatorVisitor::IMUL_cbuf(u64) {
582 ThrowNotImplemented(Opcode::IMUL_cbuf);
583}
584
585void TranslatorVisitor::IMUL_imm(u64) {
586 ThrowNotImplemented(Opcode::IMUL_imm);
587}
588
589void TranslatorVisitor::IMUL32I(u64) {
590 ThrowNotImplemented(Opcode::IMUL32I);
591}
592
593void TranslatorVisitor::ISBERD(u64) {
594 ThrowNotImplemented(Opcode::ISBERD);
595}
596
597void TranslatorVisitor::ISCADD_reg(u64) {
598 ThrowNotImplemented(Opcode::ISCADD_reg);
599}
600
601void TranslatorVisitor::ISCADD_cbuf(u64) {
602 ThrowNotImplemented(Opcode::ISCADD_cbuf);
603}
604
605void TranslatorVisitor::ISCADD_imm(u64) {
606 ThrowNotImplemented(Opcode::ISCADD_imm);
607}
608
609void TranslatorVisitor::ISCADD32I(u64) {
610 ThrowNotImplemented(Opcode::ISCADD32I);
611}
612
613void TranslatorVisitor::ISET_reg(u64) {
614 ThrowNotImplemented(Opcode::ISET_reg);
615}
616
617void TranslatorVisitor::ISET_cbuf(u64) {
618 ThrowNotImplemented(Opcode::ISET_cbuf);
619}
620
621void TranslatorVisitor::ISET_imm(u64) {
622 ThrowNotImplemented(Opcode::ISET_imm);
623}
624
625void TranslatorVisitor::ISETP_reg(u64) {
626 ThrowNotImplemented(Opcode::ISETP_reg);
627}
628
629void TranslatorVisitor::ISETP_cbuf(u64) {
630 ThrowNotImplemented(Opcode::ISETP_cbuf);
631}
632
633void TranslatorVisitor::ISETP_imm(u64) {
634 ThrowNotImplemented(Opcode::ISETP_imm);
635}
636
637void TranslatorVisitor::JCAL(u64) {
638 ThrowNotImplemented(Opcode::JCAL);
639}
640
641void TranslatorVisitor::JMP(u64) {
642 ThrowNotImplemented(Opcode::JMP);
643}
644
645void TranslatorVisitor::JMX(u64) {
646 ThrowNotImplemented(Opcode::JMX);
647}
648
649void TranslatorVisitor::KIL(u64) {
650 ThrowNotImplemented(Opcode::KIL);
651}
652
653void TranslatorVisitor::LD(u64) {
654 ThrowNotImplemented(Opcode::LD);
655}
656
657void TranslatorVisitor::LDC(u64) {
658 ThrowNotImplemented(Opcode::LDC);
659}
660
661void TranslatorVisitor::LDG(u64) {
662 ThrowNotImplemented(Opcode::LDG);
663}
664
665void TranslatorVisitor::LDL(u64) {
666 ThrowNotImplemented(Opcode::LDL);
667}
668
669void TranslatorVisitor::LDS(u64) {
670 ThrowNotImplemented(Opcode::LDS);
671}
672
673void TranslatorVisitor::LEA_hi_reg(u64) {
674 ThrowNotImplemented(Opcode::LEA_hi_reg);
675}
676
677void TranslatorVisitor::LEA_hi_cbuf(u64) {
678 ThrowNotImplemented(Opcode::LEA_hi_cbuf);
679}
680
681void TranslatorVisitor::LEA_lo_reg(u64) {
682 ThrowNotImplemented(Opcode::LEA_lo_reg);
683}
684
685void TranslatorVisitor::LEA_lo_cbuf(u64) {
686 ThrowNotImplemented(Opcode::LEA_lo_cbuf);
687}
688
689void TranslatorVisitor::LEA_lo_imm(u64) {
690 ThrowNotImplemented(Opcode::LEA_lo_imm);
691}
692
693void TranslatorVisitor::LEPC(u64) {
694 ThrowNotImplemented(Opcode::LEPC);
695}
696
697void TranslatorVisitor::LONGJMP(u64) {
698 ThrowNotImplemented(Opcode::LONGJMP);
699}
700
701void TranslatorVisitor::LOP_reg(u64) {
702 ThrowNotImplemented(Opcode::LOP_reg);
703}
704
705void TranslatorVisitor::LOP_cbuf(u64) {
706 ThrowNotImplemented(Opcode::LOP_cbuf);
707}
708
709void TranslatorVisitor::LOP_imm(u64) {
710 ThrowNotImplemented(Opcode::LOP_imm);
711}
712
713void TranslatorVisitor::LOP3_reg(u64) {
714 ThrowNotImplemented(Opcode::LOP3_reg);
715}
716
717void TranslatorVisitor::LOP3_cbuf(u64) {
718 ThrowNotImplemented(Opcode::LOP3_cbuf);
719}
720
721void TranslatorVisitor::LOP3_imm(u64) {
722 ThrowNotImplemented(Opcode::LOP3_imm);
723}
724
725void TranslatorVisitor::LOP32I(u64) {
726 ThrowNotImplemented(Opcode::LOP32I);
727}
728
729void TranslatorVisitor::MEMBAR(u64) {
730 ThrowNotImplemented(Opcode::MEMBAR);
731}
732
733void TranslatorVisitor::MOV32I(u64) {
734 ThrowNotImplemented(Opcode::MOV32I);
735}
736
737void TranslatorVisitor::NOP(u64) {
738 ThrowNotImplemented(Opcode::NOP);
739}
740
741void TranslatorVisitor::OUT_reg(u64) {
742 ThrowNotImplemented(Opcode::OUT_reg);
743}
744
745void TranslatorVisitor::OUT_cbuf(u64) {
746 ThrowNotImplemented(Opcode::OUT_cbuf);
747}
748
749void TranslatorVisitor::OUT_imm(u64) {
750 ThrowNotImplemented(Opcode::OUT_imm);
751}
752
753void TranslatorVisitor::P2R_reg(u64) {
754 ThrowNotImplemented(Opcode::P2R_reg);
755}
756
757void TranslatorVisitor::P2R_cbuf(u64) {
758 ThrowNotImplemented(Opcode::P2R_cbuf);
759}
760
761void TranslatorVisitor::P2R_imm(u64) {
762 ThrowNotImplemented(Opcode::P2R_imm);
763}
764
765void TranslatorVisitor::PBK(u64) {
766 // PBK is a no-op
767}
768
769void TranslatorVisitor::PCNT(u64) {
770 ThrowNotImplemented(Opcode::PCNT);
771}
772
773void TranslatorVisitor::PEXIT(u64) {
774 ThrowNotImplemented(Opcode::PEXIT);
775}
776
777void TranslatorVisitor::PIXLD(u64) {
778 ThrowNotImplemented(Opcode::PIXLD);
779}
780
781void TranslatorVisitor::PLONGJMP(u64) {
782 ThrowNotImplemented(Opcode::PLONGJMP);
783}
784
785void TranslatorVisitor::POPC_reg(u64) {
786 ThrowNotImplemented(Opcode::POPC_reg);
787}
788
789void TranslatorVisitor::POPC_cbuf(u64) {
790 ThrowNotImplemented(Opcode::POPC_cbuf);
791}
792
793void TranslatorVisitor::POPC_imm(u64) {
794 ThrowNotImplemented(Opcode::POPC_imm);
795}
796
797void TranslatorVisitor::PRET(u64) {
798 ThrowNotImplemented(Opcode::PRET);
799}
800
801void TranslatorVisitor::PRMT_reg(u64) {
802 ThrowNotImplemented(Opcode::PRMT_reg);
803}
804
805void TranslatorVisitor::PRMT_rc(u64) {
806 ThrowNotImplemented(Opcode::PRMT_rc);
807}
808
809void TranslatorVisitor::PRMT_cr(u64) {
810 ThrowNotImplemented(Opcode::PRMT_cr);
811}
812
813void TranslatorVisitor::PRMT_imm(u64) {
814 ThrowNotImplemented(Opcode::PRMT_imm);
815}
816
817void TranslatorVisitor::PSET(u64) {
818 ThrowNotImplemented(Opcode::PSET);
819}
820
821void TranslatorVisitor::PSETP(u64) {
822 ThrowNotImplemented(Opcode::PSETP);
823}
824
825void TranslatorVisitor::R2B(u64) {
826 ThrowNotImplemented(Opcode::R2B);
827}
828
829void TranslatorVisitor::R2P_reg(u64) {
830 ThrowNotImplemented(Opcode::R2P_reg);
831}
832
833void TranslatorVisitor::R2P_cbuf(u64) {
834 ThrowNotImplemented(Opcode::R2P_cbuf);
835}
836
837void TranslatorVisitor::R2P_imm(u64) {
838 ThrowNotImplemented(Opcode::R2P_imm);
839}
840
841void TranslatorVisitor::RAM(u64) {
842 ThrowNotImplemented(Opcode::RAM);
843}
844
845void TranslatorVisitor::RED(u64) {
846 ThrowNotImplemented(Opcode::RED);
847}
848
849void TranslatorVisitor::RET(u64) {
850 ThrowNotImplemented(Opcode::RET);
851}
852
853void TranslatorVisitor::RRO_reg(u64) {
854 ThrowNotImplemented(Opcode::RRO_reg);
855}
856
857void TranslatorVisitor::RRO_cbuf(u64) {
858 ThrowNotImplemented(Opcode::RRO_cbuf);
859}
860
861void TranslatorVisitor::RRO_imm(u64) {
862 ThrowNotImplemented(Opcode::RRO_imm);
863}
864
865void TranslatorVisitor::RTT(u64) {
866 ThrowNotImplemented(Opcode::RTT);
867}
868
869void TranslatorVisitor::S2R(u64) {
870 ThrowNotImplemented(Opcode::S2R);
871}
872
873void TranslatorVisitor::SAM(u64) {
874 ThrowNotImplemented(Opcode::SAM);
875}
876
877void TranslatorVisitor::SEL_reg(u64) {
878 ThrowNotImplemented(Opcode::SEL_reg);
879}
880
881void TranslatorVisitor::SEL_cbuf(u64) {
882 ThrowNotImplemented(Opcode::SEL_cbuf);
883}
884
885void TranslatorVisitor::SEL_imm(u64) {
886 ThrowNotImplemented(Opcode::SEL_imm);
887}
888
889void TranslatorVisitor::SETCRSPTR(u64) {
890 ThrowNotImplemented(Opcode::SETCRSPTR);
891}
892
893void TranslatorVisitor::SETLMEMBASE(u64) {
894 ThrowNotImplemented(Opcode::SETLMEMBASE);
895}
896
897void TranslatorVisitor::SHF_l_reg(u64) {
898 ThrowNotImplemented(Opcode::SHF_l_reg);
899}
900
901void TranslatorVisitor::SHF_l_imm(u64) {
902 ThrowNotImplemented(Opcode::SHF_l_imm);
903}
904
905void TranslatorVisitor::SHF_r_reg(u64) {
906 ThrowNotImplemented(Opcode::SHF_r_reg);
907}
908
909void TranslatorVisitor::SHF_r_imm(u64) {
910 ThrowNotImplemented(Opcode::SHF_r_imm);
911}
912
913void TranslatorVisitor::SHFL(u64) {
914 ThrowNotImplemented(Opcode::SHFL);
915}
916
917void TranslatorVisitor::SHL_reg(u64) {
918 ThrowNotImplemented(Opcode::SHL_reg);
919}
920
921void TranslatorVisitor::SHL_cbuf(u64) {
922 ThrowNotImplemented(Opcode::SHL_cbuf);
923}
924
925void TranslatorVisitor::SHL_imm(u64) {
926 ThrowNotImplemented(Opcode::SHL_imm);
927}
928
929void TranslatorVisitor::SHR_reg(u64) {
930 ThrowNotImplemented(Opcode::SHR_reg);
931}
932
933void TranslatorVisitor::SHR_cbuf(u64) {
934 ThrowNotImplemented(Opcode::SHR_cbuf);
935}
936
937void TranslatorVisitor::SHR_imm(u64) {
938 ThrowNotImplemented(Opcode::SHR_imm);
939}
940
941void TranslatorVisitor::SSY(u64) {
942 ThrowNotImplemented(Opcode::SSY);
943}
944
945void TranslatorVisitor::ST(u64) {
946 ThrowNotImplemented(Opcode::ST);
947}
948
949void TranslatorVisitor::STL(u64) {
950 ThrowNotImplemented(Opcode::STL);
951}
952
953void TranslatorVisitor::STP(u64) {
954 ThrowNotImplemented(Opcode::STP);
955}
956
957void TranslatorVisitor::STS(u64) {
958 ThrowNotImplemented(Opcode::STS);
959}
960
961void TranslatorVisitor::SUATOM_cas(u64) {
962 ThrowNotImplemented(Opcode::SUATOM_cas);
963}
964
965void TranslatorVisitor::SULD(u64) {
966 ThrowNotImplemented(Opcode::SULD);
967}
968
969void TranslatorVisitor::SURED(u64) {
970 ThrowNotImplemented(Opcode::SURED);
971}
972
973void TranslatorVisitor::SUST(u64) {
974 ThrowNotImplemented(Opcode::SUST);
975}
976
977void TranslatorVisitor::SYNC(u64) {
978 ThrowNotImplemented(Opcode::SYNC);
979}
980
981void TranslatorVisitor::TEX(u64) {
982 ThrowNotImplemented(Opcode::TEX);
983}
984
985void TranslatorVisitor::TEX_b(u64) {
986 ThrowNotImplemented(Opcode::TEX_b);
987}
988
989void TranslatorVisitor::TEXS(u64) {
990 ThrowNotImplemented(Opcode::TEXS);
991}
992
993void TranslatorVisitor::TLD(u64) {
994 ThrowNotImplemented(Opcode::TLD);
995}
996
997void TranslatorVisitor::TLD_b(u64) {
998 ThrowNotImplemented(Opcode::TLD_b);
999}
1000
1001void TranslatorVisitor::TLD4(u64) {
1002 ThrowNotImplemented(Opcode::TLD4);
1003}
1004
1005void TranslatorVisitor::TLD4_b(u64) {
1006 ThrowNotImplemented(Opcode::TLD4_b);
1007}
1008
1009void TranslatorVisitor::TLD4S(u64) {
1010 ThrowNotImplemented(Opcode::TLD4S);
1011}
1012
1013void TranslatorVisitor::TLDS(u64) {
1014 ThrowNotImplemented(Opcode::TLDS);
1015}
1016
1017void TranslatorVisitor::TMML(u64) {
1018 ThrowNotImplemented(Opcode::TMML);
1019}
1020
1021void TranslatorVisitor::TMML_b(u64) {
1022 ThrowNotImplemented(Opcode::TMML_b);
1023}
1024
1025void TranslatorVisitor::TXA(u64) {
1026 ThrowNotImplemented(Opcode::TXA);
1027}
1028
1029void TranslatorVisitor::TXD(u64) {
1030 ThrowNotImplemented(Opcode::TXD);
1031}
1032
1033void TranslatorVisitor::TXD_b(u64) {
1034 ThrowNotImplemented(Opcode::TXD_b);
1035}
1036
1037void TranslatorVisitor::TXQ(u64) {
1038 ThrowNotImplemented(Opcode::TXQ);
1039}
1040
1041void TranslatorVisitor::TXQ_b(u64) {
1042 ThrowNotImplemented(Opcode::TXQ_b);
1043}
1044
1045void TranslatorVisitor::VABSDIFF(u64) {
1046 ThrowNotImplemented(Opcode::VABSDIFF);
1047}
1048
1049void TranslatorVisitor::VABSDIFF4(u64) {
1050 ThrowNotImplemented(Opcode::VABSDIFF4);
1051}
1052
1053void TranslatorVisitor::VADD(u64) {
1054 ThrowNotImplemented(Opcode::VADD);
1055}
1056
1057void TranslatorVisitor::VMAD(u64) {
1058 ThrowNotImplemented(Opcode::VMAD);
1059}
1060
1061void TranslatorVisitor::VMNMX(u64) {
1062 ThrowNotImplemented(Opcode::VMNMX);
1063}
1064
1065void TranslatorVisitor::VOTE(u64) {
1066 ThrowNotImplemented(Opcode::VOTE);
1067}
1068
1069void TranslatorVisitor::VOTE_vtg(u64) {
1070 ThrowNotImplemented(Opcode::VOTE_vtg);
1071}
1072
1073void TranslatorVisitor::VSET(u64) {
1074 ThrowNotImplemented(Opcode::VSET);
1075}
1076
1077void TranslatorVisitor::VSETP(u64) {
1078 ThrowNotImplemented(Opcode::VSETP);
1079}
1080
1081void TranslatorVisitor::VSHL(u64) {
1082 ThrowNotImplemented(Opcode::VSHL);
1083}
1084
1085void TranslatorVisitor::VSHR(u64) {
1086 ThrowNotImplemented(Opcode::VSHR);
1087}
1088
1089void TranslatorVisitor::XMAD_reg(u64) {
1090 ThrowNotImplemented(Opcode::XMAD_reg);
1091}
1092
1093void TranslatorVisitor::XMAD_rc(u64) {
1094 ThrowNotImplemented(Opcode::XMAD_rc);
1095}
1096
1097void TranslatorVisitor::XMAD_cr(u64) {
1098 ThrowNotImplemented(Opcode::XMAD_cr);
1099}
1100
1101void TranslatorVisitor::XMAD_imm(u64) {
1102 ThrowNotImplemented(Opcode::XMAD_imm);
1103}
1104
1105} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp
new file mode 100644
index 000000000..7fa35ba3a
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp
@@ -0,0 +1,45 @@
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 "common/bit_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13union MOV {
14 u64 raw;
15 BitField<0, 8, IR::Reg> dest_reg;
16 BitField<20, 8, IR::Reg> src_reg;
17 BitField<39, 4, u64> mask;
18};
19
20void CheckMask(MOV mov) {
21 if (mov.mask != 0xf) {
22 throw NotImplementedException("Non-full move mask");
23 }
24}
25} // Anonymous namespace
26
27void TranslatorVisitor::MOV_reg(u64 insn) {
28 const MOV mov{insn};
29 CheckMask(mov);
30 X(mov.dest_reg, X(mov.src_reg));
31}
32
33void TranslatorVisitor::MOV_cbuf(u64 insn) {
34 const MOV mov{insn};
35 CheckMask(mov);
36 X(mov.dest_reg, GetCbuf(insn));
37}
38
39void TranslatorVisitor::MOV_imm(u64 insn) {
40 const MOV mov{insn};
41 CheckMask(mov);
42 X(mov.dest_reg, GetImm(insn));
43}
44
45} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/translate.cpp b/src/shader_recompiler/frontend/maxwell/translate/translate.cpp
new file mode 100644
index 000000000..66a306745
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/translate.cpp
@@ -0,0 +1,50 @@
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/environment.h"
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/maxwell/decode.h"
8#include "shader_recompiler/frontend/maxwell/location.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10#include "shader_recompiler/frontend/maxwell/translate/translate.h"
11
12namespace Shader::Maxwell {
13
14template <auto visitor_method>
15static void Invoke(TranslatorVisitor& visitor, Location pc, u64 insn) {
16 using MethodType = decltype(visitor_method);
17 if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, Location, u64>) {
18 (visitor.*visitor_method)(pc, insn);
19 } else if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, u64>) {
20 (visitor.*visitor_method)(insn);
21 } else {
22 (visitor.*visitor_method)();
23 }
24}
25
26IR::Block Translate(Environment& env, const Flow::Block& flow_block) {
27 IR::Block block{flow_block.begin.Offset(), flow_block.end.Offset()};
28 TranslatorVisitor visitor{env, block};
29
30 const Location pc_end{flow_block.end};
31 Location pc{flow_block.begin};
32 while (pc != pc_end) {
33 const u64 insn{env.ReadInstruction(pc.Offset())};
34 const Opcode opcode{Decode(insn)};
35 switch (opcode) {
36#define INST(name, cute, mask) \
37 case Opcode::name: \
38 Invoke<&TranslatorVisitor::name>(visitor, pc, insn); \
39 break;
40#include "shader_recompiler/frontend/maxwell/maxwell.inc"
41#undef OPCODE
42 default:
43 throw LogicError("Invalid opcode {}", opcode);
44 }
45 ++pc;
46 }
47 return block;
48}
49
50} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/translate.h b/src/shader_recompiler/frontend/maxwell/translate/translate.h
new file mode 100644
index 000000000..788742dea
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/translate.h
@@ -0,0 +1,16 @@
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 "shader_recompiler/environment.h"
8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/maxwell/location.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11
12namespace Shader::Maxwell {
13
14[[nodiscard]] IR::Block Translate(Environment& env, const Flow::Block& flow_block);
15
16} // namespace Shader::Maxwell