diff options
Diffstat (limited to 'src/shader_recompiler/frontend')
14 files changed, 347 insertions, 44 deletions
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp index 9b898e4e1..552472487 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp +++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp | |||
| @@ -87,6 +87,10 @@ void IREmitter::Return() { | |||
| 87 | Inst(Opcode::Return); | 87 | Inst(Opcode::Return); |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | void IREmitter::Unreachable() { | ||
| 91 | Inst(Opcode::Unreachable); | ||
| 92 | } | ||
| 93 | |||
| 90 | void IREmitter::DemoteToHelperInvocation(Block* continue_label) { | 94 | void IREmitter::DemoteToHelperInvocation(Block* continue_label) { |
| 91 | block->SetBranch(continue_label); | 95 | block->SetBranch(continue_label); |
| 92 | continue_label->AddImmediatePredecessor(block); | 96 | continue_label->AddImmediatePredecessor(block); |
| @@ -126,6 +130,14 @@ void IREmitter::SetGotoVariable(u32 id, const U1& value) { | |||
| 126 | Inst(Opcode::SetGotoVariable, id, value); | 130 | Inst(Opcode::SetGotoVariable, id, value); |
| 127 | } | 131 | } |
| 128 | 132 | ||
| 133 | U32 IREmitter::GetIndirectBranchVariable() { | ||
| 134 | return Inst<U32>(Opcode::GetIndirectBranchVariable); | ||
| 135 | } | ||
| 136 | |||
| 137 | void IREmitter::SetIndirectBranchVariable(const U32& value) { | ||
| 138 | Inst(Opcode::SetIndirectBranchVariable, value); | ||
| 139 | } | ||
| 140 | |||
| 129 | void IREmitter::SetPred(IR::Pred pred, const U1& value) { | 141 | void IREmitter::SetPred(IR::Pred pred, const U1& value) { |
| 130 | Inst(Opcode::SetPred, pred, value); | 142 | Inst(Opcode::SetPred, pred, value); |
| 131 | } | 143 | } |
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h index 269f367a4..17bc32fc8 100644 --- a/src/shader_recompiler/frontend/ir/ir_emitter.h +++ b/src/shader_recompiler/frontend/ir/ir_emitter.h | |||
| @@ -37,6 +37,7 @@ public: | |||
| 37 | void LoopMerge(Block* merge_block, Block* continue_target); | 37 | void LoopMerge(Block* merge_block, Block* continue_target); |
| 38 | void SelectionMerge(Block* merge_block); | 38 | void SelectionMerge(Block* merge_block); |
| 39 | void Return(); | 39 | void Return(); |
| 40 | void Unreachable(); | ||
| 40 | void DemoteToHelperInvocation(Block* continue_label); | 41 | void DemoteToHelperInvocation(Block* continue_label); |
| 41 | 42 | ||
| 42 | void Prologue(); | 43 | void Prologue(); |
| @@ -51,6 +52,9 @@ public: | |||
| 51 | [[nodiscard]] U1 GetGotoVariable(u32 id); | 52 | [[nodiscard]] U1 GetGotoVariable(u32 id); |
| 52 | void SetGotoVariable(u32 id, const U1& value); | 53 | void SetGotoVariable(u32 id, const U1& value); |
| 53 | 54 | ||
| 55 | [[nodiscard]] U32 GetIndirectBranchVariable(); | ||
| 56 | void SetIndirectBranchVariable(const U32& value); | ||
| 57 | |||
| 54 | [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); | 58 | [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); |
| 55 | [[nodiscard]] UAny GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, | 59 | [[nodiscard]] UAny GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, |
| 56 | bool is_signed); | 60 | bool is_signed); |
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 52a5e5034..c3ba6b522 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -55,6 +55,7 @@ bool Inst::MayHaveSideEffects() const noexcept { | |||
| 55 | case Opcode::LoopMerge: | 55 | case Opcode::LoopMerge: |
| 56 | case Opcode::SelectionMerge: | 56 | case Opcode::SelectionMerge: |
| 57 | case Opcode::Return: | 57 | case Opcode::Return: |
| 58 | case Opcode::Unreachable: | ||
| 58 | case Opcode::DemoteToHelperInvocation: | 59 | case Opcode::DemoteToHelperInvocation: |
| 59 | case Opcode::Prologue: | 60 | case Opcode::Prologue: |
| 60 | case Opcode::Epilogue: | 61 | case Opcode::Epilogue: |
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc index 9b050995b..fb79e3d8d 100644 --- a/src/shader_recompiler/frontend/ir/opcodes.inc +++ b/src/shader_recompiler/frontend/ir/opcodes.inc | |||
| @@ -13,6 +13,7 @@ OPCODE(BranchConditional, Void, U1, | |||
| 13 | OPCODE(LoopMerge, Void, Label, Label, ) | 13 | OPCODE(LoopMerge, Void, Label, Label, ) |
| 14 | OPCODE(SelectionMerge, Void, Label, ) | 14 | OPCODE(SelectionMerge, Void, Label, ) |
| 15 | OPCODE(Return, Void, ) | 15 | OPCODE(Return, Void, ) |
| 16 | OPCODE(Unreachable, Void, ) | ||
| 16 | OPCODE(DemoteToHelperInvocation, Void, Label, ) | 17 | OPCODE(DemoteToHelperInvocation, Void, Label, ) |
| 17 | 18 | ||
| 18 | // Special operations | 19 | // Special operations |
| @@ -26,6 +27,8 @@ OPCODE(GetPred, U1, Pred | |||
| 26 | OPCODE(SetPred, Void, Pred, U1, ) | 27 | OPCODE(SetPred, Void, Pred, U1, ) |
| 27 | OPCODE(GetGotoVariable, U1, U32, ) | 28 | OPCODE(GetGotoVariable, U1, U32, ) |
| 28 | OPCODE(SetGotoVariable, Void, U32, U1, ) | 29 | OPCODE(SetGotoVariable, Void, U32, U1, ) |
| 30 | OPCODE(GetIndirectBranchVariable, U32, ) | ||
| 31 | OPCODE(SetIndirectBranchVariable, Void, U32, ) | ||
| 29 | OPCODE(GetCbufU8, U32, U32, U32, ) | 32 | OPCODE(GetCbufU8, U32, U32, U32, ) |
| 30 | OPCODE(GetCbufS8, U32, U32, U32, ) | 33 | OPCODE(GetCbufS8, U32, U32, U32, ) |
| 31 | OPCODE(GetCbufU16, U32, U32, U32, ) | 34 | OPCODE(GetCbufU16, U32, U32, U32, ) |
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index 4f6707fae..1e9b8e426 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "shader_recompiler/exception.h" | 14 | #include "shader_recompiler/exception.h" |
| 15 | #include "shader_recompiler/frontend/maxwell/control_flow.h" | 15 | #include "shader_recompiler/frontend/maxwell/control_flow.h" |
| 16 | #include "shader_recompiler/frontend/maxwell/decode.h" | 16 | #include "shader_recompiler/frontend/maxwell/decode.h" |
| 17 | #include "shader_recompiler/frontend/maxwell/indirect_branch_table_track.h" | ||
| 17 | #include "shader_recompiler/frontend/maxwell/location.h" | 18 | #include "shader_recompiler/frontend/maxwell/location.h" |
| 18 | 19 | ||
| 19 | namespace Shader::Maxwell::Flow { | 20 | namespace Shader::Maxwell::Flow { |
| @@ -252,9 +253,7 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 252 | const Opcode opcode{Decode(inst.raw)}; | 253 | const Opcode opcode{Decode(inst.raw)}; |
| 253 | switch (opcode) { | 254 | switch (opcode) { |
| 254 | case Opcode::BRA: | 255 | case Opcode::BRA: |
| 255 | case Opcode::BRX: | ||
| 256 | case Opcode::JMP: | 256 | case Opcode::JMP: |
| 257 | case Opcode::JMX: | ||
| 258 | case Opcode::RET: | 257 | case Opcode::RET: |
| 259 | if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) { | 258 | if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) { |
| 260 | return AnalysisState::Continue; | 259 | return AnalysisState::Continue; |
| @@ -264,10 +263,6 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 264 | case Opcode::JMP: | 263 | case Opcode::JMP: |
| 265 | AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode)); | 264 | AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode)); |
| 266 | break; | 265 | break; |
| 267 | case Opcode::BRX: | ||
| 268 | case Opcode::JMX: | ||
| 269 | AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode)); | ||
| 270 | break; | ||
| 271 | case Opcode::RET: | 266 | case Opcode::RET: |
| 272 | block->end_class = EndClass::Return; | 267 | block->end_class = EndClass::Return; |
| 273 | break; | 268 | break; |
| @@ -302,6 +297,9 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 302 | case Opcode::SSY: | 297 | case Opcode::SSY: |
| 303 | block->stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst)); | 298 | block->stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst)); |
| 304 | return AnalysisState::Continue; | 299 | return AnalysisState::Continue; |
| 300 | case Opcode::BRX: | ||
| 301 | case Opcode::JMX: | ||
| 302 | return AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode), function_id); | ||
| 305 | case Opcode::EXIT: | 303 | case Opcode::EXIT: |
| 306 | return AnalyzeEXIT(block, function_id, pc, inst); | 304 | return AnalyzeEXIT(block, function_id, pc, inst); |
| 307 | case Opcode::PRET: | 305 | case Opcode::PRET: |
| @@ -407,8 +405,46 @@ void CFG::AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruct | |||
| 407 | block->branch_true = AddLabel(block, block->stack, bra_pc, function_id); | 405 | block->branch_true = AddLabel(block, block->stack, bra_pc, function_id); |
| 408 | } | 406 | } |
| 409 | 407 | ||
| 410 | void CFG::AnalyzeBRX(Block*, Location, Instruction, bool is_absolute) { | 408 | CFG::AnalysisState CFG::AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute, |
| 411 | throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX"); | 409 | FunctionId function_id) { |
| 410 | const std::optional brx_table{TrackIndirectBranchTable(env, pc, block->begin)}; | ||
| 411 | if (!brx_table) { | ||
| 412 | TrackIndirectBranchTable(env, pc, block->begin); | ||
| 413 | throw NotImplementedException("Failed to track indirect branch"); | ||
| 414 | } | ||
| 415 | const IR::FlowTest flow_test{inst.branch.flow_test}; | ||
| 416 | const Predicate pred{inst.Pred()}; | ||
| 417 | if (flow_test != IR::FlowTest::T || pred != Predicate{true}) { | ||
| 418 | throw NotImplementedException("Conditional indirect branch"); | ||
| 419 | } | ||
| 420 | std::vector<u32> targets; | ||
| 421 | targets.reserve(brx_table->num_entries); | ||
| 422 | for (u32 i = 0; i < brx_table->num_entries; ++i) { | ||
| 423 | u32 target{env.ReadCbufValue(brx_table->cbuf_index, brx_table->cbuf_offset + i * 4)}; | ||
| 424 | if (!is_absolute) { | ||
| 425 | target += pc.Offset(); | ||
| 426 | } | ||
| 427 | target += brx_table->branch_offset; | ||
| 428 | target += 8; | ||
| 429 | targets.push_back(target); | ||
| 430 | } | ||
| 431 | std::ranges::sort(targets); | ||
| 432 | targets.erase(std::unique(targets.begin(), targets.end()), targets.end()); | ||
| 433 | |||
| 434 | block->indirect_branches.reserve(targets.size()); | ||
| 435 | for (const u32 target : targets) { | ||
| 436 | Block* const branch{AddLabel(block, block->stack, target, function_id)}; | ||
| 437 | block->indirect_branches.push_back(branch); | ||
| 438 | } | ||
| 439 | block->cond = IR::Condition{true}; | ||
| 440 | block->end = pc + 1; | ||
| 441 | block->end_class = EndClass::IndirectBranch; | ||
| 442 | block->branch_reg = brx_table->branch_reg; | ||
| 443 | block->branch_offset = brx_table->branch_offset + 8; | ||
| 444 | if (!is_absolute) { | ||
| 445 | block->branch_offset += pc.Offset(); | ||
| 446 | } | ||
| 447 | return AnalysisState::Branch; | ||
| 412 | } | 448 | } |
| 413 | 449 | ||
| 414 | CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, | 450 | CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, |
| @@ -449,7 +485,6 @@ Block* CFG::AddLabel(Block* block, Stack stack, Location pc, FunctionId function | |||
| 449 | // Block already exists and it has been visited | 485 | // Block already exists and it has been visited |
| 450 | return &*it; | 486 | return &*it; |
| 451 | } | 487 | } |
| 452 | // TODO: FIX DANGLING BLOCKS | ||
| 453 | Block* const new_block{block_pool.Create(Block{ | 488 | Block* const new_block{block_pool.Create(Block{ |
| 454 | .begin{pc}, | 489 | .begin{pc}, |
| 455 | .end{pc}, | 490 | .end{pc}, |
| @@ -494,6 +529,11 @@ std::string CFG::Dot() const { | |||
| 494 | add_branch(block.branch_false, false); | 529 | add_branch(block.branch_false, false); |
| 495 | } | 530 | } |
| 496 | break; | 531 | break; |
| 532 | case EndClass::IndirectBranch: | ||
| 533 | for (Block* const branch : block.indirect_branches) { | ||
| 534 | add_branch(branch, false); | ||
| 535 | } | ||
| 536 | break; | ||
| 497 | case EndClass::Call: | 537 | case EndClass::Call: |
| 498 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | 538 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); |
| 499 | dot += fmt::format("\t\tN{}->{};\n", node_uid, NameOf(*block.return_block)); | 539 | dot += fmt::format("\t\tN{}->{};\n", node_uid, NameOf(*block.return_block)); |
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h index 22f134194..1e05fcb97 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.h +++ b/src/shader_recompiler/frontend/maxwell/control_flow.h | |||
| @@ -26,6 +26,7 @@ using FunctionId = size_t; | |||
| 26 | 26 | ||
| 27 | enum class EndClass { | 27 | enum class EndClass { |
| 28 | Branch, | 28 | Branch, |
| 29 | IndirectBranch, | ||
| 29 | Call, | 30 | Call, |
| 30 | Exit, | 31 | Exit, |
| 31 | Return, | 32 | Return, |
| @@ -76,11 +77,14 @@ struct Block : boost::intrusive::set_base_hook< | |||
| 76 | union { | 77 | union { |
| 77 | Block* branch_true; | 78 | Block* branch_true; |
| 78 | FunctionId function_call; | 79 | FunctionId function_call; |
| 80 | IR::Reg branch_reg; | ||
| 79 | }; | 81 | }; |
| 80 | union { | 82 | union { |
| 81 | Block* branch_false; | 83 | Block* branch_false; |
| 82 | Block* return_block; | 84 | Block* return_block; |
| 85 | s32 branch_offset; | ||
| 83 | }; | 86 | }; |
| 87 | std::vector<Block*> indirect_branches; | ||
| 84 | }; | 88 | }; |
| 85 | 89 | ||
| 86 | struct Label { | 90 | struct Label { |
| @@ -139,7 +143,8 @@ private: | |||
| 139 | 143 | ||
| 140 | void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, | 144 | void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, |
| 141 | bool is_absolute); | 145 | bool is_absolute); |
| 142 | void AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute); | 146 | AnalysisState AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute, |
| 147 | FunctionId function_id); | ||
| 143 | AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); | 148 | AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); |
| 144 | 149 | ||
| 145 | /// Return the branch target block id | 150 | /// Return the branch target block id |
diff --git a/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp new file mode 100644 index 000000000..96453509d --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp | |||
| @@ -0,0 +1,108 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <optional> | ||
| 6 | |||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "shader_recompiler/exception.h" | ||
| 9 | #include "shader_recompiler/frontend/maxwell/decode.h" | ||
| 10 | #include "shader_recompiler/frontend/maxwell/indirect_branch_table_track.h" | ||
| 11 | #include "shader_recompiler/frontend/maxwell/opcodes.h" | ||
| 12 | #include "shader_recompiler/frontend/maxwell/translate/impl/load_constant.h" | ||
| 13 | |||
| 14 | namespace Shader::Maxwell { | ||
| 15 | namespace { | ||
| 16 | union Encoding { | ||
| 17 | u64 raw; | ||
| 18 | BitField<0, 8, IR::Reg> dest_reg; | ||
| 19 | BitField<8, 8, IR::Reg> src_reg; | ||
| 20 | BitField<20, 19, u64> immediate; | ||
| 21 | BitField<56, 1, u64> is_negative; | ||
| 22 | BitField<20, 24, s64> brx_offset; | ||
| 23 | }; | ||
| 24 | |||
| 25 | template <typename Callable> | ||
| 26 | std::optional<u64> Track(Environment& env, Location block_begin, Location& pos, Callable&& func) { | ||
| 27 | while (pos >= block_begin) { | ||
| 28 | const u64 insn{env.ReadInstruction(pos.Offset())}; | ||
| 29 | --pos; | ||
| 30 | if (func(insn, Decode(insn))) { | ||
| 31 | return insn; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | return std::nullopt; | ||
| 35 | } | ||
| 36 | |||
| 37 | std::optional<u64> TrackLDC(Environment& env, Location block_begin, Location& pos, | ||
| 38 | IR::Reg brx_reg) { | ||
| 39 | return Track(env, block_begin, pos, [brx_reg](u64 insn, Opcode opcode) { | ||
| 40 | const LDC::Encoding ldc{insn}; | ||
| 41 | return opcode == Opcode::LDC && ldc.dest_reg == brx_reg && ldc.size == LDC::Size::B32 && | ||
| 42 | ldc.mode == LDC::Mode::Default; | ||
| 43 | }); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::optional<u64> TrackSHL(Environment& env, Location block_begin, Location& pos, | ||
| 47 | IR::Reg ldc_reg) { | ||
| 48 | return Track(env, block_begin, pos, [ldc_reg](u64 insn, Opcode opcode) { | ||
| 49 | const Encoding shl{insn}; | ||
| 50 | return opcode == Opcode::SHL_imm && shl.dest_reg == ldc_reg; | ||
| 51 | }); | ||
| 52 | } | ||
| 53 | |||
| 54 | std::optional<u64> TrackIMNMX(Environment& env, Location block_begin, Location& pos, | ||
| 55 | IR::Reg shl_reg) { | ||
| 56 | return Track(env, block_begin, pos, [shl_reg](u64 insn, Opcode opcode) { | ||
| 57 | const Encoding imnmx{insn}; | ||
| 58 | return opcode == Opcode::IMNMX_imm && imnmx.dest_reg == shl_reg; | ||
| 59 | }); | ||
| 60 | } | ||
| 61 | } // Anonymous namespace | ||
| 62 | |||
| 63 | std::optional<IndirectBranchTableInfo> TrackIndirectBranchTable(Environment& env, Location brx_pos, | ||
| 64 | Location block_begin) { | ||
| 65 | const u64 brx_insn{env.ReadInstruction(brx_pos.Offset())}; | ||
| 66 | const Opcode brx_opcode{Decode(brx_insn)}; | ||
| 67 | if (brx_opcode != Opcode::BRX && brx_opcode != Opcode::JMX) { | ||
| 68 | throw LogicError("Tracked instruction is not BRX or JMX"); | ||
| 69 | } | ||
| 70 | const IR::Reg brx_reg{Encoding{brx_insn}.src_reg}; | ||
| 71 | const s32 brx_offset{static_cast<s32>(Encoding{brx_insn}.brx_offset)}; | ||
| 72 | |||
| 73 | Location pos{brx_pos}; | ||
| 74 | const std::optional<u64> ldc_insn{TrackLDC(env, block_begin, pos, brx_reg)}; | ||
| 75 | if (!ldc_insn) { | ||
| 76 | return std::nullopt; | ||
| 77 | } | ||
| 78 | const LDC::Encoding ldc{*ldc_insn}; | ||
| 79 | const u32 cbuf_index{static_cast<u32>(ldc.index)}; | ||
| 80 | const u32 cbuf_offset{static_cast<u32>(static_cast<s32>(ldc.offset.Value()))}; | ||
| 81 | const IR::Reg ldc_reg{ldc.src_reg}; | ||
| 82 | |||
| 83 | const std::optional<u64> shl_insn{TrackSHL(env, block_begin, pos, ldc_reg)}; | ||
| 84 | if (!shl_insn) { | ||
| 85 | return std::nullopt; | ||
| 86 | } | ||
| 87 | const Encoding shl{*shl_insn}; | ||
| 88 | const IR::Reg shl_reg{shl.src_reg}; | ||
| 89 | |||
| 90 | const std::optional<u64> imnmx_insn{TrackIMNMX(env, block_begin, pos, shl_reg)}; | ||
| 91 | if (!imnmx_insn) { | ||
| 92 | return std::nullopt; | ||
| 93 | } | ||
| 94 | const Encoding imnmx{*imnmx_insn}; | ||
| 95 | if (imnmx.is_negative != 0) { | ||
| 96 | return std::nullopt; | ||
| 97 | } | ||
| 98 | const u32 imnmx_immediate{static_cast<u32>(imnmx.immediate.Value())}; | ||
| 99 | return IndirectBranchTableInfo{ | ||
| 100 | .cbuf_index{cbuf_index}, | ||
| 101 | .cbuf_offset{cbuf_offset}, | ||
| 102 | .num_entries{imnmx_immediate + 1}, | ||
| 103 | .branch_offset{brx_offset}, | ||
| 104 | .branch_reg{brx_reg}, | ||
| 105 | }; | ||
| 106 | } | ||
| 107 | |||
| 108 | } // namespace Shader::Maxwell | ||
diff --git a/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h new file mode 100644 index 000000000..eee5102fa --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <optional> | ||
| 8 | |||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "shader_recompiler/environment.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 13 | #include "shader_recompiler/frontend/maxwell/location.h" | ||
| 14 | |||
| 15 | namespace Shader::Maxwell { | ||
| 16 | |||
| 17 | struct IndirectBranchTableInfo { | ||
| 18 | u32 cbuf_index{}; | ||
| 19 | u32 cbuf_offset{}; | ||
| 20 | u32 num_entries{}; | ||
| 21 | s32 branch_offset{}; | ||
| 22 | IR::Reg branch_reg{}; | ||
| 23 | }; | ||
| 24 | |||
| 25 | std::optional<IndirectBranchTableInfo> TrackIndirectBranchTable(Environment& env, Location brx_pos, | ||
| 26 | Location block_begin); | ||
| 27 | |||
| 28 | } // namespace Shader::Maxwell | ||
diff --git a/src/shader_recompiler/frontend/maxwell/instruction.h b/src/shader_recompiler/frontend/maxwell/instruction.h index 57fd531f2..743d68d61 100644 --- a/src/shader_recompiler/frontend/maxwell/instruction.h +++ b/src/shader_recompiler/frontend/maxwell/instruction.h | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/bit_field.h" | 7 | #include "common/bit_field.h" |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "shader_recompiler/frontend/ir/flow_test.h" | 9 | #include "shader_recompiler/frontend/ir/flow_test.h" |
| 10 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 10 | 11 | ||
| 11 | namespace Shader::Maxwell { | 12 | namespace Shader::Maxwell { |
| 12 | 13 | ||
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index 9d4688390..a6e55f61e 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "shader_recompiler/environment.h" | 17 | #include "shader_recompiler/environment.h" |
| 18 | #include "shader_recompiler/frontend/ir/basic_block.h" | 18 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 20 | #include "shader_recompiler/frontend/maxwell/decode.h" | ||
| 20 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" | 21 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" |
| 21 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | 22 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" |
| 22 | #include "shader_recompiler/object_pool.h" | 23 | #include "shader_recompiler/object_pool.h" |
| @@ -46,12 +47,15 @@ enum class StatementType { | |||
| 46 | Break, | 47 | Break, |
| 47 | Return, | 48 | Return, |
| 48 | Kill, | 49 | Kill, |
| 50 | Unreachable, | ||
| 49 | Function, | 51 | Function, |
| 50 | Identity, | 52 | Identity, |
| 51 | Not, | 53 | Not, |
| 52 | Or, | 54 | Or, |
| 53 | SetVariable, | 55 | SetVariable, |
| 56 | SetIndirectBranchVariable, | ||
| 54 | Variable, | 57 | Variable, |
| 58 | IndirectBranchCond, | ||
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | bool HasChildren(StatementType type) { | 61 | bool HasChildren(StatementType type) { |
| @@ -72,12 +76,15 @@ struct Loop {}; | |||
| 72 | struct Break {}; | 76 | struct Break {}; |
| 73 | struct Return {}; | 77 | struct Return {}; |
| 74 | struct Kill {}; | 78 | struct Kill {}; |
| 79 | struct Unreachable {}; | ||
| 75 | struct FunctionTag {}; | 80 | struct FunctionTag {}; |
| 76 | struct Identity {}; | 81 | struct Identity {}; |
| 77 | struct Not {}; | 82 | struct Not {}; |
| 78 | struct Or {}; | 83 | struct Or {}; |
| 79 | struct SetVariable {}; | 84 | struct SetVariable {}; |
| 85 | struct SetIndirectBranchVariable {}; | ||
| 80 | struct Variable {}; | 86 | struct Variable {}; |
| 87 | struct IndirectBranchCond {}; | ||
| 81 | 88 | ||
| 82 | #ifdef _MSC_VER | 89 | #ifdef _MSC_VER |
| 83 | #pragma warning(push) | 90 | #pragma warning(push) |
| @@ -96,6 +103,7 @@ struct Statement : ListBaseHook { | |||
| 96 | : cond{cond_}, up{up_}, type{StatementType::Break} {} | 103 | : cond{cond_}, up{up_}, type{StatementType::Break} {} |
| 97 | Statement(Return) : type{StatementType::Return} {} | 104 | Statement(Return) : type{StatementType::Return} {} |
| 98 | Statement(Kill) : type{StatementType::Kill} {} | 105 | Statement(Kill) : type{StatementType::Kill} {} |
| 106 | Statement(Unreachable) : type{StatementType::Unreachable} {} | ||
| 99 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} | 107 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} |
| 100 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} | 108 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} |
| 101 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} | 109 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} |
| @@ -103,7 +111,12 @@ struct Statement : ListBaseHook { | |||
| 103 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} | 111 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} |
| 104 | Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) | 112 | Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) |
| 105 | : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} | 113 | : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} |
| 114 | Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_) | ||
| 115 | : branch_offset{branch_offset_}, | ||
| 116 | branch_reg{branch_reg_}, type{StatementType::SetIndirectBranchVariable} {} | ||
| 106 | Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {} | 117 | Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {} |
| 118 | Statement(IndirectBranchCond, u32 location_) | ||
| 119 | : location{location_}, type{StatementType::IndirectBranchCond} {} | ||
| 107 | 120 | ||
| 108 | ~Statement() { | 121 | ~Statement() { |
| 109 | if (HasChildren(type)) { | 122 | if (HasChildren(type)) { |
| @@ -118,11 +131,14 @@ struct Statement : ListBaseHook { | |||
| 118 | IR::Condition guest_cond; | 131 | IR::Condition guest_cond; |
| 119 | Statement* op; | 132 | Statement* op; |
| 120 | Statement* op_a; | 133 | Statement* op_a; |
| 134 | u32 location; | ||
| 135 | s32 branch_offset; | ||
| 121 | }; | 136 | }; |
| 122 | union { | 137 | union { |
| 123 | Statement* cond; | 138 | Statement* cond; |
| 124 | Statement* op_b; | 139 | Statement* op_b; |
| 125 | u32 id; | 140 | u32 id; |
| 141 | IR::Reg branch_reg; | ||
| 126 | }; | 142 | }; |
| 127 | Statement* up{}; | 143 | Statement* up{}; |
| 128 | StatementType type; | 144 | StatementType type; |
| @@ -141,6 +157,8 @@ std::string DumpExpr(const Statement* stmt) { | |||
| 141 | return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b)); | 157 | return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b)); |
| 142 | case StatementType::Variable: | 158 | case StatementType::Variable: |
| 143 | return fmt::format("goto_L{}", stmt->id); | 159 | return fmt::format("goto_L{}", stmt->id); |
| 160 | case StatementType::IndirectBranchCond: | ||
| 161 | return fmt::format("(indirect_branch == {:x})", stmt->location); | ||
| 144 | default: | 162 | default: |
| 145 | return "<invalid type>"; | 163 | return "<invalid type>"; |
| 146 | } | 164 | } |
| @@ -182,14 +200,22 @@ std::string DumpTree(const Tree& tree, u32 indentation = 0) { | |||
| 182 | case StatementType::Kill: | 200 | case StatementType::Kill: |
| 183 | ret += fmt::format("{} kill;\n", indent); | 201 | ret += fmt::format("{} kill;\n", indent); |
| 184 | break; | 202 | break; |
| 203 | case StatementType::Unreachable: | ||
| 204 | ret += fmt::format("{} unreachable;\n", indent); | ||
| 205 | break; | ||
| 185 | case StatementType::SetVariable: | 206 | case StatementType::SetVariable: |
| 186 | ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); | 207 | ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); |
| 187 | break; | 208 | break; |
| 209 | case StatementType::SetIndirectBranchVariable: | ||
| 210 | ret += fmt::format("{} indirect_branch = {} + {};\n", indent, stmt->branch_reg, | ||
| 211 | stmt->branch_offset); | ||
| 212 | break; | ||
| 188 | case StatementType::Function: | 213 | case StatementType::Function: |
| 189 | case StatementType::Identity: | 214 | case StatementType::Identity: |
| 190 | case StatementType::Not: | 215 | case StatementType::Not: |
| 191 | case StatementType::Or: | 216 | case StatementType::Or: |
| 192 | case StatementType::Variable: | 217 | case StatementType::Variable: |
| 218 | case StatementType::IndirectBranchCond: | ||
| 193 | throw LogicError("Statement can't be printed"); | 219 | throw LogicError("Statement can't be printed"); |
| 194 | } | 220 | } |
| 195 | } | 221 | } |
| @@ -417,6 +443,17 @@ private: | |||
| 417 | } | 443 | } |
| 418 | break; | 444 | break; |
| 419 | } | 445 | } |
| 446 | case Flow::EndClass::IndirectBranch: | ||
| 447 | root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg, | ||
| 448 | block.branch_offset)); | ||
| 449 | for (Flow::Block* const branch : block.indirect_branches) { | ||
| 450 | const Node indirect_label{local_labels.at(branch)}; | ||
| 451 | Statement* cond{pool.Create(IndirectBranchCond{}, branch->begin.Offset())}; | ||
| 452 | Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)}; | ||
| 453 | gotos.push_back(root.insert(ip, *goto_stmt)); | ||
| 454 | } | ||
| 455 | root.insert(ip, *pool.Create(Unreachable{})); | ||
| 456 | break; | ||
| 420 | case Flow::EndClass::Call: { | 457 | case Flow::EndClass::Call: { |
| 421 | Flow::Function& call{cfg.Functions()[block.function_call]}; | 458 | Flow::Function& call{cfg.Functions()[block.function_call]}; |
| 422 | const Node call_return_label{local_labels.at(block.return_block)}; | 459 | const Node call_return_label{local_labels.at(block.return_block)}; |
| @@ -623,6 +660,8 @@ IR::Block* TryFindForwardBlock(const Statement& stmt) { | |||
| 623 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); | 660 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); |
| 624 | case StatementType::Variable: | 661 | case StatementType::Variable: |
| 625 | return ir.GetGotoVariable(stmt.id); | 662 | return ir.GetGotoVariable(stmt.id); |
| 663 | case StatementType::IndirectBranchCond: | ||
| 664 | return ir.IEqual(ir.GetIndirectBranchVariable(), ir.Imm32(stmt.location)); | ||
| 626 | default: | 665 | default: |
| 627 | throw NotImplementedException("Statement type {}", stmt.type); | 666 | throw NotImplementedException("Statement type {}", stmt.type); |
| 628 | } | 667 | } |
| @@ -670,6 +709,15 @@ private: | |||
| 670 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); | 709 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); |
| 671 | break; | 710 | break; |
| 672 | } | 711 | } |
| 712 | case StatementType::SetIndirectBranchVariable: { | ||
| 713 | if (!current_block) { | ||
| 714 | current_block = MergeBlock(parent, stmt); | ||
| 715 | } | ||
| 716 | IR::IREmitter ir{*current_block}; | ||
| 717 | IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))}; | ||
| 718 | ir.SetIndirectBranchVariable(address); | ||
| 719 | break; | ||
| 720 | } | ||
| 673 | case StatementType::If: { | 721 | case StatementType::If: { |
| 674 | if (!current_block) { | 722 | if (!current_block) { |
| 675 | current_block = block_pool.Create(inst_pool); | 723 | current_block = block_pool.Create(inst_pool); |
| @@ -756,6 +804,15 @@ private: | |||
| 756 | current_block = demote_block; | 804 | current_block = demote_block; |
| 757 | break; | 805 | break; |
| 758 | } | 806 | } |
| 807 | case StatementType::Unreachable: { | ||
| 808 | if (!current_block) { | ||
| 809 | current_block = block_pool.Create(inst_pool); | ||
| 810 | block_list.push_back(current_block); | ||
| 811 | } | ||
| 812 | IR::IREmitter{*current_block}.Unreachable(); | ||
| 813 | current_block = nullptr; | ||
| 814 | break; | ||
| 815 | } | ||
| 759 | default: | 816 | default: |
| 760 | throw NotImplementedException("Statement type {}", stmt.type); | 817 | throw NotImplementedException("Statement type {}", stmt.type); |
| 761 | } | 818 | } |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp new file mode 100644 index 000000000..371c0e0f7 --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/bit_field.h" | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "shader_recompiler/exception.h" | ||
| 8 | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | ||
| 9 | |||
| 10 | namespace Shader::Maxwell { | ||
| 11 | namespace { | ||
| 12 | void Check(u64 insn) { | ||
| 13 | union { | ||
| 14 | u64 raw; | ||
| 15 | BitField<5, 1, u64> cbuf_mode; | ||
| 16 | BitField<6, 1, u64> lmt; | ||
| 17 | } const encoding{insn}; | ||
| 18 | |||
| 19 | if (encoding.cbuf_mode != 0) { | ||
| 20 | throw NotImplementedException("Constant buffer mode"); | ||
| 21 | } | ||
| 22 | if (encoding.lmt != 0) { | ||
| 23 | throw NotImplementedException("LMT"); | ||
| 24 | } | ||
| 25 | } | ||
| 26 | } // Anonymous namespace | ||
| 27 | |||
| 28 | void TranslatorVisitor::BRX(u64 insn) { | ||
| 29 | Check(insn); | ||
| 30 | } | ||
| 31 | |||
| 32 | void TranslatorVisitor::JMX(u64 insn) { | ||
| 33 | Check(insn); | ||
| 34 | } | ||
| 35 | |||
| 36 | } // namespace Shader::Maxwell | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp index 39becf93c..49ccb7d62 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp | |||
| @@ -5,25 +5,11 @@ | |||
| 5 | #include "common/bit_field.h" | 5 | #include "common/bit_field.h" |
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" | 7 | #include "shader_recompiler/frontend/maxwell/translate/impl/impl.h" |
| 8 | #include "shader_recompiler/frontend/maxwell/translate/impl/load_constant.h" | ||
| 8 | 9 | ||
| 9 | namespace Shader::Maxwell { | 10 | namespace Shader::Maxwell { |
| 11 | using namespace LDC; | ||
| 10 | namespace { | 12 | namespace { |
| 11 | enum class Mode : u64 { | ||
| 12 | Default, | ||
| 13 | IL, | ||
| 14 | IS, | ||
| 15 | ISL, | ||
| 16 | }; | ||
| 17 | |||
| 18 | enum class Size : u64 { | ||
| 19 | U8, | ||
| 20 | S8, | ||
| 21 | U16, | ||
| 22 | S16, | ||
| 23 | B32, | ||
| 24 | B64, | ||
| 25 | }; | ||
| 26 | |||
| 27 | std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, | 13 | std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, |
| 28 | const IR::U32& reg, const IR::U32& imm) { | 14 | const IR::U32& reg, const IR::U32& imm) { |
| 29 | switch (mode) { | 15 | switch (mode) { |
| @@ -37,16 +23,7 @@ std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& im | |||
| 37 | } // Anonymous namespace | 23 | } // Anonymous namespace |
| 38 | 24 | ||
| 39 | void TranslatorVisitor::LDC(u64 insn) { | 25 | void TranslatorVisitor::LDC(u64 insn) { |
| 40 | union { | 26 | const Encoding ldc{insn}; |
| 41 | u64 raw; | ||
| 42 | BitField<0, 8, IR::Reg> dest_reg; | ||
| 43 | BitField<8, 8, IR::Reg> src_reg; | ||
| 44 | BitField<20, 16, s64> offset; | ||
| 45 | BitField<36, 5, u64> index; | ||
| 46 | BitField<44, 2, Mode> mode; | ||
| 47 | BitField<48, 3, Size> size; | ||
| 48 | } const ldc{insn}; | ||
| 49 | |||
| 50 | const IR::U32 imm_index{ir.Imm32(static_cast<u32>(ldc.index))}; | 27 | const IR::U32 imm_index{ir.Imm32(static_cast<u32>(ldc.index))}; |
| 51 | const IR::U32 reg{X(ldc.src_reg)}; | 28 | const IR::U32 reg{X(ldc.src_reg)}; |
| 52 | const IR::U32 imm{ir.Imm32(static_cast<s32>(ldc.offset))}; | 29 | const IR::U32 imm{ir.Imm32(static_cast<s32>(ldc.offset))}; |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h new file mode 100644 index 000000000..3074ea0e3 --- /dev/null +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 10 | |||
| 11 | namespace Shader::Maxwell::LDC { | ||
| 12 | |||
| 13 | enum class Mode : u64 { | ||
| 14 | Default, | ||
| 15 | IL, | ||
| 16 | IS, | ||
| 17 | ISL, | ||
| 18 | }; | ||
| 19 | |||
| 20 | enum class Size : u64 { | ||
| 21 | U8, | ||
| 22 | S8, | ||
| 23 | U16, | ||
| 24 | S16, | ||
| 25 | B32, | ||
| 26 | B64, | ||
| 27 | }; | ||
| 28 | |||
| 29 | union Encoding { | ||
| 30 | u64 raw; | ||
| 31 | BitField<0, 8, IR::Reg> dest_reg; | ||
| 32 | BitField<8, 8, IR::Reg> src_reg; | ||
| 33 | BitField<20, 16, s64> offset; | ||
| 34 | BitField<36, 5, u64> index; | ||
| 35 | BitField<44, 2, Mode> mode; | ||
| 36 | BitField<48, 3, Size> size; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // namespace Shader::Maxwell::LDC | ||
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp index b62d8ee2a..a0057a473 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp | |||
| @@ -53,10 +53,6 @@ void TranslatorVisitor::BRK(u64) { | |||
| 53 | ThrowNotImplemented(Opcode::BRK); | 53 | ThrowNotImplemented(Opcode::BRK); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | void TranslatorVisitor::BRX(u64) { | ||
| 57 | ThrowNotImplemented(Opcode::BRX); | ||
| 58 | } | ||
| 59 | |||
| 60 | void TranslatorVisitor::CAL() { | 56 | void TranslatorVisitor::CAL() { |
| 61 | // CAL is a no-op | 57 | // CAL is a no-op |
| 62 | } | 58 | } |
| @@ -181,10 +177,6 @@ void TranslatorVisitor::JMP(u64) { | |||
| 181 | ThrowNotImplemented(Opcode::JMP); | 177 | ThrowNotImplemented(Opcode::JMP); |
| 182 | } | 178 | } |
| 183 | 179 | ||
| 184 | void TranslatorVisitor::JMX(u64) { | ||
| 185 | ThrowNotImplemented(Opcode::JMX); | ||
| 186 | } | ||
| 187 | |||
| 188 | void TranslatorVisitor::KIL() { | 180 | void TranslatorVisitor::KIL() { |
| 189 | // KIL is a no-op | 181 | // KIL is a no-op |
| 190 | } | 182 | } |