summaryrefslogtreecommitdiffstats
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <optional>
+#include <ranges>
+#include <string>
+#include <utility>
+
+#include <fmt/format.h>
+
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+#include "shader_recompiler/frontend/maxwell/decode.h"
+#include "shader_recompiler/frontend/maxwell/location.h"
+
+namespace Shader::Maxwell::Flow {
+
+static u32 BranchOffset(Location pc, Instruction inst) {
+ return pc.Offset() + inst.branch.Offset() + 8;
+}
+
+static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) {
+ if (pc <= block.begin || pc >= block.end) {
+ throw InvalidArgument("Invalid address to split={}", pc);
+ }
+ return {
+ Block{
+ .begin{block.begin},
+ .end{pc},
+ .end_class{EndClass::Branch},
+ .id{block.id},
+ .stack{block.stack},
+ .cond{true},
+ .branch_true{new_id},
+ .branch_false{UNREACHABLE_BLOCK_ID},
+ },
+ Block{
+ .begin{pc},
+ .end{block.end},
+ .end_class{block.end_class},
+ .id{new_id},
+ .stack{std::move(block.stack)},
+ .cond{block.cond},
+ .branch_true{block.branch_true},
+ .branch_false{block.branch_false},
+ },
+ };
+}
+
+static Token OpcodeToken(Opcode opcode) {
+ switch (opcode) {
+ case Opcode::PBK:
+ case Opcode::BRK:
+ return Token::PBK;
+ case Opcode::PCNT:
+ case Opcode::CONT:
+ return Token::PBK;
+ case Opcode::PEXIT:
+ case Opcode::EXIT:
+ return Token::PEXIT;
+ case Opcode::PLONGJMP:
+ case Opcode::LONGJMP:
+ return Token::PLONGJMP;
+ case Opcode::PRET:
+ case Opcode::RET:
+ case Opcode::CAL:
+ return Token::PRET;
+ case Opcode::SSY:
+ case Opcode::SYNC:
+ return Token::SSY;
+ default:
+ throw InvalidArgument("{}", opcode);
+ }
+}
+
+static bool IsAbsoluteJump(Opcode opcode) {
+ switch (opcode) {
+ case Opcode::JCAL:
+ case Opcode::JMP:
+ case Opcode::JMX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool HasFlowTest(Opcode opcode) {
+ switch (opcode) {
+ case Opcode::BRA:
+ case Opcode::BRX:
+ case Opcode::EXIT:
+ case Opcode::JMP:
+ case Opcode::JMX:
+ case Opcode::BRK:
+ case Opcode::CONT:
+ case Opcode::LONGJMP:
+ case Opcode::RET:
+ case Opcode::SYNC:
+ return true;
+ case Opcode::CAL:
+ case Opcode::JCAL:
+ return false;
+ default:
+ throw InvalidArgument("Invalid branch {}", opcode);
+ }
+}
+
+static std::string Name(const Block& block) {
+ if (block.begin.IsVirtual()) {
+ return fmt::format("\"Virtual {}\"", block.id);
+ } else {
+ return fmt::format("\"{}\"", block.begin);
+ }
+}
+
+void Stack::Push(Token token, Location target) {
+ entries.push_back({
+ .token{token},
+ .target{target},
+ });
+}
+
+std::pair<Location, Stack> Stack::Pop(Token token) const {
+ const std::optional<Location> pc{Peek(token)};
+ if (!pc) {
+ throw LogicError("Token could not be found");
+ }
+ return {*pc, Remove(token)};
+}
+
+std::optional<Location> Stack::Peek(Token token) const {
+ const auto reverse_entries{entries | std::views::reverse};
+ const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)};
+ if (it == reverse_entries.end()) {
+ return std::nullopt;
+ }
+ return it->target;
+}
+
+Stack Stack::Remove(Token token) const {
+ const auto reverse_entries{entries | std::views::reverse};
+ const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)};
+ const auto pos{std::distance(reverse_entries.begin(), it)};
+ Stack result;
+ result.entries.insert(result.entries.end(), entries.begin(), entries.end() - pos - 1);
+ return result;
+}
+
+bool Block::Contains(Location pc) const noexcept {
+ return pc >= begin && pc < end;
+}
+
+Function::Function(Location start_address)
+ : entrypoint{start_address}, labels{Label{
+ .address{start_address},
+ .block_id{0},
+ .stack{},
+ }} {}
+
+CFG::CFG(Environment& env_, Location start_address) : env{env_} {
+ functions.emplace_back(start_address);
+ for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) {
+ while (!functions[function_id].labels.empty()) {
+ Function& function{functions[function_id]};
+ Label label{function.labels.back()};
+ function.labels.pop_back();
+ AnalyzeLabel(function_id, label);
+ }
+ }
+}
+
+void CFG::AnalyzeLabel(FunctionId function_id, Label& label) {
+ if (InspectVisitedBlocks(function_id, label)) {
+ // Label address has been visited
+ return;
+ }
+ // Try to find the next block
+ Function* function{&functions[function_id]};
+ Location pc{label.address};
+ const auto next{std::upper_bound(function->blocks.begin(), function->blocks.end(), pc,
+ [function](Location pc, u32 block_index) {
+ return pc < function->blocks_data[block_index].begin;
+ })};
+ const auto next_index{std::distance(function->blocks.begin(), next)};
+ const bool is_last{next == function->blocks.end()};
+ Location next_pc;
+ BlockId next_id{UNREACHABLE_BLOCK_ID};
+ if (!is_last) {
+ next_pc = function->blocks_data[*next].begin;
+ next_id = function->blocks_data[*next].id;
+ }
+ // Insert before the next block
+ Block block{
+ .begin{pc},
+ .end{pc},
+ .end_class{EndClass::Branch},
+ .id{label.block_id},
+ .stack{std::move(label.stack)},
+ .cond{true},
+ .branch_true{UNREACHABLE_BLOCK_ID},
+ .branch_false{UNREACHABLE_BLOCK_ID},
+ };
+ // Analyze instructions until it reaches an already visited block or there's a branch
+ bool is_branch{false};
+ while (is_last || pc < next_pc) {
+ is_branch = AnalyzeInst(block, function_id, pc) == AnalysisState::Branch;
+ if (is_branch) {
+ break;
+ }
+ ++pc;
+ }
+ if (!is_branch) {
+ // If the block finished without a branch,
+ // it means that the next instruction is already visited, jump to it
+ block.end = pc;
+ block.cond = true;
+ block.branch_true = next_id;
+ block.branch_false = UNREACHABLE_BLOCK_ID;
+ }
+ // Function's pointer might be invalid, resolve it again
+ function = &functions[function_id];
+ const u32 new_block_index = static_cast<u32>(function->blocks_data.size());
+ function->blocks.insert(function->blocks.begin() + next_index, new_block_index);
+ function->blocks_data.push_back(std::move(block));
+}
+
+bool CFG::InspectVisitedBlocks(FunctionId function_id, const Label& label) {
+ const Location pc{label.address};
+ Function& function{functions[function_id]};
+ const auto it{std::ranges::find_if(function.blocks, [&function, pc](u32 block_index) {
+ return function.blocks_data[block_index].Contains(pc);
+ })};
+ if (it == function.blocks.end()) {
+ // Address has not been visited
+ return false;
+ }
+ Block& block{function.blocks_data[*it]};
+ if (block.begin == pc) {
+ throw LogicError("Dangling branch");
+ }
+ const u32 first_index{*it};
+ const u32 second_index{static_cast<u32>(function.blocks_data.size())};
+ const std::array new_indices{first_index, second_index};
+ std::array split_blocks{Split(std::move(block), pc, label.block_id)};
+ function.blocks_data[*it] = std::move(split_blocks[0]);
+ function.blocks_data.push_back(std::move(split_blocks[1]));
+ function.blocks.insert(function.blocks.erase(it), new_indices.begin(), new_indices.end());
+ return true;
+}
+
+CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Location pc) {
+ const Instruction inst{env.ReadInstruction(pc.Offset())};
+ const Opcode opcode{Decode(inst.raw)};
+ switch (opcode) {
+ case Opcode::BRA:
+ case Opcode::BRX:
+ case Opcode::JMP:
+ case Opcode::JMX:
+ case Opcode::RET:
+ if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) {
+ return AnalysisState::Continue;
+ }
+ switch (opcode) {
+ case Opcode::BRA:
+ case Opcode::JMP:
+ AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode));
+ break;
+ case Opcode::BRX:
+ case Opcode::JMX:
+ AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode));
+ break;
+ case Opcode::RET:
+ block.end_class = EndClass::Return;
+ break;
+ default:
+ break;
+ }
+ block.end = pc;
+ return AnalysisState::Branch;
+ case Opcode::BRK:
+ case Opcode::CONT:
+ case Opcode::LONGJMP:
+ case Opcode::SYNC: {
+ if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) {
+ return AnalysisState::Continue;
+ }
+ const auto [stack_pc, new_stack]{block.stack.Pop(OpcodeToken(opcode))};
+ block.branch_true = AddLabel(block, new_stack, stack_pc, function_id);
+ block.end = pc;
+ return AnalysisState::Branch;
+ }
+ case Opcode::PBK:
+ case Opcode::PCNT:
+ case Opcode::PEXIT:
+ case Opcode::PLONGJMP:
+ case Opcode::SSY:
+ block.stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst));
+ return AnalysisState::Continue;
+ case Opcode::EXIT:
+ return AnalyzeEXIT(block, function_id, pc, inst);
+ case Opcode::PRET:
+ throw NotImplementedException("PRET flow analysis");
+ case Opcode::CAL:
+ case Opcode::JCAL: {
+ const bool is_absolute{IsAbsoluteJump(opcode)};
+ const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
+ // Technically CAL pushes into PRET, but that's implicit in the function call for us
+ // Insert the function into the list if it doesn't exist
+ if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) {
+ functions.push_back(cal_pc);
+ }
+ // Handle CAL like a regular instruction
+ break;
+ }
+ default:
+ break;
+ }
+ const Predicate pred{inst.Pred()};
+ if (pred == Predicate{true} || pred == Predicate{false}) {
+ return AnalysisState::Continue;
+ }
+ const IR::Condition cond{static_cast<IR::Pred>(pred.index), pred.negated};
+ AnalyzeCondInst(block, function_id, pc, EndClass::Branch, cond);
+ return AnalysisState::Branch;
+}
+
+void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc,
+ EndClass insn_end_class, IR::Condition cond) {
+ if (block.begin != pc) {
+ // If the block doesn't start in the conditional instruction
+ // mark it as a label to visit it later
+ block.end = pc;
+ block.cond = true;
+ block.branch_true = AddLabel(block, block.stack, pc, function_id);
+ block.branch_false = UNREACHABLE_BLOCK_ID;
+ return;
+ }
+ // Impersonate the visited block with a virtual block
+ // Jump from this virtual to the real conditional instruction and the next instruction
+ Function& function{functions[function_id]};
+ const BlockId conditional_block_id{++function.current_block_id};
+ function.blocks.push_back(static_cast<u32>(function.blocks_data.size()));
+ Block& virtual_block{function.blocks_data.emplace_back(Block{
+ .begin{}, // Virtual block
+ .end{},
+ .end_class{EndClass::Branch},
+ .id{block.id}, // Impersonating
+ .stack{block.stack},
+ .cond{cond},
+ .branch_true{conditional_block_id},
+ .branch_false{UNREACHABLE_BLOCK_ID},
+ })};
+ // Set the end properties of the conditional instruction and give it a new identity
+ Block& conditional_block{block};
+ conditional_block.end = pc;
+ conditional_block.end_class = insn_end_class;
+ conditional_block.id = conditional_block_id;
+ // Add a label to the instruction after the conditional instruction
+ const BlockId endif_block_id{AddLabel(conditional_block, block.stack, pc + 1, function_id)};
+ // Branch to the next instruction from the virtual block
+ virtual_block.branch_false = endif_block_id;
+ // And branch to it from the conditional instruction if it is a branch
+ if (insn_end_class == EndClass::Branch) {
+ conditional_block.cond = true;
+ conditional_block.branch_true = endif_block_id;
+ conditional_block.branch_false = UNREACHABLE_BLOCK_ID;
+ }
+}
+
+bool CFG::AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst,
+ Opcode opcode) {
+ if (inst.branch.is_cbuf) {
+ throw NotImplementedException("Branch with constant buffer offset");
+ }
+ const Predicate pred{inst.Pred()};
+ if (pred == Predicate{false}) {
+ return false;
+ }
+ const bool has_flow_test{HasFlowTest(opcode)};
+ const IR::FlowTest flow_test{has_flow_test ? inst.branch.flow_test.Value() : IR::FlowTest::T};
+ if (pred != Predicate{true} || flow_test != IR::FlowTest::T) {
+ block.cond = IR::Condition(flow_test, static_cast<IR::Pred>(pred.index), pred.negated);
+ block.branch_false = AddLabel(block, block.stack, pc + 1, function_id);
+ } else {
+ block.cond = true;
+ }
+ return true;
+}
+
+void CFG::AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
+ bool is_absolute) {
+ const Location bra_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
+ block.branch_true = AddLabel(block, block.stack, bra_pc, function_id);
+}
+
+void CFG::AnalyzeBRX(Block&, Location, Instruction, bool is_absolute) {
+ throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX");
+}
+
+void CFG::AnalyzeCAL(Location pc, Instruction inst, bool is_absolute) {
+ const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
+ // Technically CAL pushes into PRET, but that's implicit in the function call for us
+ // Insert the function to the function list if it doesn't exist
+ const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)};
+ if (it == functions.end()) {
+ functions.emplace_back(cal_pc);
+ }
+}
+
+CFG::AnalysisState CFG::AnalyzeEXIT(Block& block, FunctionId function_id, Location pc,
+ Instruction inst) {
+ const IR::FlowTest flow_test{inst.branch.flow_test};
+ const Predicate pred{inst.Pred()};
+ if (pred == Predicate{false} || flow_test == IR::FlowTest::F) {
+ // EXIT will never be taken
+ return AnalysisState::Continue;
+ }
+ if (pred != Predicate{true} || flow_test != IR::FlowTest::T) {
+ if (block.stack.Peek(Token::PEXIT).has_value()) {
+ throw NotImplementedException("Conditional EXIT with PEXIT token");
+ }
+ const IR::Condition cond{flow_test, static_cast<IR::Pred>(pred.index), pred.negated};
+ AnalyzeCondInst(block, function_id, pc, EndClass::Exit, cond);
+ return AnalysisState::Branch;
+ }
+ if (const std::optional<Location> exit_pc{block.stack.Peek(Token::PEXIT)}) {
+ const Stack popped_stack{block.stack.Remove(Token::PEXIT)};
+ block.cond = true;
+ block.branch_true = AddLabel(block, popped_stack, *exit_pc, function_id);
+ block.branch_false = UNREACHABLE_BLOCK_ID;
+ return AnalysisState::Branch;
+ }
+ block.end = pc;
+ block.end_class = EndClass::Exit;
+ return AnalysisState::Branch;
+}
+
+BlockId CFG::AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id) {
+ Function& function{functions[function_id]};
+ if (block.begin == pc) {
+ return block.id;
+ }
+ const auto target{std::ranges::find(function.blocks_data, pc, &Block::begin)};
+ if (target != function.blocks_data.end()) {
+ return target->id;
+ }
+ const BlockId block_id{++function.current_block_id};
+ function.labels.push_back(Label{
+ .address{pc},
+ .block_id{block_id},
+ .stack{std::move(stack)},
+ });
+ return block_id;
+}
+
+std::string CFG::Dot() const {
+ int node_uid{0};
+
+ std::string dot{"digraph shader {\n"};
+ for (const Function& function : functions) {
+ dot += fmt::format("\tsubgraph cluster_{} {{\n", function.entrypoint);
+ dot += fmt::format("\t\tnode [style=filled];\n");
+ for (const u32 block_index : function.blocks) {
+ const Block& block{function.blocks_data[block_index]};
+ const std::string name{Name(block)};
+ const auto add_branch = [&](BlockId branch_id, bool add_label) {
+ const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)};
+ dot += fmt::format("\t\t{}->", name);
+ if (it == function.blocks_data.end()) {
+ dot += fmt::format("\"Unknown label {}\"", branch_id);
+ } else {
+ dot += Name(*it);
+ };
+ if (add_label && block.cond != true && block.cond != false) {
+ dot += fmt::format(" [label=\"{}\"]", block.cond);
+ }
+ dot += '\n';
+ };
+ dot += fmt::format("\t\t{};\n", name);
+ switch (block.end_class) {
+ case EndClass::Branch:
+ if (block.cond != false) {
+ add_branch(block.branch_true, true);
+ }
+ if (block.cond != true) {
+ add_branch(block.branch_false, false);
+ }
+ break;
+ case EndClass::Exit:
+ dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
+ dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n",
+ node_uid);
+ ++node_uid;
+ break;
+ case EndClass::Return:
+ dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
+ dot += fmt::format("\t\tN{} [label=\"Return\"][shape=square][style=stripped];\n",
+ node_uid);
+ ++node_uid;
+ break;
+ case EndClass::Unreachable:
+ dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
+ dot += fmt::format(
+ "\t\tN{} [label=\"Unreachable\"][shape=square][style=stripped];\n", node_uid);
+ ++node_uid;
+ break;
+ }
+ }
+ if (function.entrypoint == 8) {
+ dot += fmt::format("\t\tlabel = \"main\";\n");
+ } else {
+ dot += fmt::format("\t\tlabel = \"Function {}\";\n", function.entrypoint);
+ }
+ dot += "\t}\n";
+ }
+ if (!functions.empty()) {
+ if (functions.front().blocks.empty()) {
+ dot += "Start;\n";
+ } else {
+ dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front()));
+ }
+ dot += fmt::format("\tStart [shape=diamond];\n");
+ }
+ dot += "}\n";
+ return dot;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <compare>
+#include <optional>
+#include <span>
+#include <string>
+#include <vector>
+
+#include <boost/container/small_vector.hpp>
+
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/frontend/ir/condition.h"
+#include "shader_recompiler/frontend/maxwell/instruction.h"
+#include "shader_recompiler/frontend/maxwell/location.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+
+namespace Shader::Maxwell::Flow {
+
+using BlockId = u32;
+using FunctionId = size_t;
+
+constexpr BlockId UNREACHABLE_BLOCK_ID{static_cast<u32>(-1)};
+
+enum class EndClass {
+ Branch,
+ Exit,
+ Return,
+ Unreachable,
+};
+
+enum class Token {
+ SSY,
+ PBK,
+ PEXIT,
+ PRET,
+ PCNT,
+ PLONGJMP,
+};
+
+struct StackEntry {
+ auto operator<=>(const StackEntry&) const noexcept = default;
+
+ Token token;
+ Location target;
+};
+
+class Stack {
+public:
+ void Push(Token token, Location target);
+ [[nodiscard]] std::pair<Location, Stack> Pop(Token token) const;
+ [[nodiscard]] std::optional<Location> Peek(Token token) const;
+ [[nodiscard]] Stack Remove(Token token) const;
+
+private:
+ boost::container::small_vector<StackEntry, 3> entries;
+};
+
+struct Block {
+ [[nodiscard]] bool Contains(Location pc) const noexcept;
+
+ Location begin;
+ Location end;
+ EndClass end_class;
+ BlockId id;
+ Stack stack;
+ IR::Condition cond;
+ BlockId branch_true;
+ BlockId branch_false;
+};
+
+struct Label {
+ Location address;
+ BlockId block_id;
+ Stack stack;
+};
+
+struct Function {
+ Function(Location start_address);
+
+ Location entrypoint;
+ BlockId current_block_id{0};
+ boost::container::small_vector<Label, 16> labels;
+ boost::container::small_vector<u32, 0x130> blocks;
+ boost::container::small_vector<Block, 0x130> blocks_data;
+};
+
+class CFG {
+ enum class AnalysisState {
+ Branch,
+ Continue,
+ };
+
+public:
+ explicit CFG(Environment& env, Location start_address);
+
+ [[nodiscard]] std::string Dot() const;
+
+ [[nodiscard]] std::span<const Function> Functions() const noexcept {
+ return std::span(functions.data(), functions.size());
+ }
+
+private:
+ void AnalyzeLabel(FunctionId function_id, Label& label);
+
+ /// Inspect already visited blocks.
+ /// Return true when the block has already been visited
+ [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
+
+ [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc);
+
+ void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class,
+ IR::Condition cond);
+
+ /// Return true when the branch instruction is confirmed to be a branch
+ [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc,
+ Instruction inst, Opcode opcode);
+
+ void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
+ bool is_absolute);
+ void AnalyzeBRX(Block& block, Location pc, Instruction inst, bool is_absolute);
+ void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute);
+ AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst);
+
+ /// Return the branch target block id
+ [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc,
+ FunctionId function_id);
+
+ Environment& env;
+ boost::container::small_vector<Function, 1> functions;
+ FunctionId current_function_id{0};
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <bit>
+#include <memory>
+#include <string_view>
+
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/decode.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+
+namespace Shader::Maxwell {
+namespace {
+struct MaskValue {
+ u64 mask;
+ u64 value;
+};
+
+constexpr MaskValue MaskValueFromEncoding(const char* encoding) {
+ u64 mask{};
+ u64 value{};
+ u64 bit{u64(1) << 63};
+ while (*encoding) {
+ switch (*encoding) {
+ case '0':
+ mask |= bit;
+ break;
+ case '1':
+ mask |= bit;
+ value |= bit;
+ break;
+ case '-':
+ break;
+ case ' ':
+ break;
+ default:
+ throw LogicError("Invalid encoding character '{}'", *encoding);
+ }
+ ++encoding;
+ if (*encoding != ' ') {
+ bit >>= 1;
+ }
+ }
+ return MaskValue{.mask{mask}, .value{value}};
+}
+
+struct InstEncoding {
+ MaskValue mask_value;
+ Opcode opcode;
+};
+constexpr std::array UNORDERED_ENCODINGS{
+#define INST(name, cute, encode) \
+ InstEncoding{ \
+ .mask_value{MaskValueFromEncoding(encode)}, \
+ .opcode{Opcode::name}, \
+ },
+#include "maxwell.inc"
+#undef INST
+};
+
+constexpr auto SortedEncodings() {
+ std::array encodings{UNORDERED_ENCODINGS};
+ std::ranges::sort(encodings, [](const InstEncoding& lhs, const InstEncoding& rhs) {
+ return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask);
+ });
+ return encodings;
+}
+constexpr auto ENCODINGS{SortedEncodings()};
+
+constexpr int WidestLeftBits() {
+ int bits{64};
+ for (const InstEncoding& encoding : ENCODINGS) {
+ bits = std::min(bits, std::countr_zero(encoding.mask_value.mask));
+ }
+ return 64 - bits;
+}
+constexpr int WIDEST_LEFT_BITS{WidestLeftBits()};
+constexpr int MASK_SHIFT{64 - WIDEST_LEFT_BITS};
+
+constexpr size_t ToFastLookupIndex(u64 value) {
+ return static_cast<size_t>(value >> MASK_SHIFT);
+}
+
+constexpr size_t FastLookupSize() {
+ size_t max_width{};
+ for (const InstEncoding& encoding : ENCODINGS) {
+ max_width = std::max(max_width, ToFastLookupIndex(encoding.mask_value.mask));
+ }
+ return max_width + 1;
+}
+constexpr size_t FAST_LOOKUP_SIZE{FastLookupSize()};
+
+struct InstInfo {
+ [[nodiscard]] u64 Mask() const noexcept {
+ return static_cast<u64>(high_mask) << MASK_SHIFT;
+ }
+
+ [[nodiscard]] u64 Value() const noexcept {
+ return static_cast<u64>(high_value) << MASK_SHIFT;
+ }
+
+ u16 high_mask;
+ u16 high_value;
+ Opcode opcode;
+};
+
+constexpr auto MakeFastLookupTableIndex(size_t index) {
+ std::array<InstInfo, 2> encodings{};
+ size_t element{};
+ for (const auto& encoding : ENCODINGS) {
+ const size_t mask{ToFastLookupIndex(encoding.mask_value.mask)};
+ const size_t value{ToFastLookupIndex(encoding.mask_value.value)};
+ if ((index & mask) == value) {
+ encodings.at(element) = InstInfo{
+ .high_mask{static_cast<u16>(encoding.mask_value.mask >> MASK_SHIFT)},
+ .high_value{static_cast<u16>(encoding.mask_value.value >> MASK_SHIFT)},
+ .opcode{encoding.opcode},
+ };
+ ++element;
+ }
+ }
+ return encodings;
+}
+
+/*constexpr*/ auto MakeFastLookupTable() {
+ auto encodings{std::make_unique<std::array<std::array<InstInfo, 2>, FAST_LOOKUP_SIZE>>()};
+ for (size_t index = 0; index < FAST_LOOKUP_SIZE; ++index) {
+ (*encodings)[index] = MakeFastLookupTableIndex(index);
+ }
+ return encodings;
+}
+const auto FAST_LOOKUP_TABLE{MakeFastLookupTable()};
+} // Anonymous namespace
+
+Opcode Decode(u64 insn) {
+ const auto& table{(*FAST_LOOKUP_TABLE)[ToFastLookupIndex(insn)]};
+ const auto it{std::ranges::find_if(
+ table, [insn](const InstInfo& info) { return (insn & info.Mask()) == info.Value(); })};
+ if (it == table.end()) {
+ throw NotImplementedException("Instruction 0x{:016x} is unknown / unimplemented", insn);
+ }
+ return it->opcode;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+
+namespace Shader::Maxwell {
+
+[[nodiscard]] Opcode Decode(u64 insn);
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "shader_recompiler/frontend/ir/flow_test.h"
+
+namespace Shader::Maxwell {
+
+struct Predicate {
+ Predicate() = default;
+ Predicate(unsigned index_, bool negated_ = false) : index{index_}, negated{negated_} {}
+ Predicate(bool value) : index{7}, negated{!value} {}
+ Predicate(u64 raw) : index{static_cast<unsigned>(raw & 7)}, negated{(raw & 8) != 0} {}
+
+ unsigned index;
+ bool negated;
+};
+
+inline bool operator==(const Predicate& lhs, const Predicate& rhs) noexcept {
+ return lhs.index == rhs.index && lhs.negated == rhs.negated;
+}
+
+inline bool operator!=(const Predicate& lhs, const Predicate& rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+union Instruction {
+ Instruction(u64 raw_) : raw{raw_} {}
+
+ u64 raw;
+
+ union {
+ BitField<5, 1, u64> is_cbuf;
+ BitField<0, 5, IR::FlowTest> flow_test;
+
+ [[nodiscard]] u32 Absolute() const noexcept {
+ return static_cast<u32>(absolute);
+ }
+
+ [[nodiscard]] s32 Offset() const noexcept {
+ return static_cast<s32>(offset);
+ }
+
+ private:
+ BitField<20, 24, s64> offset;
+ BitField<20, 32, u64> absolute;
+ } branch;
+
+ [[nodiscard]] Predicate Pred() const noexcept {
+ return Predicate{pred};
+ }
+
+private:
+ BitField<16, 4, u64> pred;
+};
+static_assert(std::is_trivially_copyable_v<Instruction>);
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <compare>
+#include <iterator>
+
+#include <fmt/format.h>
+
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+
+namespace Shader::Maxwell {
+
+class Location {
+ static constexpr u32 VIRTUAL_OFFSET{std::numeric_limits<u32>::max()};
+
+public:
+ constexpr Location() = default;
+
+ constexpr Location(u32 initial_offset) : offset{initial_offset} {
+ if (initial_offset % 8 != 0) {
+ throw InvalidArgument("initial_offset={} is not a multiple of 8", initial_offset);
+ }
+ Align();
+ }
+
+ [[nodiscard]] constexpr u32 Offset() const noexcept {
+ return offset;
+ }
+
+ [[nodiscard]] constexpr bool IsVirtual() const {
+ return offset == VIRTUAL_OFFSET;
+ }
+
+ constexpr auto operator<=>(const Location&) const noexcept = default;
+
+ constexpr Location operator++() noexcept {
+ const Location copy{*this};
+ Step();
+ return copy;
+ }
+
+ constexpr Location operator++(int) noexcept {
+ Step();
+ return *this;
+ }
+
+ constexpr Location operator--() noexcept {
+ const Location copy{*this};
+ Back();
+ return copy;
+ }
+
+ constexpr Location operator--(int) noexcept {
+ Back();
+ return *this;
+ }
+
+ constexpr Location operator+(int number) const {
+ Location new_pc{*this};
+ while (number > 0) {
+ --number;
+ ++new_pc;
+ }
+ while (number < 0) {
+ ++number;
+ --new_pc;
+ }
+ return new_pc;
+ }
+
+ constexpr Location operator-(int number) const {
+ return operator+(-number);
+ }
+
+private:
+ constexpr void Align() {
+ offset += offset % 32 == 0 ? 8 : 0;
+ }
+
+ constexpr void Step() {
+ offset += 8 + (offset % 32 == 24 ? 8 : 0);
+ }
+
+ constexpr void Back() {
+ offset -= 8 + (offset % 32 == 8 ? 8 : 0);
+ }
+
+ u32 offset{VIRTUAL_OFFSET};
+};
+
+} // namespace Shader::Maxwell
+
+template <>
+struct fmt::formatter<Shader::Maxwell::Location> {
+ constexpr auto parse(format_parse_context& ctx) {
+ return ctx.begin();
+ }
+ template <typename FormatContext>
+ auto format(const Shader::Maxwell::Location& location, FormatContext& ctx) {
+ return fmt::format_to(ctx.out(), "{:04x}", location.Offset());
+ }
+};
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+INST(AL2P, "AL2P", "1110 1111 1010 0---")
+INST(ALD, "ALD", "1110 1111 1101 1---")
+INST(AST, "AST", "1110 1111 1111 0---")
+INST(ATOM_cas, "ATOM (cas)", "1110 1110 1111 ----")
+INST(ATOM, "ATOM", "1110 1101 ---- ----")
+INST(ATOMS_cas, "ATOMS (cas)", "1110 1110 ---- ----")
+INST(ATOMS, "ATOMS", "1110 1100 ---- ----")
+INST(B2R, "B2R", "1111 0000 1011 1---")
+INST(BAR, "BAR", "1111 0000 1010 1---")
+INST(BFE_reg, "BFE (reg)", "0101 1100 0000 0---")
+INST(BFE_cbuf, "BFE (cbuf)", "0100 1100 0000 0---")
+INST(BFE_imm, "BFE (imm)", "0011 100- 0000 0---")
+INST(BFI_reg, "BFI (reg)", "0101 1011 1111 0---")
+INST(BFI_rc, "BFI (rc)", "0101 0011 1111 0---")
+INST(BFI_cr, "BFI (cr)", "0100 1011 1111 0---")
+INST(BFI_imm, "BFI (imm)", "0011 011- 1111 0---")
+INST(BPT, "BPT", "1110 0011 1010 ----")
+INST(BRA, "BRA", "1110 0010 0100 ----")
+INST(BRK, "BRK", "1110 0011 0100 ----")
+INST(BRX, "BRX", "1110 0010 0101 ----")
+INST(CAL, "CAL", "1110 0010 0110 ----")
+INST(CCTL, "CCTL", "1110 1111 011- ----")
+INST(CCTLL, "CCTLL", "1110 1111 100- ----")
+INST(CONT, "CONT", "1110 0011 0101 ----")
+INST(CS2R, "CS2R", "0101 0000 1100 1---")
+INST(CSET, "CSET", "0101 0000 1001 1---")
+INST(CSETP, "CSETP", "0101 0000 1010 0---")
+INST(DADD_reg, "DADD (reg)", "0101 1100 0111 0---")
+INST(DADD_cbuf, "DADD (cbuf)", "0100 1100 0111 0---")
+INST(DADD_imm, "DADD (imm)", "0011 100- 0111 0---")
+INST(DEPBAR, "DEPBAR", "1111 0000 1111 0---")
+INST(DFMA_reg, "DFMA (reg)", "0101 1011 0111 ----")
+INST(DFMA_rc, "DFMA (rc)", "0101 0011 0111 ----")
+INST(DFMA_cr, "DFMA (cr)", "0010 1011 0111 ----")
+INST(DFMA_imm, "DFMA (imm)", "0011 011- 0111 ----")
+INST(DMNMX_reg, "DMNMX (reg)", "0100 1100 0101 0---")
+INST(DMNMX_cbuf, "DMNMX (cbuf)", "0101 1100 0101 0---")
+INST(DMNMX_imm, "DMNMX (imm)", "0011 100- 0101 0---")
+INST(DMUL_reg, "DMUL (reg)", "0101 1100 1000 0---")
+INST(DMUL_cbuf, "DMUL (cbuf)", "0100 1100 1000 0---")
+INST(DMUL_imm, "DMUL (imm)", "0011 100- 1000 0---")
+INST(DSET_reg, "DSET (reg)", "0101 1001 0--- ----")
+INST(DSET_cbuf, "DSET (cbuf)", "0100 1001 0--- ----")
+INST(DSET_imm, "DSET (imm)", "0011 001- 0--- ----")
+INST(DSETP_reg, "DSETP (reg)", "0101 1011 1000 ----")
+INST(DSETP_cbuf, "DSETP (cbuf)", "0100 1011 1000 ----")
+INST(DSETP_imm, "DSETP (imm)", "0011 011- 1000 ----")
+INST(EXIT, "EXIT", "1110 0011 0000 ----")
+INST(F2F_reg, "F2F (reg)", "0101 1100 1010 1---")
+INST(F2F_cbuf, "F2F (cbuf)", "0100 1100 1010 1---")
+INST(F2F_imm, "F2F (imm)", "0011 100- 1010 1---")
+INST(F2I_reg, "F2I (reg)", "0101 1100 1011 0---")
+INST(F2I_cbuf, "F2I (cbuf)", "0100 1100 1011 0---")
+INST(F2I_imm, "F2I (imm)", "0011 100- 1011 0---")
+INST(FADD_reg, "FADD (reg)", "0101 1100 0101 1---")
+INST(FADD_cbuf, "FADD (cbuf)", "0100 1100 0101 1---")
+INST(FADD_imm, "FADD (imm)", "0011 100- 0101 1---")
+INST(FADD32I, "FADD32I", "0000 10-- ---- ----")
+INST(FCHK_reg, "FCHK (reg)", "0101 1100 1000 1---")
+INST(FCHK_cbuf, "FCHK (cbuf)", "0100 1100 1000 1---")
+INST(FCHK_imm, "FCHK (imm)", "0011 100- 1000 1---")
+INST(FCMP_reg, "FCMP (reg)", "0101 1011 1010 ----")
+INST(FCMP_rc, "FCMP (rc)", "0101 0011 1010 ----")
+INST(FCMP_cr, "FCMP (cr)", "0100 1011 1010 ----")
+INST(FCMP_imm, "FCMP (imm)", "0011 011- 1010 ----")
+INST(FFMA_reg, "FFMA (reg)", "0101 1001 1--- ----")
+INST(FFMA_rc, "FFMA (rc)", "0101 0001 1--- ----")
+INST(FFMA_cr, "FFMA (cr)", "0100 1001 1--- ----")
+INST(FFMA_imm, "FFMA (imm)", "0011 001- 1--- ----")
+INST(FFMA32I, "FFMA32I", "0000 11-- ---- ----")
+INST(FLO_reg, "FLO (reg)", "0101 1100 0011 0---")
+INST(FLO_cbuf, "FLO (cbuf)", "0100 1100 0011 0---")
+INST(FLO_imm, "FLO (imm)", "0011 100- 0011 0---")
+INST(FMNMX_reg, "FMNMX (reg)", "0101 1100 0110 0---")
+INST(FMNMX_cbuf, "FMNMX (cbuf)", "0100 1100 0110 0---")
+INST(FMNMX_imm, "FMNMX (imm)", "0011 100- 0110 0---")
+INST(FMUL_reg, "FMUL (reg)", "0101 1100 0110 1---")
+INST(FMUL_cbuf, "FMUL (cbuf)", "0100 1100 0110 1---")
+INST(FMUL_imm, "FMUL (imm)", "0011 100- 0110 1---")
+INST(FMUL32I, "FMUL32I", "0001 1110 ---- ----")
+INST(FSET_reg, "FSET (reg)", "0101 1000 ---- ----")
+INST(FSET_cbuf, "FSET (cbuf)", "0100 1000 ---- ----")
+INST(FSET_imm, "FSET (imm)", "0011 000- ---- ----")
+INST(FSETP_reg, "FSETP (reg)", "0101 1011 1011 ----")
+INST(FSETP_cbuf, "FSETP (cbuf)", "0100 1011 1011 ----")
+INST(FSETP_imm, "FSETP (imm)", "0011 011- 1011 ----")
+INST(FSWZADD, "FSWZADD", "0101 0000 1111 1---")
+INST(GETCRSPTR, "GETCRSPTR", "1110 0010 1100 ----")
+INST(GETLMEMBASE, "GETLMEMBASE", "1110 0010 1101 ----")
+INST(HADD2_reg, "HADD2 (reg)", "0101 1101 0001 0---")
+INST(HADD2_cbuf, "HADD2 (cbuf)", "0111 101- 1--- ----")
+INST(HADD2_imm, "HADD2 (imm)", "0111 101- 0--- ----")
+INST(HADD2_32I, "HADD2_32I", "0010 110- ---- ----")
+INST(HFMA2_reg, "HFMA2 (reg)", "0101 1101 0000 0---")
+INST(HFMA2_rc, "HFMA2 (rc)", "0110 0--- 1--- ----")
+INST(HFMA2_cr, "HFMA2 (cr)", "0111 0--- 1--- ----")
+INST(HFMA2_imm, "HFMA2 (imm)", "0111 0--- 0--- ----")
+INST(HFMA2_32I, "HFMA2_32I", "0010 100- ---- ----")
+INST(HMUL2_reg, "HMUL2 (reg)", "0101 1101 0000 1---")
+INST(HMUL2_cbuf, "HMUL2 (cbuf)", "0111 100- 1--- ----")
+INST(HMUL2_imm, "HMUL2 (imm)", "0111 100- 0--- ----")
+INST(HMUL2_32I, "HMUL2_32I", "0010 101- ---- ----")
+INST(HSET2_reg, "HSET2 (reg)", "0101 1101 0001 1---")
+INST(HSET2_cbuf, "HSET2 (cbuf)", "0111 1100 1--- ----")
+INST(HSET2_imm, "HSET2 (imm)", "0111 1100 0--- ----")
+INST(HSETP2_reg, "HSETP2 (reg)", "0101 1101 0010 0---")
+INST(HSETP2_cbuf, "HSETP2 (cbuf)", "0111 111- 1--- ----")
+INST(HSETP2_imm, "HSETP2 (imm)", "0111 111- 0--- ----")
+INST(I2F_reg, "I2F (reg)", "0101 1100 1011 1---")
+INST(I2F_cbuf, "I2F (cbuf)", "0100 1100 1011 1---")
+INST(I2F_imm, "I2F (imm)", "0011 100- 1011 1---")
+INST(I2I_reg, "I2I (reg)", "0101 1100 1110 0---")
+INST(I2I_cbuf, "I2I (cbuf)", "0100 1100 1110 0---")
+INST(I2I_imm, "I2I (imm)", "0011 100- 1110 0---")
+INST(IADD_reg, "IADD (reg)", "0101 1100 0001 0---")
+INST(IADD_cbuf, "IADD (cbuf)", "0100 1100 0001 0---")
+INST(IADD_imm, "IADD (imm)", "0011 100- 0001 0---")
+INST(IADD3_reg, "IADD3 (reg)", "0101 1100 1100 ----")
+INST(IADD3_cbuf, "IADD3 (cbuf)", "0100 1100 1100 ----")
+INST(IADD3_imm, "IADD3 (imm)", "0011 100- 1100 ----")
+INST(IADD32I, "IADD32I", "0001 110- ---- ----")
+INST(ICMP_reg, "ICMP (reg)", "0101 1011 0100 ----")
+INST(ICMP_rc, "ICMP (rc)", "0101 0011 0100 ----")
+INST(ICMP_cr, "ICMP (cr)", "0100 1011 0100 ----")
+INST(ICMP_imm, "ICMP (imm)", "0011 011- 0100 ----")
+INST(IDE, "IDE", "1110 0011 1001 ----")
+INST(IDP_reg, "IDP (reg)", "0101 0011 1111 1---")
+INST(IDP_imm, "IDP (imm)", "0101 0011 1101 1---")
+INST(IMAD_reg, "IMAD (reg)", "0101 1010 0--- ----")
+INST(IMAD_rc, "IMAD (rc)", "0101 0010 0--- ----")
+INST(IMAD_cr, "IMAD (cr)", "0100 1010 0--- ----")
+INST(IMAD_imm, "IMAD (imm)", "0011 010- 0--- ----")
+INST(IMAD32I, "IMAD32I", "1000 00-- ---- ----")
+INST(IMADSP_reg, "IMADSP (reg)", "0101 1010 1--- ----")
+INST(IMADSP_rc, "IMADSP (rc)", "0101 0010 1--- ----")
+INST(IMADSP_cr, "IMADSP (cr)", "0100 1010 1--- ----")
+INST(IMADSP_imm, "IMADSP (imm)", "0011 010- 1--- ----")
+INST(IMNMX_reg, "IMNMX (reg)", "0101 1100 0010 0---")
+INST(IMNMX_cbuf, "IMNMX (cbuf)", "0100 1100 0010 0---")
+INST(IMNMX_imm, "IMNMX (imm)", "0011 100- 0010 0---")
+INST(IMUL_reg, "IMUL (reg)", "0101 1100 0011 1---")
+INST(IMUL_cbuf, "IMUL (cbuf)", "0100 1100 0011 1---")
+INST(IMUL_imm, "IMUL (imm)", "0011 100- 0011 1---")
+INST(IMUL32I, "IMUL32I", "0001 1111 ---- ----")
+INST(IPA, "IPA", "1110 0000 ---- ----")
+INST(ISBERD, "ISBERD", "1110 1111 1101 0---")
+INST(ISCADD_reg, "ISCADD (reg)", "0101 1100 0001 1---")
+INST(ISCADD_cbuf, "ISCADD (cbuf)", "0100 1100 0001 1---")
+INST(ISCADD_imm, "ISCADD (imm)", "0011 100- 0001 1---")
+INST(ISCADD32I, "ISCADD32I", "0001 01-- ---- ----")
+INST(ISET_reg, "ISET (reg)", "0101 1011 0101 ----")
+INST(ISET_cbuf, "ISET (cbuf)", "0100 1011 0101 ----")
+INST(ISET_imm, "ISET (imm)", "0011 011- 0101 ----")
+INST(ISETP_reg, "ISETP (reg)", "0101 1011 0110 ----")
+INST(ISETP_cbuf, "ISETP (cbuf)", "0100 1011 0110 ----")
+INST(ISETP_imm, "ISETP (imm)", "0011 011- 0110 ----")
+INST(JCAL, "JCAL", "1110 0010 0010 ----")
+INST(JMP, "JMP", "1110 0010 0001 ----")
+INST(JMX, "JMX", "1110 0010 0000 ----")
+INST(KIL, "KIL", "1110 0011 0011 ----")
+INST(LD, "LD", "100- ---- ---- ----")
+INST(LDC, "LDC", "1110 1111 1001 0---")
+INST(LDG, "LDG", "1110 1110 1101 0---")
+INST(LDL, "LDL", "1110 1111 0100 0---")
+INST(LDS, "LDS", "1110 1111 0100 1---")
+INST(LEA_hi_reg, "LEA (hi reg)", "0101 1011 1101 1---")
+INST(LEA_hi_cbuf, "LEA (hi cbuf)", "0001 10-- ---- ----")
+INST(LEA_lo_reg, "LEA (lo reg)", "0101 1011 1101 0---")
+INST(LEA_lo_cbuf, "LEA (lo cbuf)", "0100 1011 1101 ----")
+INST(LEA_lo_imm, "LEA (lo imm)", "0011 011- 1101 0---")
+INST(LEPC, "LEPC", "0101 0000 1101 0---")
+INST(LONGJMP, "LONGJMP", "1110 0011 0001 ----")
+INST(LOP_reg, "LOP (reg)", "0101 1100 0100 0---")
+INST(LOP_cbuf, "LOP (cbuf)", "0100 1100 0100 0---")
+INST(LOP_imm, "LOP (imm)", "0011 100- 0100 0---")
+INST(LOP3_reg, "LOP3 (reg)", "0101 1011 1110 0---")
+INST(LOP3_cbuf, "LOP3 (cbuf)", "0011 11-- ---- ----")
+INST(LOP3_imm, "LOP3 (imm)", "0000 001- ---- ----")
+INST(LOP32I, "LOP32I", "0000 01-- ---- ----")
+INST(MEMBAR, "MEMBAR", "1110 1111 1001 1---")
+INST(MOV_reg, "MOV (reg)", "0101 1100 1001 1---")
+INST(MOV_cbuf, "MOV (cbuf)", "0100 1100 1001 1---")
+INST(MOV_imm, "MOV (imm)", "0011 100- 1001 1---")
+INST(MOV32I, "MOV32I", "0000 0001 0000 ----")
+INST(MUFU, "MUFU", "0101 0000 1000 0---")
+INST(NOP, "NOP", "0101 0000 1011 0---")
+INST(OUT_reg, "OUT (reg)", "1111 1011 1110 0---")
+INST(OUT_cbuf, "OUT (cbuf)", "1110 1011 1110 0---")
+INST(OUT_imm, "OUT (imm)", "1111 011- 1110 0---")
+INST(P2R_reg, "P2R (reg)", "0101 1100 1110 1---")
+INST(P2R_cbuf, "P2R (cbuf)", "0100 1100 1110 1---")
+INST(P2R_imm, "P2R (imm)", "0011 1000 1110 1---")
+INST(PBK, "PBK", "1110 0010 1010 ----")
+INST(PCNT, "PCNT", "1110 0010 1011 ----")
+INST(PEXIT, "PEXIT", "1110 0010 0011 ----")
+INST(PIXLD, "PIXLD", "1110 1111 1110 1---")
+INST(PLONGJMP, "PLONGJMP", "1110 0010 1000 ----")
+INST(POPC_reg, "POPC (reg)", "0101 1100 0000 1---")
+INST(POPC_cbuf, "POPC (cbuf)", "0100 1100 0000 1---")
+INST(POPC_imm, "POPC (imm)", "0011 100- 0000 1---")
+INST(PRET, "PRET", "1110 0010 0111 ----")
+INST(PRMT_reg, "PRMT (reg)", "0101 1011 1100 ----")
+INST(PRMT_rc, "PRMT (rc)", "0101 0011 1100 ----")
+INST(PRMT_cr, "PRMT (cr)", "0100 1011 1100 ----")
+INST(PRMT_imm, "PRMT (imm)", "0011 011- 1100 ----")
+INST(PSET, "PSET", "0101 0000 1000 1---")
+INST(PSETP, "PSETP", "0101 0000 1001 0---")
+INST(R2B, "R2B", "1111 0000 1100 0---")
+INST(R2P_reg, "R2P (reg)", "0101 1100 1111 0---")
+INST(R2P_cbuf, "R2P (cbuf)", "0100 1100 1111 0---")
+INST(R2P_imm, "R2P (imm)", "0011 100- 1111 0---")
+INST(RAM, "RAM", "1110 0011 1000 ----")
+INST(RED, "RED", "1110 1011 1111 1---")
+INST(RET, "RET", "1110 0011 0010 ----")
+INST(RRO_reg, "RRO (reg)", "0101 1100 1001 0---")
+INST(RRO_cbuf, "RRO (cbuf)", "0100 1100 1001 0---")
+INST(RRO_imm, "RRO (imm)", "0011 100- 1001 0---")
+INST(RTT, "RTT", "1110 0011 0110 ----")
+INST(S2R, "S2R", "1111 0000 1100 1---")
+INST(SAM, "SAM", "1110 0011 0111 ----")
+INST(SEL_reg, "SEL (reg)", "0101 1100 1010 0---")
+INST(SEL_cbuf, "SEL (cbuf)", "0100 1100 1010 0---")
+INST(SEL_imm, "SEL (imm)", "0011 100- 1010 0---")
+INST(SETCRSPTR, "SETCRSPTR", "1110 0010 1110 ----")
+INST(SETLMEMBASE, "SETLMEMBASE", "1110 0010 1111 ----")
+INST(SHF_l_reg, "SHF (l reg)", "0101 1011 1111 1---")
+INST(SHF_l_imm, "SHF (l imm)", "0011 011- 1111 1---")
+INST(SHF_r_reg, "SHF (r reg)", "0101 1100 1111 1---")
+INST(SHF_r_imm, "SHF (r imm)", "0011 100- 1111 1---")
+INST(SHFL, "SHFL", "1110 1111 0001 0---")
+INST(SHL_reg, "SHL (reg)", "0101 1100 0100 1---")
+INST(SHL_cbuf, "SHL (cbuf)", "0100 1100 0100 1---")
+INST(SHL_imm, "SHL (imm)", "0011 100- 0100 1---")
+INST(SHR_reg, "SHR (reg)", "0101 1100 0010 1---")
+INST(SHR_cbuf, "SHR (cbuf)", "0100 1100 0010 1---")
+INST(SHR_imm, "SHR (imm)", "0011 100- 0010 1---")
+INST(SSY, "SSY", "1110 0010 1001 ----")
+INST(ST, "ST", "101- ---- ---- ----")
+INST(STG, "STG", "1110 1110 1101 1---")
+INST(STL, "STL", "1110 1111 0101 0---")
+INST(STP, "STP", "1110 1110 1010 0---")
+INST(STS, "STS", "1110 1111 0101 1---")
+INST(SUATOM_cas, "SUATOM", "1110 1010 ---- ----")
+INST(SULD, "SULD", "1110 1011 000- ----")
+INST(SURED, "SURED", "1110 1011 010- ----")
+INST(SUST, "SUST", "1110 1011 001- ----")
+INST(SYNC, "SYNC", "1111 0000 1111 1---")
+INST(TEX, "TEX", "1100 00-- --11 1---")
+INST(TEX_b, "TEX (b)", "1101 1110 1011 1---")
+INST(TEXS, "TEXS", "1101 -00- ---- ----")
+INST(TLD, "TLD", "1101 1100 --11 1---")
+INST(TLD_b, "TLD (b)", "1101 1101 --11 1---")
+INST(TLD4, "TLD4", "1100 10-- --11 1---")
+INST(TLD4_b, "TLD4 (b)", "1101 1110 1111 1---")
+INST(TLD4S, "TLD4S", "1101 1111 -0-- ----")
+INST(TLDS, "TLDS", "1101 -01- ---- ----")
+INST(TMML, "TMML", "1101 1111 0101 1---")
+INST(TMML_b, "TMML (b)", "1101 1111 0110 0---")
+INST(TXA, "TXA", "1101 1111 0100 0---")
+INST(TXD, "TXD", "1101 1110 0011 10--")
+INST(TXD_b, "TXD (b)", "1101 1110 0111 10--")
+INST(TXQ, "TXQ", "1101 1111 0100 1---")
+INST(TXQ_b, "TXQ (b)", "1101 1111 0101 0---")
+INST(VABSDIFF, "VABSDIFF", "0101 0100 ---- ----")
+INST(VABSDIFF4, "VABSDIFF4", "0101 0000 0--- ----")
+INST(VADD, "VADD", "0010 00-- ---- ----")
+INST(VMAD, "VMAD", "0101 1111 ---- ----")
+INST(VMNMX, "VMNMX", "0011 101- ---- ----")
+INST(VOTE, "VOTE", "0101 0000 1101 1---")
+INST(VOTE_vtg, "VOTE (vtg)", "0101 0000 1110 0---")
+INST(VSET, "VSET", "0100 000- ---- ----")
+INST(VSETP, "VSETP", "0101 0000 1111 0---")
+INST(VSHL, "VSHL", "0101 0111 ---- ----")
+INST(VSHR, "VSHR", "0101 0110 ---- ----")
+INST(XMAD_reg, "XMAD (reg)", "0101 1011 00-- ----")
+INST(XMAD_rc, "XMAD (rc)", "0101 0001 0--- ----")
+INST(XMAD_cr, "XMAD (cr)", "0100 111- ---- ----")
+INST(XMAD_imm, "XMAD (imm)", "0011 011- 00-- ----")
+
+// Removed due to its weird formatting making fast tables larger
+// 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+
+namespace Shader::Maxwell {
+namespace {
+constexpr std::array NAME_TABLE{
+#define INST(name, cute, encode) #cute,
+#include "maxwell.inc"
+#undef INST
+};
+} // Anonymous namespace
+
+const char* NameOf(Opcode opcode) {
+ if (static_cast<size_t>(opcode) >= NAME_TABLE.size()) {
+ throw InvalidArgument("Invalid opcode with raw value {}", static_cast<int>(opcode));
+ }
+ return NAME_TABLE[static_cast<size_t>(opcode)];
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <fmt/format.h>
+
+namespace Shader::Maxwell {
+
+enum class Opcode {
+#define INST(name, cute, encode) name,
+#include "maxwell.inc"
+#undef INST
+};
+
+const char* NameOf(Opcode opcode);
+
+} // namespace Shader::Maxwell
+
+template <>
+struct fmt::formatter<Shader::Maxwell::Opcode> {
+ constexpr auto parse(format_parse_context& ctx) {
+ return ctx.begin();
+ }
+ template <typename FormatContext>
+ auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) {
+ return format_to(ctx.out(), "{}", NameOf(opcode));
+ }
+};
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <memory>
+
+#include "shader_recompiler/frontend/maxwell/program.h"
+#include "shader_recompiler/frontend/maxwell/termination_code.h"
+#include "shader_recompiler/frontend/maxwell/translate/translate.h"
+
+namespace Shader::Maxwell {
+
+Program::Function::~Function() {
+ std::ranges::for_each(blocks, &std::destroy_at<IR::Block>);
+}
+
+Program::Program(Environment& env, const Flow::CFG& cfg) {
+ std::vector<IR::Block*> block_map;
+ functions.reserve(cfg.Functions().size());
+
+ for (const Flow::Function& cfg_function : cfg.Functions()) {
+ Function& function{functions.emplace_back()};
+
+ const size_t num_blocks{cfg_function.blocks.size()};
+ IR::Block* block_memory{block_alloc_pool.allocate(num_blocks)};
+ function.blocks.reserve(num_blocks);
+
+ block_map.resize(cfg_function.blocks_data.size());
+
+ // Visit the instructions of all blocks
+ for (const Flow::BlockId block_id : cfg_function.blocks) {
+ const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
+
+ IR::Block* const block{std::construct_at(block_memory, Translate(env, flow_block))};
+ ++block_memory;
+ function.blocks.push_back(block);
+ block_map[flow_block.id] = block;
+ }
+ // Now that all blocks are defined, emit the termination instructions
+ for (const Flow::BlockId block_id : cfg_function.blocks) {
+ const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
+ EmitTerminationCode(flow_block, block_map);
+ }
+ }
+}
+
+std::string DumpProgram(const Program& program) {
+ size_t index{0};
+ std::map<const IR::Inst*, size_t> inst_to_index;
+ std::map<const IR::Block*, size_t> block_to_index;
+
+ for (const Program::Function& function : program.functions) {
+ for (const IR::Block* const block : function.blocks) {
+ block_to_index.emplace(block, index);
+ ++index;
+ }
+ }
+ std::string ret;
+ for (const Program::Function& function : program.functions) {
+ ret += fmt::format("Function\n");
+ for (const IR::Block* const block : function.blocks) {
+ ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
+ }
+ }
+ return ret;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include <boost/pool/pool_alloc.hpp>
+
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+
+namespace Shader::Maxwell {
+
+class Program {
+ friend std::string DumpProgram(const Program& program);
+
+public:
+ explicit Program(Environment& env, const Flow::CFG& cfg);
+
+private:
+ struct Function {
+ ~Function();
+
+ std::vector<IR::Block*> blocks;
+ };
+
+ boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete,
+ boost::details::pool::null_mutex>
+ block_alloc_pool;
+ std::vector<Function> functions;
+};
+
+[[nodiscard]] std::string DumpProgram(const Program& program);
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <span>
+
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/ir_emitter.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+#include "shader_recompiler/frontend/maxwell/termination_code.h"
+
+namespace Shader::Maxwell {
+
+static void EmitExit(IR::IREmitter& ir) {
+ ir.Exit();
+}
+
+static IR::U1 GetFlowTest(IR::FlowTest flow_test, IR::IREmitter& ir) {
+ switch (flow_test) {
+ case IR::FlowTest::T:
+ return ir.Imm1(true);
+ case IR::FlowTest::F:
+ return ir.Imm1(false);
+ case IR::FlowTest::NE:
+ // FIXME: Verify this
+ return ir.LogicalNot(ir.GetZFlag());
+ case IR::FlowTest::NaN:
+ // FIXME: Verify this
+ return ir.LogicalAnd(ir.GetSFlag(), ir.GetZFlag());
+ default:
+ throw NotImplementedException("Flow test {}", flow_test);
+ }
+}
+
+static IR::U1 GetCond(IR::Condition cond, IR::IREmitter& ir) {
+ const IR::FlowTest flow_test{cond.FlowTest()};
+ const auto [pred, pred_negated]{cond.Pred()};
+ if (pred == IR::Pred::PT && !pred_negated) {
+ return GetFlowTest(flow_test, ir);
+ }
+ if (flow_test == IR::FlowTest::T) {
+ return ir.GetPred(pred, pred_negated);
+ }
+ return ir.LogicalAnd(ir.GetPred(pred, pred_negated), GetFlowTest(flow_test, ir));
+}
+
+static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map,
+ IR::IREmitter& ir) {
+ if (flow_block.cond == true) {
+ return ir.Branch(block_map[flow_block.branch_true]);
+ }
+ if (flow_block.cond == false) {
+ return ir.Branch(block_map[flow_block.branch_false]);
+ }
+ return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true],
+ block_map[flow_block.branch_false]);
+}
+
+void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map) {
+ IR::Block* const block{block_map[flow_block.id]};
+ IR::IREmitter ir(*block);
+ switch (flow_block.end_class) {
+ case Flow::EndClass::Branch:
+ EmitBranch(flow_block, block_map, ir);
+ break;
+ case Flow::EndClass::Exit:
+ EmitExit(ir);
+ break;
+ case Flow::EndClass::Return:
+ ir.Return();
+ break;
+ case Flow::EndClass::Unreachable:
+ ir.Unreachable();
+ break;
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <span>
+
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+
+namespace Shader::Maxwell {
+
+void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map);
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+
+void TranslatorVisitor::EXIT(u64) {
+ ir.Exit();
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+namespace {
+enum class DestFormat : u64 {
+ Invalid,
+ I16,
+ I32,
+ I64,
+};
+enum class SrcFormat : u64 {
+ Invalid,
+ F16,
+ F32,
+ F64,
+};
+enum class Rounding : u64 {
+ Round,
+ Floor,
+ Ceil,
+ Trunc,
+};
+
+union F2I {
+ u64 raw;
+ BitField<0, 8, IR::Reg> dest_reg;
+ BitField<8, 2, DestFormat> dest_format;
+ BitField<10, 2, SrcFormat> src_format;
+ BitField<12, 1, u64> is_signed;
+ BitField<39, 1, Rounding> rounding;
+ BitField<49, 1, u64> half;
+ BitField<44, 1, u64> ftz;
+ BitField<45, 1, u64> abs;
+ BitField<47, 1, u64> cc;
+ BitField<49, 1, u64> neg;
+};
+
+size_t BitSize(DestFormat dest_format) {
+ switch (dest_format) {
+ case DestFormat::I16:
+ return 16;
+ case DestFormat::I32:
+ return 32;
+ case DestFormat::I64:
+ return 64;
+ default:
+ throw NotImplementedException("Invalid destination format {}", dest_format);
+ }
+}
+
+void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::U16U32U64& op_a) {
+ // F2I is used to convert from a floating point value to an integer
+ const F2I f2i{insn};
+
+ const IR::U16U32U64 float_value{v.ir.FPAbsNeg(op_a, f2i.abs != 0, f2i.neg != 0)};
+ const IR::U16U32U64 rounded_value{[&] {
+ switch (f2i.rounding) {
+ case Rounding::Round:
+ return v.ir.FPRoundEven(float_value);
+ case Rounding::Floor:
+ return v.ir.FPFloor(float_value);
+ case Rounding::Ceil:
+ return v.ir.FPCeil(float_value);
+ case Rounding::Trunc:
+ return v.ir.FPTrunc(float_value);
+ default:
+ throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value());
+ }
+ }()};
+
+ // TODO: Handle out of bounds conversions.
+ // For example converting F32 65537.0 to U16, the expected value is 0xffff,
+
+ const bool is_signed{f2i.is_signed != 0};
+ const size_t bitsize{BitSize(f2i.dest_format)};
+ const IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, rounded_value)};
+
+ v.X(f2i.dest_reg, result);
+
+ if (f2i.cc != 0) {
+ v.SetZFlag(v.ir.GetZeroFromOp(result));
+ if (is_signed) {
+ v.SetSFlag(v.ir.GetSignFromOp(result));
+ } else {
+ v.ResetSFlag();
+ }
+ v.ResetCFlag();
+
+ // TODO: Investigate if out of bound conversions sets the overflow flag
+ v.ResetOFlag();
+ }
+}
+} // Anonymous namespace
+
+void TranslatorVisitor::F2I_reg(u64 insn) {
+ union {
+ F2I base;
+ BitField<20, 8, IR::Reg> src_reg;
+ } const f2i{insn};
+
+ const IR::U16U32U64 op_a{[&]() -> IR::U16U32U64 {
+ switch (f2i.base.src_format) {
+ case SrcFormat::F16:
+ return ir.CompositeExtract(ir.UnpackFloat2x16(X(f2i.src_reg)), f2i.base.half);
+ case SrcFormat::F32:
+ return X(f2i.src_reg);
+ case SrcFormat::F64:
+ return ir.PackDouble2x32(ir.CompositeConstruct(X(f2i.src_reg), X(f2i.src_reg + 1)));
+ default:
+ throw NotImplementedException("Invalid F2I source format {}",
+ f2i.base.src_format.Value());
+ }
+ }()};
+
+ TranslateF2I(*this, insn, op_a);
+}
+
+void TranslatorVisitor::F2I_cbuf(u64) {
+ throw NotImplementedException("{}", Opcode::F2I_cbuf);
+}
+
+void TranslatorVisitor::F2I_imm(u64) {
+ throw NotImplementedException("{}", Opcode::F2I_imm);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+namespace {
+enum class Operation {
+ Cos = 0,
+ Sin = 1,
+ Ex2 = 2, // Base 2 exponent
+ Lg2 = 3, // Base 2 logarithm
+ Rcp = 4, // Reciprocal
+ Rsq = 5, // Reciprocal square root
+ Rcp64H = 6, // 64-bit reciprocal
+ Rsq64H = 7, // 64-bit reciprocal square root
+ Sqrt = 8,
+};
+} // Anonymous namespace
+
+void TranslatorVisitor::MUFU(u64 insn) {
+ // MUFU is used to implement a bunch of special functions. See Operation.
+ union {
+ u64 raw;
+ BitField<0, 8, IR::Reg> dest_reg;
+ BitField<8, 8, IR::Reg> src_reg;
+ BitField<20, 4, Operation> operation;
+ BitField<46, 1, u64> abs;
+ BitField<48, 1, u64> neg;
+ BitField<50, 1, u64> sat;
+ } const mufu{insn};
+
+ const IR::U32 op_a{ir.FPAbsNeg(X(mufu.src_reg), mufu.abs != 0, mufu.neg != 0)};
+ IR::U32 value{[&]() -> IR::U32 {
+ switch (mufu.operation) {
+ case Operation::Cos:
+ return ir.FPCosNotReduced(op_a);
+ case Operation::Sin:
+ return ir.FPSinNotReduced(op_a);
+ case Operation::Ex2:
+ return ir.FPExp2NotReduced(op_a);
+ case Operation::Lg2:
+ return ir.FPLog2(op_a);
+ case Operation::Rcp:
+ return ir.FPRecip(op_a);
+ case Operation::Rsq:
+ return ir.FPRecipSqrt(op_a);
+ case Operation::Rcp64H:
+ throw NotImplementedException("MUFU.RCP64H");
+ case Operation::Rsq64H:
+ throw NotImplementedException("MUFU.RSQ64H");
+ case Operation::Sqrt:
+ return ir.FPSqrt(op_a);
+ default:
+ throw NotImplementedException("Invalid MUFU operation {}", mufu.operation.Value());
+ }
+ }()};
+
+ if (mufu.sat) {
+ value = ir.FPSaturate(value);
+ }
+
+ X(mufu.dest_reg, value);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "shader_recompiler/frontend/ir/ir_emitter.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+
+IR::U32 TranslatorVisitor::X(IR::Reg reg) {
+ return ir.GetReg(reg);
+}
+
+void TranslatorVisitor::X(IR::Reg dest_reg, const IR::U32& value) {
+ ir.SetReg(dest_reg, value);
+}
+
+IR::U32 TranslatorVisitor::GetCbuf(u64 insn) {
+ union {
+ u64 raw;
+ BitField<20, 14, s64> offset;
+ BitField<34, 5, u64> binding;
+ } const cbuf{insn};
+ if (cbuf.binding >= 18) {
+ throw NotImplementedException("Out of bounds constant buffer binding {}", cbuf.binding);
+ }
+ if (cbuf.offset >= 0x10'000 || cbuf.offset < 0) {
+ throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset);
+ }
+ const IR::U32 binding{ir.Imm32(static_cast<u32>(cbuf.binding))};
+ const IR::U32 byte_offset{ir.Imm32(static_cast<u32>(cbuf.offset) * 4)};
+ return ir.GetCbuf(binding, byte_offset);
+}
+
+IR::U32 TranslatorVisitor::GetImm(u64 insn) {
+ union {
+ u64 raw;
+ BitField<20, 19, u64> value;
+ BitField<56, 1, u64> is_negative;
+ } const imm{insn};
+ const s32 positive_value{static_cast<s32>(imm.value)};
+ const s32 value{imm.is_negative != 0 ? -positive_value : positive_value};
+ return ir.Imm32(value);
+}
+
+void TranslatorVisitor::SetZFlag(const IR::U1& value) {
+ ir.SetZFlag(value);
+}
+
+void TranslatorVisitor::SetSFlag(const IR::U1& value) {
+ ir.SetSFlag(value);
+}
+
+void TranslatorVisitor::SetCFlag(const IR::U1& value) {
+ ir.SetCFlag(value);
+}
+
+void TranslatorVisitor::SetOFlag(const IR::U1& value) {
+ ir.SetOFlag(value);
+}
+
+void TranslatorVisitor::ResetZero() {
+ SetZFlag(ir.Imm1(false));
+}
+
+void TranslatorVisitor::ResetSFlag() {
+ SetSFlag(ir.Imm1(false));
+}
+
+void TranslatorVisitor::ResetCFlag() {
+ SetCFlag(ir.Imm1(false));
+}
+
+void TranslatorVisitor::ResetOFlag() {
+ SetOFlag(ir.Imm1(false));
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/ir_emitter.h"
+#include "shader_recompiler/frontend/maxwell/instruction.h"
+
+namespace Shader::Maxwell {
+
+class TranslatorVisitor {
+public:
+ explicit TranslatorVisitor(Environment& env_, IR::Block& block) : env{env_} ,ir(block) {}
+
+ Environment& env;
+ IR::IREmitter ir;
+
+ void AL2P(u64 insn);
+ void ALD(u64 insn);
+ void AST(u64 insn);
+ void ATOM_cas(u64 insn);
+ void ATOM(u64 insn);
+ void ATOMS_cas(u64 insn);
+ void ATOMS(u64 insn);
+ void B2R(u64 insn);
+ void BAR(u64 insn);
+ void BFE_reg(u64 insn);
+ void BFE_cbuf(u64 insn);
+ void BFE_imm(u64 insn);
+ void BFI_reg(u64 insn);
+ void BFI_rc(u64 insn);
+ void BFI_cr(u64 insn);
+ void BFI_imm(u64 insn);
+ void BPT(u64 insn);
+ void BRA(u64 insn);
+ void BRK(u64 insn);
+ void BRX(u64 insn);
+ void CAL(u64 insn);
+ void CCTL(u64 insn);
+ void CCTLL(u64 insn);
+ void CONT(u64 insn);
+ void CS2R(u64 insn);
+ void CSET(u64 insn);
+ void CSETP(u64 insn);
+ void DADD_reg(u64 insn);
+ void DADD_cbuf(u64 insn);
+ void DADD_imm(u64 insn);
+ void DEPBAR(u64 insn);
+ void DFMA_reg(u64 insn);
+ void DFMA_rc(u64 insn);
+ void DFMA_cr(u64 insn);
+ void DFMA_imm(u64 insn);
+ void DMNMX_reg(u64 insn);
+ void DMNMX_cbuf(u64 insn);
+ void DMNMX_imm(u64 insn);
+ void DMUL_reg(u64 insn);
+ void DMUL_cbuf(u64 insn);
+ void DMUL_imm(u64 insn);
+ void DSET_reg(u64 insn);
+ void DSET_cbuf(u64 insn);
+ void DSET_imm(u64 insn);
+ void DSETP_reg(u64 insn);
+ void DSETP_cbuf(u64 insn);
+ void DSETP_imm(u64 insn);
+ void EXIT(u64 insn);
+ void F2F_reg(u64 insn);
+ void F2F_cbuf(u64 insn);
+ void F2F_imm(u64 insn);
+ void F2I_reg(u64 insn);
+ void F2I_cbuf(u64 insn);
+ void F2I_imm(u64 insn);
+ void FADD_reg(u64 insn);
+ void FADD_cbuf(u64 insn);
+ void FADD_imm(u64 insn);
+ void FADD32I(u64 insn);
+ void FCHK_reg(u64 insn);
+ void FCHK_cbuf(u64 insn);
+ void FCHK_imm(u64 insn);
+ void FCMP_reg(u64 insn);
+ void FCMP_rc(u64 insn);
+ void FCMP_cr(u64 insn);
+ void FCMP_imm(u64 insn);
+ void FFMA_reg(u64 insn);
+ void FFMA_rc(u64 insn);
+ void FFMA_cr(u64 insn);
+ void FFMA_imm(u64 insn);
+ void FFMA32I(u64 insn);
+ void FLO_reg(u64 insn);
+ void FLO_cbuf(u64 insn);
+ void FLO_imm(u64 insn);
+ void FMNMX_reg(u64 insn);
+ void FMNMX_cbuf(u64 insn);
+ void FMNMX_imm(u64 insn);
+ void FMUL_reg(u64 insn);
+ void FMUL_cbuf(u64 insn);
+ void FMUL_imm(u64 insn);
+ void FMUL32I(u64 insn);
+ void FSET_reg(u64 insn);
+ void FSET_cbuf(u64 insn);
+ void FSET_imm(u64 insn);
+ void FSETP_reg(u64 insn);
+ void FSETP_cbuf(u64 insn);
+ void FSETP_imm(u64 insn);
+ void FSWZADD(u64 insn);
+ void GETCRSPTR(u64 insn);
+ void GETLMEMBASE(u64 insn);
+ void HADD2_reg(u64 insn);
+ void HADD2_cbuf(u64 insn);
+ void HADD2_imm(u64 insn);
+ void HADD2_32I(u64 insn);
+ void HFMA2_reg(u64 insn);
+ void HFMA2_rc(u64 insn);
+ void HFMA2_cr(u64 insn);
+ void HFMA2_imm(u64 insn);
+ void HFMA2_32I(u64 insn);
+ void HMUL2_reg(u64 insn);
+ void HMUL2_cbuf(u64 insn);
+ void HMUL2_imm(u64 insn);
+ void HMUL2_32I(u64 insn);
+ void HSET2_reg(u64 insn);
+ void HSET2_cbuf(u64 insn);
+ void HSET2_imm(u64 insn);
+ void HSETP2_reg(u64 insn);
+ void HSETP2_cbuf(u64 insn);
+ void HSETP2_imm(u64 insn);
+ void I2F_reg(u64 insn);
+ void I2F_cbuf(u64 insn);
+ void I2F_imm(u64 insn);
+ void I2I_reg(u64 insn);
+ void I2I_cbuf(u64 insn);
+ void I2I_imm(u64 insn);
+ void IADD_reg(u64 insn);
+ void IADD_cbuf(u64 insn);
+ void IADD_imm(u64 insn);
+ void IADD3_reg(u64 insn);
+ void IADD3_cbuf(u64 insn);
+ void IADD3_imm(u64 insn);
+ void IADD32I(u64 insn);
+ void ICMP_reg(u64 insn);
+ void ICMP_rc(u64 insn);
+ void ICMP_cr(u64 insn);
+ void ICMP_imm(u64 insn);
+ void IDE(u64 insn);
+ void IDP_reg(u64 insn);
+ void IDP_imm(u64 insn);
+ void IMAD_reg(u64 insn);
+ void IMAD_rc(u64 insn);
+ void IMAD_cr(u64 insn);
+ void IMAD_imm(u64 insn);
+ void IMAD32I(u64 insn);
+ void IMADSP_reg(u64 insn);
+ void IMADSP_rc(u64 insn);
+ void IMADSP_cr(u64 insn);
+ void IMADSP_imm(u64 insn);
+ void IMNMX_reg(u64 insn);
+ void IMNMX_cbuf(u64 insn);
+ void IMNMX_imm(u64 insn);
+ void IMUL_reg(u64 insn);
+ void IMUL_cbuf(u64 insn);
+ void IMUL_imm(u64 insn);
+ void IMUL32I(u64 insn);
+ void IPA(u64 insn);
+ void ISBERD(u64 insn);
+ void ISCADD_reg(u64 insn);
+ void ISCADD_cbuf(u64 insn);
+ void ISCADD_imm(u64 insn);
+ void ISCADD32I(u64 insn);
+ void ISET_reg(u64 insn);
+ void ISET_cbuf(u64 insn);
+ void ISET_imm(u64 insn);
+ void ISETP_reg(u64 insn);
+ void ISETP_cbuf(u64 insn);
+ void ISETP_imm(u64 insn);
+ void JCAL(u64 insn);
+ void JMP(u64 insn);
+ void JMX(u64 insn);
+ void KIL(u64 insn);
+ void LD(u64 insn);
+ void LDC(u64 insn);
+ void LDG(u64 insn);
+ void LDL(u64 insn);
+ void LDS(u64 insn);
+ void LEA_hi_reg(u64 insn);
+ void LEA_hi_cbuf(u64 insn);
+ void LEA_lo_reg(u64 insn);
+ void LEA_lo_cbuf(u64 insn);
+ void LEA_lo_imm(u64 insn);
+ void LEPC(u64 insn);
+ void LONGJMP(u64 insn);
+ void LOP_reg(u64 insn);
+ void LOP_cbuf(u64 insn);
+ void LOP_imm(u64 insn);
+ void LOP3_reg(u64 insn);
+ void LOP3_cbuf(u64 insn);
+ void LOP3_imm(u64 insn);
+ void LOP32I(u64 insn);
+ void MEMBAR(u64 insn);
+ void MOV_reg(u64 insn);
+ void MOV_cbuf(u64 insn);
+ void MOV_imm(u64 insn);
+ void MOV32I(u64 insn);
+ void MUFU(u64 insn);
+ void NOP(u64 insn);
+ void OUT_reg(u64 insn);
+ void OUT_cbuf(u64 insn);
+ void OUT_imm(u64 insn);
+ void P2R_reg(u64 insn);
+ void P2R_cbuf(u64 insn);
+ void P2R_imm(u64 insn);
+ void PBK(u64 insn);
+ void PCNT(u64 insn);
+ void PEXIT(u64 insn);
+ void PIXLD(u64 insn);
+ void PLONGJMP(u64 insn);
+ void POPC_reg(u64 insn);
+ void POPC_cbuf(u64 insn);
+ void POPC_imm(u64 insn);
+ void PRET(u64 insn);
+ void PRMT_reg(u64 insn);
+ void PRMT_rc(u64 insn);
+ void PRMT_cr(u64 insn);
+ void PRMT_imm(u64 insn);
+ void PSET(u64 insn);
+ void PSETP(u64 insn);
+ void R2B(u64 insn);
+ void R2P_reg(u64 insn);
+ void R2P_cbuf(u64 insn);
+ void R2P_imm(u64 insn);
+ void RAM(u64 insn);
+ void RED(u64 insn);
+ void RET(u64 insn);
+ void RRO_reg(u64 insn);
+ void RRO_cbuf(u64 insn);
+ void RRO_imm(u64 insn);
+ void RTT(u64 insn);
+ void S2R(u64 insn);
+ void SAM(u64 insn);
+ void SEL_reg(u64 insn);
+ void SEL_cbuf(u64 insn);
+ void SEL_imm(u64 insn);
+ void SETCRSPTR(u64 insn);
+ void SETLMEMBASE(u64 insn);
+ void SHF_l_reg(u64 insn);
+ void SHF_l_imm(u64 insn);
+ void SHF_r_reg(u64 insn);
+ void SHF_r_imm(u64 insn);
+ void SHFL(u64 insn);
+ void SHL_reg(u64 insn);
+ void SHL_cbuf(u64 insn);
+ void SHL_imm(u64 insn);
+ void SHR_reg(u64 insn);
+ void SHR_cbuf(u64 insn);
+ void SHR_imm(u64 insn);
+ void SSY(u64 insn);
+ void ST(u64 insn);
+ void STG(u64 insn);
+ void STL(u64 insn);
+ void STP(u64 insn);
+ void STS(u64 insn);
+ void SUATOM_cas(u64 insn);
+ void SULD(u64 insn);
+ void SURED(u64 insn);
+ void SUST(u64 insn);
+ void SYNC(u64 insn);
+ void TEX(u64 insn);
+ void TEX_b(u64 insn);
+ void TEXS(u64 insn);
+ void TLD(u64 insn);
+ void TLD_b(u64 insn);
+ void TLD4(u64 insn);
+ void TLD4_b(u64 insn);
+ void TLD4S(u64 insn);
+ void TLDS(u64 insn);
+ void TMML(u64 insn);
+ void TMML_b(u64 insn);
+ void TXA(u64 insn);
+ void TXD(u64 insn);
+ void TXD_b(u64 insn);
+ void TXQ(u64 insn);
+ void TXQ_b(u64 insn);
+ void VABSDIFF(u64 insn);
+ void VABSDIFF4(u64 insn);
+ void VADD(u64 insn);
+ void VMAD(u64 insn);
+ void VMNMX(u64 insn);
+ void VOTE(u64 insn);
+ void VOTE_vtg(u64 insn);
+ void VSET(u64 insn);
+ void VSETP(u64 insn);
+ void VSHL(u64 insn);
+ void VSHR(u64 insn);
+ void XMAD_reg(u64 insn);
+ void XMAD_rc(u64 insn);
+ void XMAD_cr(u64 insn);
+ void XMAD_imm(u64 insn);
+
+ [[nodiscard]] IR::U32 X(IR::Reg reg);
+ void X(IR::Reg dest_reg, const IR::U32& value);
+
+ [[nodiscard]] IR::U32 GetCbuf(u64 insn);
+
+ [[nodiscard]] IR::U32 GetImm(u64 insn);
+
+ void SetZFlag(const IR::U1& value);
+ void SetSFlag(const IR::U1& value);
+ void SetCFlag(const IR::U1& value);
+ void SetOFlag(const IR::U1& value);
+
+ void ResetZero();
+ void ResetSFlag();
+ void ResetCFlag();
+ void ResetOFlag();
+};
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+namespace {
+enum class InterpolationMode : u64 {
+ Pass = 0,
+ Multiply = 1,
+ Constant = 2,
+ Sc = 3,
+};
+
+enum class SampleMode : u64 {
+ Default = 0,
+ Centroid = 1,
+ Offset = 2,
+};
+} // Anonymous namespace
+
+void TranslatorVisitor::IPA(u64 insn) {
+ // IPA is the instruction used to read varyings from a fragment shader.
+ // gl_FragCoord is mapped to the gl_Position attribute.
+ // It yields unknown results when used outside of the fragment shader stage.
+ union {
+ u64 raw;
+ BitField<0, 8, IR::Reg> dest_reg;
+ BitField<8, 8, IR::Reg> index_reg;
+ BitField<20, 8, IR::Reg> multiplier;
+ BitField<30, 8, IR::Attribute> attribute;
+ BitField<38, 1, u64> idx;
+ BitField<51, 1, u64> sat;
+ BitField<52, 2, SampleMode> sample_mode;
+ BitField<54, 2, InterpolationMode> interpolation_mode;
+ } const ipa{insn};
+
+ // Indexed IPAs are used for indexed varyings.
+ // For example:
+ //
+ // in vec4 colors[4];
+ // uniform int idx;
+ // void main() {
+ // gl_FragColor = colors[idx];
+ // }
+ const bool is_indexed{ipa.idx != 0 && ipa.index_reg != IR::Reg::RZ};
+ if (is_indexed) {
+ throw NotImplementedException("IPA.IDX");
+ }
+
+ const IR::Attribute attribute{ipa.attribute};
+ IR::U32 value{ir.GetAttribute(attribute)};
+ if (IR::IsGeneric(attribute)) {
+ // const bool is_perspective{UnimplementedReadHeader(GenericAttributeIndex(attribute))};
+ const bool is_perspective{false};
+ if (is_perspective) {
+ const IR::U32 rcp_position_w{ir.FPRecip(ir.GetAttribute(IR::Attribute::PositionW))};
+ value = ir.FPMul(value, rcp_position_w);
+ }
+ }
+
+ switch (ipa.interpolation_mode) {
+ case InterpolationMode::Pass:
+ break;
+ case InterpolationMode::Multiply:
+ value = ir.FPMul(value, ir.GetReg(ipa.multiplier));
+ break;
+ case InterpolationMode::Constant:
+ throw NotImplementedException("IPA.CONSTANT");
+ case InterpolationMode::Sc:
+ throw NotImplementedException("IPA.SC");
+ }
+
+ // Saturated IPAs are generally generated out of clamped varyings.
+ // For example: clamp(some_varying, 0.0, 1.0)
+ const bool is_saturated{ipa.sat != 0};
+ if (is_saturated) {
+ if (attribute == IR::Attribute::FrontFace) {
+ throw NotImplementedException("IPA.SAT on FrontFace");
+ }
+ value = ir.FPSaturate(value);
+ }
+
+ ir.SetReg(ipa.dest_reg, value);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+namespace {
+enum class StoreSize : u64 {
+ U8,
+ S8,
+ U16,
+ S16,
+ B32,
+ B64,
+ B128,
+};
+
+// See Table 28 in https://docs.nvidia.com/cuda/parallel-thread-execution/index.html
+enum class StoreCache : u64 {
+ WB, // Cache write-back all coherent levels
+ CG, // Cache at global level
+ CS, // Cache streaming, likely to be accessed once
+ WT, // Cache write-through (to system memory)
+};
+} // Anonymous namespace
+
+void TranslatorVisitor::STG(u64 insn) {
+ // STG stores registers into global memory.
+ union {
+ u64 raw;
+ BitField<0, 8, IR::Reg> data_reg;
+ BitField<8, 8, IR::Reg> addr_reg;
+ BitField<45, 1, u64> e;
+ BitField<46, 2, StoreCache> cache;
+ BitField<48, 3, StoreSize> size;
+ } const stg{insn};
+
+ const IR::U64 address{[&]() -> IR::U64 {
+ if (stg.e == 0) {
+ // STG without .E uses a 32-bit pointer, zero-extend it
+ return ir.ConvertU(64, X(stg.addr_reg));
+ }
+ if (!IR::IsAligned(stg.addr_reg, 2)) {
+ throw NotImplementedException("Unaligned address register");
+ }
+ // Pack two registers to build the 32-bit address
+ return ir.PackUint2x32(ir.CompositeConstruct(X(stg.addr_reg), X(stg.addr_reg + 1)));
+ }()};
+
+ switch (stg.size) {
+ case StoreSize::U8:
+ ir.WriteGlobalU8(address, X(stg.data_reg));
+ break;
+ case StoreSize::S8:
+ ir.WriteGlobalS8(address, X(stg.data_reg));
+ break;
+ case StoreSize::U16:
+ ir.WriteGlobalU16(address, X(stg.data_reg));
+ break;
+ case StoreSize::S16:
+ ir.WriteGlobalS16(address, X(stg.data_reg));
+ break;
+ case StoreSize::B32:
+ ir.WriteGlobal32(address, X(stg.data_reg));
+ break;
+ case StoreSize::B64: {
+ if (!IR::IsAligned(stg.data_reg, 2)) {
+ throw NotImplementedException("Unaligned data registers");
+ }
+ const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1))};
+ ir.WriteGlobal64(address, vector);
+ break;
+ }
+ case StoreSize::B128:
+ if (!IR::IsAligned(stg.data_reg, 4)) {
+ throw NotImplementedException("Unaligned data registers");
+ }
+ const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1),
+ X(stg.data_reg + 2), X(stg.data_reg + 3))};
+ ir.WriteGlobal128(address, vector);
+ break;
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+#include "shader_recompiler/ir_opt/passes.h"
+
+namespace Shader::Maxwell {
+
+[[maybe_unused]] static inline void DumpOptimized(IR::Block& block) {
+ auto raw{IR::DumpBlock(block)};
+
+ Optimization::GetSetElimination(block);
+ Optimization::DeadCodeEliminationPass(block);
+ Optimization::IdentityRemovalPass(block);
+ auto dumped{IR::DumpBlock(block)};
+
+ fmt::print(stderr, "{}", dumped);
+}
+
+[[noreturn]] static void ThrowNotImplemented(Opcode opcode) {
+ throw NotImplementedException("Instruction {} is not implemented", opcode);
+}
+
+void TranslatorVisitor::AL2P(u64) {
+ ThrowNotImplemented(Opcode::AL2P);
+}
+
+void TranslatorVisitor::ALD(u64) {
+ ThrowNotImplemented(Opcode::ALD);
+}
+
+void TranslatorVisitor::AST(u64) {
+ ThrowNotImplemented(Opcode::AST);
+}
+
+void TranslatorVisitor::ATOM_cas(u64) {
+ ThrowNotImplemented(Opcode::ATOM_cas);
+}
+
+void TranslatorVisitor::ATOM(u64) {
+ ThrowNotImplemented(Opcode::ATOM);
+}
+
+void TranslatorVisitor::ATOMS_cas(u64) {
+ ThrowNotImplemented(Opcode::ATOMS_cas);
+}
+
+void TranslatorVisitor::ATOMS(u64) {
+ ThrowNotImplemented(Opcode::ATOMS);
+}
+
+void TranslatorVisitor::B2R(u64) {
+ ThrowNotImplemented(Opcode::B2R);
+}
+
+void TranslatorVisitor::BAR(u64) {
+ ThrowNotImplemented(Opcode::BAR);
+}
+
+void TranslatorVisitor::BFE_reg(u64) {
+ ThrowNotImplemented(Opcode::BFE_reg);
+}
+
+void TranslatorVisitor::BFE_cbuf(u64) {
+ ThrowNotImplemented(Opcode::BFE_cbuf);
+}
+
+void TranslatorVisitor::BFE_imm(u64) {
+ ThrowNotImplemented(Opcode::BFE_imm);
+}
+
+void TranslatorVisitor::BFI_reg(u64) {
+ ThrowNotImplemented(Opcode::BFI_reg);
+}
+
+void TranslatorVisitor::BFI_rc(u64) {
+ ThrowNotImplemented(Opcode::BFI_rc);
+}
+
+void TranslatorVisitor::BFI_cr(u64) {
+ ThrowNotImplemented(Opcode::BFI_cr);
+}
+
+void TranslatorVisitor::BFI_imm(u64) {
+ ThrowNotImplemented(Opcode::BFI_imm);
+}
+
+void TranslatorVisitor::BPT(u64) {
+ ThrowNotImplemented(Opcode::BPT);
+}
+
+void TranslatorVisitor::BRA(u64) {
+ ThrowNotImplemented(Opcode::BRA);
+}
+
+void TranslatorVisitor::BRK(u64) {
+ ThrowNotImplemented(Opcode::BRK);
+}
+
+void TranslatorVisitor::BRX(u64) {
+ ThrowNotImplemented(Opcode::BRX);
+}
+
+void TranslatorVisitor::CAL(u64) {
+ ThrowNotImplemented(Opcode::CAL);
+}
+
+void TranslatorVisitor::CCTL(u64) {
+ ThrowNotImplemented(Opcode::CCTL);
+}
+
+void TranslatorVisitor::CCTLL(u64) {
+ ThrowNotImplemented(Opcode::CCTLL);
+}
+
+void TranslatorVisitor::CONT(u64) {
+ ThrowNotImplemented(Opcode::CONT);
+}
+
+void TranslatorVisitor::CS2R(u64) {
+ ThrowNotImplemented(Opcode::CS2R);
+}
+
+void TranslatorVisitor::CSET(u64) {
+ ThrowNotImplemented(Opcode::CSET);
+}
+
+void TranslatorVisitor::CSETP(u64) {
+ ThrowNotImplemented(Opcode::CSETP);
+}
+
+void TranslatorVisitor::DADD_reg(u64) {
+ ThrowNotImplemented(Opcode::DADD_reg);
+}
+
+void TranslatorVisitor::DADD_cbuf(u64) {
+ ThrowNotImplemented(Opcode::DADD_cbuf);
+}
+
+void TranslatorVisitor::DADD_imm(u64) {
+ ThrowNotImplemented(Opcode::DADD_imm);
+}
+
+void TranslatorVisitor::DEPBAR(u64) {
+ ThrowNotImplemented(Opcode::DEPBAR);
+}
+
+void TranslatorVisitor::DFMA_reg(u64) {
+ ThrowNotImplemented(Opcode::DFMA_reg);
+}
+
+void TranslatorVisitor::DFMA_rc(u64) {
+ ThrowNotImplemented(Opcode::DFMA_rc);
+}
+
+void TranslatorVisitor::DFMA_cr(u64) {
+ ThrowNotImplemented(Opcode::DFMA_cr);
+}
+
+void TranslatorVisitor::DFMA_imm(u64) {
+ ThrowNotImplemented(Opcode::DFMA_imm);
+}
+
+void TranslatorVisitor::DMNMX_reg(u64) {
+ ThrowNotImplemented(Opcode::DMNMX_reg);
+}
+
+void TranslatorVisitor::DMNMX_cbuf(u64) {
+ ThrowNotImplemented(Opcode::DMNMX_cbuf);
+}
+
+void TranslatorVisitor::DMNMX_imm(u64) {
+ ThrowNotImplemented(Opcode::DMNMX_imm);
+}
+
+void TranslatorVisitor::DMUL_reg(u64) {
+ ThrowNotImplemented(Opcode::DMUL_reg);
+}
+
+void TranslatorVisitor::DMUL_cbuf(u64) {
+ ThrowNotImplemented(Opcode::DMUL_cbuf);
+}
+
+void TranslatorVisitor::DMUL_imm(u64) {
+ ThrowNotImplemented(Opcode::DMUL_imm);
+}
+
+void TranslatorVisitor::DSET_reg(u64) {
+ ThrowNotImplemented(Opcode::DSET_reg);
+}
+
+void TranslatorVisitor::DSET_cbuf(u64) {
+ ThrowNotImplemented(Opcode::DSET_cbuf);
+}
+
+void TranslatorVisitor::DSET_imm(u64) {
+ ThrowNotImplemented(Opcode::DSET_imm);
+}
+
+void TranslatorVisitor::DSETP_reg(u64) {
+ ThrowNotImplemented(Opcode::DSETP_reg);
+}
+
+void TranslatorVisitor::DSETP_cbuf(u64) {
+ ThrowNotImplemented(Opcode::DSETP_cbuf);
+}
+
+void TranslatorVisitor::DSETP_imm(u64) {
+ ThrowNotImplemented(Opcode::DSETP_imm);
+}
+
+void TranslatorVisitor::EXIT(u64) {
+ throw LogicError("Visting EXIT instruction");
+}
+
+void TranslatorVisitor::F2F_reg(u64) {
+ ThrowNotImplemented(Opcode::F2F_reg);
+}
+
+void TranslatorVisitor::F2F_cbuf(u64) {
+ ThrowNotImplemented(Opcode::F2F_cbuf);
+}
+
+void TranslatorVisitor::F2F_imm(u64) {
+ ThrowNotImplemented(Opcode::F2F_imm);
+}
+
+void TranslatorVisitor::FADD_reg(u64) {
+ ThrowNotImplemented(Opcode::FADD_reg);
+}
+
+void TranslatorVisitor::FADD_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FADD_cbuf);
+}
+
+void TranslatorVisitor::FADD_imm(u64) {
+ ThrowNotImplemented(Opcode::FADD_imm);
+}
+
+void TranslatorVisitor::FADD32I(u64) {
+ ThrowNotImplemented(Opcode::FADD32I);
+}
+
+void TranslatorVisitor::FCHK_reg(u64) {
+ ThrowNotImplemented(Opcode::FCHK_reg);
+}
+
+void TranslatorVisitor::FCHK_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FCHK_cbuf);
+}
+
+void TranslatorVisitor::FCHK_imm(u64) {
+ ThrowNotImplemented(Opcode::FCHK_imm);
+}
+
+void TranslatorVisitor::FCMP_reg(u64) {
+ ThrowNotImplemented(Opcode::FCMP_reg);
+}
+
+void TranslatorVisitor::FCMP_rc(u64) {
+ ThrowNotImplemented(Opcode::FCMP_rc);
+}
+
+void TranslatorVisitor::FCMP_cr(u64) {
+ ThrowNotImplemented(Opcode::FCMP_cr);
+}
+
+void TranslatorVisitor::FCMP_imm(u64) {
+ ThrowNotImplemented(Opcode::FCMP_imm);
+}
+
+void TranslatorVisitor::FFMA_reg(u64) {
+ ThrowNotImplemented(Opcode::FFMA_reg);
+}
+
+void TranslatorVisitor::FFMA_rc(u64) {
+ ThrowNotImplemented(Opcode::FFMA_rc);
+}
+
+void TranslatorVisitor::FFMA_cr(u64) {
+ ThrowNotImplemented(Opcode::FFMA_cr);
+}
+
+void TranslatorVisitor::FFMA_imm(u64) {
+ ThrowNotImplemented(Opcode::FFMA_imm);
+}
+
+void TranslatorVisitor::FFMA32I(u64) {
+ ThrowNotImplemented(Opcode::FFMA32I);
+}
+
+void TranslatorVisitor::FLO_reg(u64) {
+ ThrowNotImplemented(Opcode::FLO_reg);
+}
+
+void TranslatorVisitor::FLO_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FLO_cbuf);
+}
+
+void TranslatorVisitor::FLO_imm(u64) {
+ ThrowNotImplemented(Opcode::FLO_imm);
+}
+
+void TranslatorVisitor::FMNMX_reg(u64) {
+ ThrowNotImplemented(Opcode::FMNMX_reg);
+}
+
+void TranslatorVisitor::FMNMX_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FMNMX_cbuf);
+}
+
+void TranslatorVisitor::FMNMX_imm(u64) {
+ ThrowNotImplemented(Opcode::FMNMX_imm);
+}
+
+void TranslatorVisitor::FMUL_reg(u64) {
+ ThrowNotImplemented(Opcode::FMUL_reg);
+}
+
+void TranslatorVisitor::FMUL_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FMUL_cbuf);
+}
+
+void TranslatorVisitor::FMUL_imm(u64) {
+ ThrowNotImplemented(Opcode::FMUL_imm);
+}
+
+void TranslatorVisitor::FMUL32I(u64) {
+ ThrowNotImplemented(Opcode::FMUL32I);
+}
+
+void TranslatorVisitor::FSET_reg(u64) {
+ ThrowNotImplemented(Opcode::FSET_reg);
+}
+
+void TranslatorVisitor::FSET_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FSET_cbuf);
+}
+
+void TranslatorVisitor::FSET_imm(u64) {
+ ThrowNotImplemented(Opcode::FSET_imm);
+}
+
+void TranslatorVisitor::FSETP_reg(u64) {
+ ThrowNotImplemented(Opcode::FSETP_reg);
+}
+
+void TranslatorVisitor::FSETP_cbuf(u64) {
+ ThrowNotImplemented(Opcode::FSETP_cbuf);
+}
+
+void TranslatorVisitor::FSETP_imm(u64) {
+ ThrowNotImplemented(Opcode::FSETP_imm);
+}
+
+void TranslatorVisitor::FSWZADD(u64) {
+ ThrowNotImplemented(Opcode::FSWZADD);
+}
+
+void TranslatorVisitor::GETCRSPTR(u64) {
+ ThrowNotImplemented(Opcode::GETCRSPTR);
+}
+
+void TranslatorVisitor::GETLMEMBASE(u64) {
+ ThrowNotImplemented(Opcode::GETLMEMBASE);
+}
+
+void TranslatorVisitor::HADD2_reg(u64) {
+ ThrowNotImplemented(Opcode::HADD2_reg);
+}
+
+void TranslatorVisitor::HADD2_cbuf(u64) {
+ ThrowNotImplemented(Opcode::HADD2_cbuf);
+}
+
+void TranslatorVisitor::HADD2_imm(u64) {
+ ThrowNotImplemented(Opcode::HADD2_imm);
+}
+
+void TranslatorVisitor::HADD2_32I(u64) {
+ ThrowNotImplemented(Opcode::HADD2_32I);
+}
+
+void TranslatorVisitor::HFMA2_reg(u64) {
+ ThrowNotImplemented(Opcode::HFMA2_reg);
+}
+
+void TranslatorVisitor::HFMA2_rc(u64) {
+ ThrowNotImplemented(Opcode::HFMA2_rc);
+}
+
+void TranslatorVisitor::HFMA2_cr(u64) {
+ ThrowNotImplemented(Opcode::HFMA2_cr);
+}
+
+void TranslatorVisitor::HFMA2_imm(u64) {
+ ThrowNotImplemented(Opcode::HFMA2_imm);
+}
+
+void TranslatorVisitor::HFMA2_32I(u64) {
+ ThrowNotImplemented(Opcode::HFMA2_32I);
+}
+
+void TranslatorVisitor::HMUL2_reg(u64) {
+ ThrowNotImplemented(Opcode::HMUL2_reg);
+}
+
+void TranslatorVisitor::HMUL2_cbuf(u64) {
+ ThrowNotImplemented(Opcode::HMUL2_cbuf);
+}
+
+void TranslatorVisitor::HMUL2_imm(u64) {
+ ThrowNotImplemented(Opcode::HMUL2_imm);
+}
+
+void TranslatorVisitor::HMUL2_32I(u64) {
+ ThrowNotImplemented(Opcode::HMUL2_32I);
+}
+
+void TranslatorVisitor::HSET2_reg(u64) {
+ ThrowNotImplemented(Opcode::HSET2_reg);
+}
+
+void TranslatorVisitor::HSET2_cbuf(u64) {
+ ThrowNotImplemented(Opcode::HSET2_cbuf);
+}
+
+void TranslatorVisitor::HSET2_imm(u64) {
+ ThrowNotImplemented(Opcode::HSET2_imm);
+}
+
+void TranslatorVisitor::HSETP2_reg(u64) {
+ ThrowNotImplemented(Opcode::HSETP2_reg);
+}
+
+void TranslatorVisitor::HSETP2_cbuf(u64) {
+ ThrowNotImplemented(Opcode::HSETP2_cbuf);
+}
+
+void TranslatorVisitor::HSETP2_imm(u64) {
+ ThrowNotImplemented(Opcode::HSETP2_imm);
+}
+
+void TranslatorVisitor::I2F_reg(u64) {
+ ThrowNotImplemented(Opcode::I2F_reg);
+}
+
+void TranslatorVisitor::I2F_cbuf(u64) {
+ ThrowNotImplemented(Opcode::I2F_cbuf);
+}
+
+void TranslatorVisitor::I2F_imm(u64) {
+ ThrowNotImplemented(Opcode::I2F_imm);
+}
+
+void TranslatorVisitor::I2I_reg(u64) {
+ ThrowNotImplemented(Opcode::I2I_reg);
+}
+
+void TranslatorVisitor::I2I_cbuf(u64) {
+ ThrowNotImplemented(Opcode::I2I_cbuf);
+}
+
+void TranslatorVisitor::I2I_imm(u64) {
+ ThrowNotImplemented(Opcode::I2I_imm);
+}
+
+void TranslatorVisitor::IADD_reg(u64) {
+ ThrowNotImplemented(Opcode::IADD_reg);
+}
+
+void TranslatorVisitor::IADD_cbuf(u64) {
+ ThrowNotImplemented(Opcode::IADD_cbuf);
+}
+
+void TranslatorVisitor::IADD_imm(u64) {
+ ThrowNotImplemented(Opcode::IADD_imm);
+}
+
+void TranslatorVisitor::IADD3_reg(u64) {
+ ThrowNotImplemented(Opcode::IADD3_reg);
+}
+
+void TranslatorVisitor::IADD3_cbuf(u64) {
+ ThrowNotImplemented(Opcode::IADD3_cbuf);
+}
+
+void TranslatorVisitor::IADD3_imm(u64) {
+ ThrowNotImplemented(Opcode::IADD3_imm);
+}
+
+void TranslatorVisitor::IADD32I(u64) {
+ ThrowNotImplemented(Opcode::IADD32I);
+}
+
+void TranslatorVisitor::ICMP_reg(u64) {
+ ThrowNotImplemented(Opcode::ICMP_reg);
+}
+
+void TranslatorVisitor::ICMP_rc(u64) {
+ ThrowNotImplemented(Opcode::ICMP_rc);
+}
+
+void TranslatorVisitor::ICMP_cr(u64) {
+ ThrowNotImplemented(Opcode::ICMP_cr);
+}
+
+void TranslatorVisitor::ICMP_imm(u64) {
+ ThrowNotImplemented(Opcode::ICMP_imm);
+}
+
+void TranslatorVisitor::IDE(u64) {
+ ThrowNotImplemented(Opcode::IDE);
+}
+
+void TranslatorVisitor::IDP_reg(u64) {
+ ThrowNotImplemented(Opcode::IDP_reg);
+}
+
+void TranslatorVisitor::IDP_imm(u64) {
+ ThrowNotImplemented(Opcode::IDP_imm);
+}
+
+void TranslatorVisitor::IMAD_reg(u64) {
+ ThrowNotImplemented(Opcode::IMAD_reg);
+}
+
+void TranslatorVisitor::IMAD_rc(u64) {
+ ThrowNotImplemented(Opcode::IMAD_rc);
+}
+
+void TranslatorVisitor::IMAD_cr(u64) {
+ ThrowNotImplemented(Opcode::IMAD_cr);
+}
+
+void TranslatorVisitor::IMAD_imm(u64) {
+ ThrowNotImplemented(Opcode::IMAD_imm);
+}
+
+void TranslatorVisitor::IMAD32I(u64) {
+ ThrowNotImplemented(Opcode::IMAD32I);
+}
+
+void TranslatorVisitor::IMADSP_reg(u64) {
+ ThrowNotImplemented(Opcode::IMADSP_reg);
+}
+
+void TranslatorVisitor::IMADSP_rc(u64) {
+ ThrowNotImplemented(Opcode::IMADSP_rc);
+}
+
+void TranslatorVisitor::IMADSP_cr(u64) {
+ ThrowNotImplemented(Opcode::IMADSP_cr);
+}
+
+void TranslatorVisitor::IMADSP_imm(u64) {
+ ThrowNotImplemented(Opcode::IMADSP_imm);
+}
+
+void TranslatorVisitor::IMNMX_reg(u64) {
+ ThrowNotImplemented(Opcode::IMNMX_reg);
+}
+
+void TranslatorVisitor::IMNMX_cbuf(u64) {
+ ThrowNotImplemented(Opcode::IMNMX_cbuf);
+}
+
+void TranslatorVisitor::IMNMX_imm(u64) {
+ ThrowNotImplemented(Opcode::IMNMX_imm);
+}
+
+void TranslatorVisitor::IMUL_reg(u64) {
+ ThrowNotImplemented(Opcode::IMUL_reg);
+}
+
+void TranslatorVisitor::IMUL_cbuf(u64) {
+ ThrowNotImplemented(Opcode::IMUL_cbuf);
+}
+
+void TranslatorVisitor::IMUL_imm(u64) {
+ ThrowNotImplemented(Opcode::IMUL_imm);
+}
+
+void TranslatorVisitor::IMUL32I(u64) {
+ ThrowNotImplemented(Opcode::IMUL32I);
+}
+
+void TranslatorVisitor::ISBERD(u64) {
+ ThrowNotImplemented(Opcode::ISBERD);
+}
+
+void TranslatorVisitor::ISCADD_reg(u64) {
+ ThrowNotImplemented(Opcode::ISCADD_reg);
+}
+
+void TranslatorVisitor::ISCADD_cbuf(u64) {
+ ThrowNotImplemented(Opcode::ISCADD_cbuf);
+}
+
+void TranslatorVisitor::ISCADD_imm(u64) {
+ ThrowNotImplemented(Opcode::ISCADD_imm);
+}
+
+void TranslatorVisitor::ISCADD32I(u64) {
+ ThrowNotImplemented(Opcode::ISCADD32I);
+}
+
+void TranslatorVisitor::ISET_reg(u64) {
+ ThrowNotImplemented(Opcode::ISET_reg);
+}
+
+void TranslatorVisitor::ISET_cbuf(u64) {
+ ThrowNotImplemented(Opcode::ISET_cbuf);
+}
+
+void TranslatorVisitor::ISET_imm(u64) {
+ ThrowNotImplemented(Opcode::ISET_imm);
+}
+
+void TranslatorVisitor::ISETP_reg(u64) {
+ ThrowNotImplemented(Opcode::ISETP_reg);
+}
+
+void TranslatorVisitor::ISETP_cbuf(u64) {
+ ThrowNotImplemented(Opcode::ISETP_cbuf);
+}
+
+void TranslatorVisitor::ISETP_imm(u64) {
+ ThrowNotImplemented(Opcode::ISETP_imm);
+}
+
+void TranslatorVisitor::JCAL(u64) {
+ ThrowNotImplemented(Opcode::JCAL);
+}
+
+void TranslatorVisitor::JMP(u64) {
+ ThrowNotImplemented(Opcode::JMP);
+}
+
+void TranslatorVisitor::JMX(u64) {
+ ThrowNotImplemented(Opcode::JMX);
+}
+
+void TranslatorVisitor::KIL(u64) {
+ ThrowNotImplemented(Opcode::KIL);
+}
+
+void TranslatorVisitor::LD(u64) {
+ ThrowNotImplemented(Opcode::LD);
+}
+
+void TranslatorVisitor::LDC(u64) {
+ ThrowNotImplemented(Opcode::LDC);
+}
+
+void TranslatorVisitor::LDG(u64) {
+ ThrowNotImplemented(Opcode::LDG);
+}
+
+void TranslatorVisitor::LDL(u64) {
+ ThrowNotImplemented(Opcode::LDL);
+}
+
+void TranslatorVisitor::LDS(u64) {
+ ThrowNotImplemented(Opcode::LDS);
+}
+
+void TranslatorVisitor::LEA_hi_reg(u64) {
+ ThrowNotImplemented(Opcode::LEA_hi_reg);
+}
+
+void TranslatorVisitor::LEA_hi_cbuf(u64) {
+ ThrowNotImplemented(Opcode::LEA_hi_cbuf);
+}
+
+void TranslatorVisitor::LEA_lo_reg(u64) {
+ ThrowNotImplemented(Opcode::LEA_lo_reg);
+}
+
+void TranslatorVisitor::LEA_lo_cbuf(u64) {
+ ThrowNotImplemented(Opcode::LEA_lo_cbuf);
+}
+
+void TranslatorVisitor::LEA_lo_imm(u64) {
+ ThrowNotImplemented(Opcode::LEA_lo_imm);
+}
+
+void TranslatorVisitor::LEPC(u64) {
+ ThrowNotImplemented(Opcode::LEPC);
+}
+
+void TranslatorVisitor::LONGJMP(u64) {
+ ThrowNotImplemented(Opcode::LONGJMP);
+}
+
+void TranslatorVisitor::LOP_reg(u64) {
+ ThrowNotImplemented(Opcode::LOP_reg);
+}
+
+void TranslatorVisitor::LOP_cbuf(u64) {
+ ThrowNotImplemented(Opcode::LOP_cbuf);
+}
+
+void TranslatorVisitor::LOP_imm(u64) {
+ ThrowNotImplemented(Opcode::LOP_imm);
+}
+
+void TranslatorVisitor::LOP3_reg(u64) {
+ ThrowNotImplemented(Opcode::LOP3_reg);
+}
+
+void TranslatorVisitor::LOP3_cbuf(u64) {
+ ThrowNotImplemented(Opcode::LOP3_cbuf);
+}
+
+void TranslatorVisitor::LOP3_imm(u64) {
+ ThrowNotImplemented(Opcode::LOP3_imm);
+}
+
+void TranslatorVisitor::LOP32I(u64) {
+ ThrowNotImplemented(Opcode::LOP32I);
+}
+
+void TranslatorVisitor::MEMBAR(u64) {
+ ThrowNotImplemented(Opcode::MEMBAR);
+}
+
+void TranslatorVisitor::MOV32I(u64) {
+ ThrowNotImplemented(Opcode::MOV32I);
+}
+
+void TranslatorVisitor::NOP(u64) {
+ ThrowNotImplemented(Opcode::NOP);
+}
+
+void TranslatorVisitor::OUT_reg(u64) {
+ ThrowNotImplemented(Opcode::OUT_reg);
+}
+
+void TranslatorVisitor::OUT_cbuf(u64) {
+ ThrowNotImplemented(Opcode::OUT_cbuf);
+}
+
+void TranslatorVisitor::OUT_imm(u64) {
+ ThrowNotImplemented(Opcode::OUT_imm);
+}
+
+void TranslatorVisitor::P2R_reg(u64) {
+ ThrowNotImplemented(Opcode::P2R_reg);
+}
+
+void TranslatorVisitor::P2R_cbuf(u64) {
+ ThrowNotImplemented(Opcode::P2R_cbuf);
+}
+
+void TranslatorVisitor::P2R_imm(u64) {
+ ThrowNotImplemented(Opcode::P2R_imm);
+}
+
+void TranslatorVisitor::PBK(u64) {
+ // PBK is a no-op
+}
+
+void TranslatorVisitor::PCNT(u64) {
+ ThrowNotImplemented(Opcode::PCNT);
+}
+
+void TranslatorVisitor::PEXIT(u64) {
+ ThrowNotImplemented(Opcode::PEXIT);
+}
+
+void TranslatorVisitor::PIXLD(u64) {
+ ThrowNotImplemented(Opcode::PIXLD);
+}
+
+void TranslatorVisitor::PLONGJMP(u64) {
+ ThrowNotImplemented(Opcode::PLONGJMP);
+}
+
+void TranslatorVisitor::POPC_reg(u64) {
+ ThrowNotImplemented(Opcode::POPC_reg);
+}
+
+void TranslatorVisitor::POPC_cbuf(u64) {
+ ThrowNotImplemented(Opcode::POPC_cbuf);
+}
+
+void TranslatorVisitor::POPC_imm(u64) {
+ ThrowNotImplemented(Opcode::POPC_imm);
+}
+
+void TranslatorVisitor::PRET(u64) {
+ ThrowNotImplemented(Opcode::PRET);
+}
+
+void TranslatorVisitor::PRMT_reg(u64) {
+ ThrowNotImplemented(Opcode::PRMT_reg);
+}
+
+void TranslatorVisitor::PRMT_rc(u64) {
+ ThrowNotImplemented(Opcode::PRMT_rc);
+}
+
+void TranslatorVisitor::PRMT_cr(u64) {
+ ThrowNotImplemented(Opcode::PRMT_cr);
+}
+
+void TranslatorVisitor::PRMT_imm(u64) {
+ ThrowNotImplemented(Opcode::PRMT_imm);
+}
+
+void TranslatorVisitor::PSET(u64) {
+ ThrowNotImplemented(Opcode::PSET);
+}
+
+void TranslatorVisitor::PSETP(u64) {
+ ThrowNotImplemented(Opcode::PSETP);
+}
+
+void TranslatorVisitor::R2B(u64) {
+ ThrowNotImplemented(Opcode::R2B);
+}
+
+void TranslatorVisitor::R2P_reg(u64) {
+ ThrowNotImplemented(Opcode::R2P_reg);
+}
+
+void TranslatorVisitor::R2P_cbuf(u64) {
+ ThrowNotImplemented(Opcode::R2P_cbuf);
+}
+
+void TranslatorVisitor::R2P_imm(u64) {
+ ThrowNotImplemented(Opcode::R2P_imm);
+}
+
+void TranslatorVisitor::RAM(u64) {
+ ThrowNotImplemented(Opcode::RAM);
+}
+
+void TranslatorVisitor::RED(u64) {
+ ThrowNotImplemented(Opcode::RED);
+}
+
+void TranslatorVisitor::RET(u64) {
+ ThrowNotImplemented(Opcode::RET);
+}
+
+void TranslatorVisitor::RRO_reg(u64) {
+ ThrowNotImplemented(Opcode::RRO_reg);
+}
+
+void TranslatorVisitor::RRO_cbuf(u64) {
+ ThrowNotImplemented(Opcode::RRO_cbuf);
+}
+
+void TranslatorVisitor::RRO_imm(u64) {
+ ThrowNotImplemented(Opcode::RRO_imm);
+}
+
+void TranslatorVisitor::RTT(u64) {
+ ThrowNotImplemented(Opcode::RTT);
+}
+
+void TranslatorVisitor::S2R(u64) {
+ ThrowNotImplemented(Opcode::S2R);
+}
+
+void TranslatorVisitor::SAM(u64) {
+ ThrowNotImplemented(Opcode::SAM);
+}
+
+void TranslatorVisitor::SEL_reg(u64) {
+ ThrowNotImplemented(Opcode::SEL_reg);
+}
+
+void TranslatorVisitor::SEL_cbuf(u64) {
+ ThrowNotImplemented(Opcode::SEL_cbuf);
+}
+
+void TranslatorVisitor::SEL_imm(u64) {
+ ThrowNotImplemented(Opcode::SEL_imm);
+}
+
+void TranslatorVisitor::SETCRSPTR(u64) {
+ ThrowNotImplemented(Opcode::SETCRSPTR);
+}
+
+void TranslatorVisitor::SETLMEMBASE(u64) {
+ ThrowNotImplemented(Opcode::SETLMEMBASE);
+}
+
+void TranslatorVisitor::SHF_l_reg(u64) {
+ ThrowNotImplemented(Opcode::SHF_l_reg);
+}
+
+void TranslatorVisitor::SHF_l_imm(u64) {
+ ThrowNotImplemented(Opcode::SHF_l_imm);
+}
+
+void TranslatorVisitor::SHF_r_reg(u64) {
+ ThrowNotImplemented(Opcode::SHF_r_reg);
+}
+
+void TranslatorVisitor::SHF_r_imm(u64) {
+ ThrowNotImplemented(Opcode::SHF_r_imm);
+}
+
+void TranslatorVisitor::SHFL(u64) {
+ ThrowNotImplemented(Opcode::SHFL);
+}
+
+void TranslatorVisitor::SHL_reg(u64) {
+ ThrowNotImplemented(Opcode::SHL_reg);
+}
+
+void TranslatorVisitor::SHL_cbuf(u64) {
+ ThrowNotImplemented(Opcode::SHL_cbuf);
+}
+
+void TranslatorVisitor::SHL_imm(u64) {
+ ThrowNotImplemented(Opcode::SHL_imm);
+}
+
+void TranslatorVisitor::SHR_reg(u64) {
+ ThrowNotImplemented(Opcode::SHR_reg);
+}
+
+void TranslatorVisitor::SHR_cbuf(u64) {
+ ThrowNotImplemented(Opcode::SHR_cbuf);
+}
+
+void TranslatorVisitor::SHR_imm(u64) {
+ ThrowNotImplemented(Opcode::SHR_imm);
+}
+
+void TranslatorVisitor::SSY(u64) {
+ ThrowNotImplemented(Opcode::SSY);
+}
+
+void TranslatorVisitor::ST(u64) {
+ ThrowNotImplemented(Opcode::ST);
+}
+
+void TranslatorVisitor::STL(u64) {
+ ThrowNotImplemented(Opcode::STL);
+}
+
+void TranslatorVisitor::STP(u64) {
+ ThrowNotImplemented(Opcode::STP);
+}
+
+void TranslatorVisitor::STS(u64) {
+ ThrowNotImplemented(Opcode::STS);
+}
+
+void TranslatorVisitor::SUATOM_cas(u64) {
+ ThrowNotImplemented(Opcode::SUATOM_cas);
+}
+
+void TranslatorVisitor::SULD(u64) {
+ ThrowNotImplemented(Opcode::SULD);
+}
+
+void TranslatorVisitor::SURED(u64) {
+ ThrowNotImplemented(Opcode::SURED);
+}
+
+void TranslatorVisitor::SUST(u64) {
+ ThrowNotImplemented(Opcode::SUST);
+}
+
+void TranslatorVisitor::SYNC(u64) {
+ ThrowNotImplemented(Opcode::SYNC);
+}
+
+void TranslatorVisitor::TEX(u64) {
+ ThrowNotImplemented(Opcode::TEX);
+}
+
+void TranslatorVisitor::TEX_b(u64) {
+ ThrowNotImplemented(Opcode::TEX_b);
+}
+
+void TranslatorVisitor::TEXS(u64) {
+ ThrowNotImplemented(Opcode::TEXS);
+}
+
+void TranslatorVisitor::TLD(u64) {
+ ThrowNotImplemented(Opcode::TLD);
+}
+
+void TranslatorVisitor::TLD_b(u64) {
+ ThrowNotImplemented(Opcode::TLD_b);
+}
+
+void TranslatorVisitor::TLD4(u64) {
+ ThrowNotImplemented(Opcode::TLD4);
+}
+
+void TranslatorVisitor::TLD4_b(u64) {
+ ThrowNotImplemented(Opcode::TLD4_b);
+}
+
+void TranslatorVisitor::TLD4S(u64) {
+ ThrowNotImplemented(Opcode::TLD4S);
+}
+
+void TranslatorVisitor::TLDS(u64) {
+ ThrowNotImplemented(Opcode::TLDS);
+}
+
+void TranslatorVisitor::TMML(u64) {
+ ThrowNotImplemented(Opcode::TMML);
+}
+
+void TranslatorVisitor::TMML_b(u64) {
+ ThrowNotImplemented(Opcode::TMML_b);
+}
+
+void TranslatorVisitor::TXA(u64) {
+ ThrowNotImplemented(Opcode::TXA);
+}
+
+void TranslatorVisitor::TXD(u64) {
+ ThrowNotImplemented(Opcode::TXD);
+}
+
+void TranslatorVisitor::TXD_b(u64) {
+ ThrowNotImplemented(Opcode::TXD_b);
+}
+
+void TranslatorVisitor::TXQ(u64) {
+ ThrowNotImplemented(Opcode::TXQ);
+}
+
+void TranslatorVisitor::TXQ_b(u64) {
+ ThrowNotImplemented(Opcode::TXQ_b);
+}
+
+void TranslatorVisitor::VABSDIFF(u64) {
+ ThrowNotImplemented(Opcode::VABSDIFF);
+}
+
+void TranslatorVisitor::VABSDIFF4(u64) {
+ ThrowNotImplemented(Opcode::VABSDIFF4);
+}
+
+void TranslatorVisitor::VADD(u64) {
+ ThrowNotImplemented(Opcode::VADD);
+}
+
+void TranslatorVisitor::VMAD(u64) {
+ ThrowNotImplemented(Opcode::VMAD);
+}
+
+void TranslatorVisitor::VMNMX(u64) {
+ ThrowNotImplemented(Opcode::VMNMX);
+}
+
+void TranslatorVisitor::VOTE(u64) {
+ ThrowNotImplemented(Opcode::VOTE);
+}
+
+void TranslatorVisitor::VOTE_vtg(u64) {
+ ThrowNotImplemented(Opcode::VOTE_vtg);
+}
+
+void TranslatorVisitor::VSET(u64) {
+ ThrowNotImplemented(Opcode::VSET);
+}
+
+void TranslatorVisitor::VSETP(u64) {
+ ThrowNotImplemented(Opcode::VSETP);
+}
+
+void TranslatorVisitor::VSHL(u64) {
+ ThrowNotImplemented(Opcode::VSHL);
+}
+
+void TranslatorVisitor::VSHR(u64) {
+ ThrowNotImplemented(Opcode::VSHR);
+}
+
+void TranslatorVisitor::XMAD_reg(u64) {
+ ThrowNotImplemented(Opcode::XMAD_reg);
+}
+
+void TranslatorVisitor::XMAD_rc(u64) {
+ ThrowNotImplemented(Opcode::XMAD_rc);
+}
+
+void TranslatorVisitor::XMAD_cr(u64) {
+ ThrowNotImplemented(Opcode::XMAD_cr);
+}
+
+void TranslatorVisitor::XMAD_imm(u64) {
+ ThrowNotImplemented(Opcode::XMAD_imm);
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/maxwell/opcode.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+
+namespace Shader::Maxwell {
+namespace {
+union MOV {
+ u64 raw;
+ BitField<0, 8, IR::Reg> dest_reg;
+ BitField<20, 8, IR::Reg> src_reg;
+ BitField<39, 4, u64> mask;
+};
+
+void CheckMask(MOV mov) {
+ if (mov.mask != 0xf) {
+ throw NotImplementedException("Non-full move mask");
+ }
+}
+} // Anonymous namespace
+
+void TranslatorVisitor::MOV_reg(u64 insn) {
+ const MOV mov{insn};
+ CheckMask(mov);
+ X(mov.dest_reg, X(mov.src_reg));
+}
+
+void TranslatorVisitor::MOV_cbuf(u64 insn) {
+ const MOV mov{insn};
+ CheckMask(mov);
+ X(mov.dest_reg, GetCbuf(insn));
+}
+
+void TranslatorVisitor::MOV_imm(u64 insn) {
+ const MOV mov{insn};
+ CheckMask(mov);
+ X(mov.dest_reg, GetImm(insn));
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/maxwell/decode.h"
+#include "shader_recompiler/frontend/maxwell/location.h"
+#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
+#include "shader_recompiler/frontend/maxwell/translate/translate.h"
+
+namespace Shader::Maxwell {
+
+template <auto visitor_method>
+static void Invoke(TranslatorVisitor& visitor, Location pc, u64 insn) {
+ using MethodType = decltype(visitor_method);
+ if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, Location, u64>) {
+ (visitor.*visitor_method)(pc, insn);
+ } else if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, u64>) {
+ (visitor.*visitor_method)(insn);
+ } else {
+ (visitor.*visitor_method)();
+ }
+}
+
+IR::Block Translate(Environment& env, const Flow::Block& flow_block) {
+ IR::Block block{flow_block.begin.Offset(), flow_block.end.Offset()};
+ TranslatorVisitor visitor{env, block};
+
+ const Location pc_end{flow_block.end};
+ Location pc{flow_block.begin};
+ while (pc != pc_end) {
+ const u64 insn{env.ReadInstruction(pc.Offset())};
+ const Opcode opcode{Decode(insn)};
+ switch (opcode) {
+#define INST(name, cute, mask) \
+ case Opcode::name: \
+ Invoke<&TranslatorVisitor::name>(visitor, pc, insn); \
+ break;
+#include "shader_recompiler/frontend/maxwell/maxwell.inc"
+#undef OPCODE
+ default:
+ throw LogicError("Invalid opcode {}", opcode);
+ }
+ ++pc;
+ }
+ return block;
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "shader_recompiler/environment.h"
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/maxwell/location.h"
+#include "shader_recompiler/frontend/maxwell/control_flow.h"
+
+namespace Shader::Maxwell {
+
+[[nodiscard]] IR::Block Translate(Environment& env, const Flow::Block& flow_block);
+
+} // namespace Shader::Maxwell