summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/CMakeLists.txt4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp10
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp4
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp12
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h4
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc3
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp58
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h7
-rw-r--r--src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.cpp108
-rw-r--r--src/shader_recompiler/frontend/maxwell/indirect_branch_table_track.h28
-rw-r--r--src/shader_recompiler/frontend/maxwell/instruction.h1
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp57
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/branch_indirect.cpp36
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.cpp29
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_constant.h39
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp8
-rw-r--r--src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp21
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp50
21 files changed, 437 insertions, 48 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 003cbefb1..44ab929b7 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -52,6 +52,8 @@ add_library(shader_recompiler STATIC
52 frontend/maxwell/control_flow.h 52 frontend/maxwell/control_flow.h
53 frontend/maxwell/decode.cpp 53 frontend/maxwell/decode.cpp
54 frontend/maxwell/decode.h 54 frontend/maxwell/decode.h
55 frontend/maxwell/indirect_branch_table_track.cpp
56 frontend/maxwell/indirect_branch_table_track.h
55 frontend/maxwell/instruction.h 57 frontend/maxwell/instruction.h
56 frontend/maxwell/location.h 58 frontend/maxwell/location.h
57 frontend/maxwell/maxwell.inc 59 frontend/maxwell/maxwell.inc
@@ -63,6 +65,7 @@ add_library(shader_recompiler STATIC
63 frontend/maxwell/structured_control_flow.h 65 frontend/maxwell/structured_control_flow.h
64 frontend/maxwell/translate/impl/bitfield_extract.cpp 66 frontend/maxwell/translate/impl/bitfield_extract.cpp
65 frontend/maxwell/translate/impl/bitfield_insert.cpp 67 frontend/maxwell/translate/impl/bitfield_insert.cpp
68 frontend/maxwell/translate/impl/branch_indirect.cpp
66 frontend/maxwell/translate/impl/common_encoding.h 69 frontend/maxwell/translate/impl/common_encoding.h
67 frontend/maxwell/translate/impl/common_funcs.cpp 70 frontend/maxwell/translate/impl/common_funcs.cpp
68 frontend/maxwell/translate/impl/common_funcs.h 71 frontend/maxwell/translate/impl/common_funcs.h
@@ -110,6 +113,7 @@ add_library(shader_recompiler STATIC
110 frontend/maxwell/translate/impl/integer_short_multiply_add.cpp 113 frontend/maxwell/translate/impl/integer_short_multiply_add.cpp
111 frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp 114 frontend/maxwell/translate/impl/integer_to_integer_conversion.cpp
112 frontend/maxwell/translate/impl/load_constant.cpp 115 frontend/maxwell/translate/impl/load_constant.cpp
116 frontend/maxwell/translate/impl/load_constant.h
113 frontend/maxwell/translate/impl/load_effective_address.cpp 117 frontend/maxwell/translate/impl/load_effective_address.cpp
114 frontend/maxwell/translate/impl/load_store_attribute.cpp 118 frontend/maxwell/translate/impl/load_store_attribute.cpp
115 frontend/maxwell/translate/impl/load_store_local_shared.cpp 119 frontend/maxwell/translate/impl/load_store_local_shared.cpp
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 204c5f9e0..02648d769 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -26,6 +26,7 @@ void EmitBranchConditional(EmitContext& ctx, Id condition, Id true_label, Id fal
26void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label); 26void EmitLoopMerge(EmitContext& ctx, Id merge_label, Id continue_label);
27void EmitSelectionMerge(EmitContext& ctx, Id merge_label); 27void EmitSelectionMerge(EmitContext& ctx, Id merge_label);
28void EmitReturn(EmitContext& ctx); 28void EmitReturn(EmitContext& ctx);
29void EmitUnreachable(EmitContext& ctx);
29void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label); 30void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label);
30void EmitPrologue(EmitContext& ctx); 31void EmitPrologue(EmitContext& ctx);
31void EmitEpilogue(EmitContext& ctx); 32void EmitEpilogue(EmitContext& ctx);
@@ -35,6 +36,8 @@ void EmitGetPred(EmitContext& ctx);
35void EmitSetPred(EmitContext& ctx); 36void EmitSetPred(EmitContext& ctx);
36void EmitSetGotoVariable(EmitContext& ctx); 37void EmitSetGotoVariable(EmitContext& ctx);
37void EmitGetGotoVariable(EmitContext& ctx); 38void EmitGetGotoVariable(EmitContext& ctx);
39void EmitSetIndirectBranchVariable(EmitContext& ctx);
40void EmitGetIndirectBranchVariable(EmitContext& ctx);
38Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 41Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
39Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 42Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
40Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); 43Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 52dcef8a4..4a267b16c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -6,8 +6,6 @@
6 6
7#include "shader_recompiler/backend/spirv/emit_spirv.h" 7#include "shader_recompiler/backend/spirv/emit_spirv.h"
8 8
9#pragma optimize("", off)
10
11namespace Shader::Backend::SPIRV { 9namespace Shader::Backend::SPIRV {
12namespace { 10namespace {
13struct AttrInfo { 11struct AttrInfo {
@@ -74,6 +72,14 @@ void EmitGetGotoVariable(EmitContext&) {
74 throw NotImplementedException("SPIR-V Instruction"); 72 throw NotImplementedException("SPIR-V Instruction");
75} 73}
76 74
75void EmitSetIndirectBranchVariable(EmitContext&) {
76 throw NotImplementedException("SPIR-V Instruction");
77}
78
79void EmitGetIndirectBranchVariable(EmitContext&) {
80 throw NotImplementedException("SPIR-V Instruction");
81}
82
77static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr, 83static Id GetCbuf(EmitContext& ctx, Id result_type, Id UniformDefinitions::*member_ptr,
78 u32 element_size, const IR::Value& binding, const IR::Value& offset) { 84 u32 element_size, const IR::Value& binding, const IR::Value& offset) {
79 if (!binding.IsImmediate()) { 85 if (!binding.IsImmediate()) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index 6b81f0169..335603f88 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -26,6 +26,10 @@ void EmitReturn(EmitContext& ctx) {
26 ctx.OpReturn(); 26 ctx.OpReturn();
27} 27}
28 28
29void EmitUnreachable(EmitContext& ctx) {
30 ctx.OpUnreachable();
31}
32
29void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label) { 33void EmitDemoteToHelperInvocation(EmitContext& ctx, Id continue_label) {
30 ctx.OpDemoteToHelperInvocationEXT(); 34 ctx.OpDemoteToHelperInvocationEXT();
31 ctx.OpBranch(continue_label); 35 ctx.OpBranch(continue_label);
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 9415d02f6..1c50ae51e 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -15,6 +15,8 @@ public:
15 15
16 [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0; 16 [[nodiscard]] virtual u64 ReadInstruction(u32 address) = 0;
17 17
18 [[nodiscard]] virtual u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) = 0;
19
18 [[nodiscard]] virtual TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) = 0; 20 [[nodiscard]] virtual TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) = 0;
19 21
20 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; 22 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
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
90void IREmitter::Unreachable() {
91 Inst(Opcode::Unreachable);
92}
93
90void IREmitter::DemoteToHelperInvocation(Block* continue_label) { 94void 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
133U32 IREmitter::GetIndirectBranchVariable() {
134 return Inst<U32>(Opcode::GetIndirectBranchVariable);
135}
136
137void IREmitter::SetIndirectBranchVariable(const U32& value) {
138 Inst(Opcode::SetIndirectBranchVariable, value);
139}
140
129void IREmitter::SetPred(IR::Pred pred, const U1& value) { 141void 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,
13OPCODE(LoopMerge, Void, Label, Label, ) 13OPCODE(LoopMerge, Void, Label, Label, )
14OPCODE(SelectionMerge, Void, Label, ) 14OPCODE(SelectionMerge, Void, Label, )
15OPCODE(Return, Void, ) 15OPCODE(Return, Void, )
16OPCODE(Unreachable, Void, )
16OPCODE(DemoteToHelperInvocation, Void, Label, ) 17OPCODE(DemoteToHelperInvocation, Void, Label, )
17 18
18// Special operations 19// Special operations
@@ -26,6 +27,8 @@ OPCODE(GetPred, U1, Pred
26OPCODE(SetPred, Void, Pred, U1, ) 27OPCODE(SetPred, Void, Pred, U1, )
27OPCODE(GetGotoVariable, U1, U32, ) 28OPCODE(GetGotoVariable, U1, U32, )
28OPCODE(SetGotoVariable, Void, U32, U1, ) 29OPCODE(SetGotoVariable, Void, U32, U1, )
30OPCODE(GetIndirectBranchVariable, U32, )
31OPCODE(SetIndirectBranchVariable, Void, U32, )
29OPCODE(GetCbufU8, U32, U32, U32, ) 32OPCODE(GetCbufU8, U32, U32, U32, )
30OPCODE(GetCbufS8, U32, U32, U32, ) 33OPCODE(GetCbufS8, U32, U32, U32, )
31OPCODE(GetCbufU16, U32, U32, U32, ) 34OPCODE(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
19namespace Shader::Maxwell::Flow { 20namespace 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
410void CFG::AnalyzeBRX(Block*, Location, Instruction, bool is_absolute) { 408CFG::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
414CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, 450CFG::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
27enum class EndClass { 27enum 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
86struct Label { 90struct 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
14namespace Shader::Maxwell {
15namespace {
16union 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
25template <typename Callable>
26std::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
37std::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
46std::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
54std::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
63std::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
15namespace Shader::Maxwell {
16
17struct IndirectBranchTableInfo {
18 u32 cbuf_index{};
19 u32 cbuf_offset{};
20 u32 num_entries{};
21 s32 branch_offset{};
22 IR::Reg branch_reg{};
23};
24
25std::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
11namespace Shader::Maxwell { 12namespace 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
57bool HasChildren(StatementType type) { 61bool HasChildren(StatementType type) {
@@ -72,12 +76,15 @@ struct Loop {};
72struct Break {}; 76struct Break {};
73struct Return {}; 77struct Return {};
74struct Kill {}; 78struct Kill {};
79struct Unreachable {};
75struct FunctionTag {}; 80struct FunctionTag {};
76struct Identity {}; 81struct Identity {};
77struct Not {}; 82struct Not {};
78struct Or {}; 83struct Or {};
79struct SetVariable {}; 84struct SetVariable {};
85struct SetIndirectBranchVariable {};
80struct Variable {}; 86struct Variable {};
87struct 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
10namespace Shader::Maxwell {
11namespace {
12void 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
28void TranslatorVisitor::BRX(u64 insn) {
29 Check(insn);
30}
31
32void 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
9namespace Shader::Maxwell { 10namespace Shader::Maxwell {
11using namespace LDC;
10namespace { 12namespace {
11enum class Mode : u64 {
12 Default,
13 IL,
14 IS,
15 ISL,
16};
17
18enum class Size : u64 {
19 U8,
20 S8,
21 U16,
22 S16,
23 B32,
24 B64,
25};
26
27std::pair<IR::U32, IR::U32> Slot(IR::IREmitter& ir, Mode mode, const IR::U32& imm_index, 13std::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
39void TranslatorVisitor::LDC(u64 insn) { 25void 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
11namespace Shader::Maxwell::LDC {
12
13enum class Mode : u64 {
14 Default,
15 IL,
16 IS,
17 ISL,
18};
19
20enum class Size : u64 {
21 U8,
22 S8,
23 U16,
24 S16,
25 B32,
26 B64,
27};
28
29union 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
56void TranslatorVisitor::BRX(u64) {
57 ThrowNotImplemented(Opcode::BRX);
58}
59
60void TranslatorVisitor::CAL() { 56void 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
184void TranslatorVisitor::JMX(u64) {
185 ThrowNotImplemented(Opcode::JMX);
186}
187
188void TranslatorVisitor::KIL() { 180void TranslatorVisitor::KIL() {
189 // KIL is a no-op 181 // KIL is a no-op
190} 182}
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
index bab7ca186..259233746 100644
--- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
+++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp
@@ -48,8 +48,12 @@ struct GotoVariable : FlagTag {
48 u32 index; 48 u32 index;
49}; 49};
50 50
51struct IndirectBranchVariable {
52 auto operator<=>(const IndirectBranchVariable&) const noexcept = default;
53};
54
51using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, 55using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag,
52 OverflowFlagTag, GotoVariable>; 56 OverflowFlagTag, GotoVariable, IndirectBranchVariable>;
53using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; 57using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>;
54 58
55struct DefTable { 59struct DefTable {
@@ -65,6 +69,10 @@ struct DefTable {
65 return goto_vars[goto_variable.index]; 69 return goto_vars[goto_variable.index];
66 } 70 }
67 71
72 [[nodiscard]] ValueMap& operator[](IndirectBranchVariable) {
73 return indirect_branch_var;
74 }
75
68 [[nodiscard]] ValueMap& operator[](ZeroFlagTag) noexcept { 76 [[nodiscard]] ValueMap& operator[](ZeroFlagTag) noexcept {
69 return zero_flag; 77 return zero_flag;
70 } 78 }
@@ -84,6 +92,7 @@ struct DefTable {
84 std::array<ValueMap, IR::NUM_USER_REGS> regs; 92 std::array<ValueMap, IR::NUM_USER_REGS> regs;
85 std::array<ValueMap, IR::NUM_USER_PREDS> preds; 93 std::array<ValueMap, IR::NUM_USER_PREDS> preds;
86 boost::container::flat_map<u32, ValueMap> goto_vars; 94 boost::container::flat_map<u32, ValueMap> goto_vars;
95 ValueMap indirect_branch_var;
87 ValueMap zero_flag; 96 ValueMap zero_flag;
88 ValueMap sign_flag; 97 ValueMap sign_flag;
89 ValueMap carry_flag; 98 ValueMap carry_flag;
@@ -102,6 +111,10 @@ IR::Opcode UndefOpcode(const FlagTag&) noexcept {
102 return IR::Opcode::UndefU1; 111 return IR::Opcode::UndefU1;
103} 112}
104 113
114IR::Opcode UndefOpcode(IndirectBranchVariable) noexcept {
115 return IR::Opcode::UndefU32;
116}
117
105[[nodiscard]] bool IsPhi(const IR::Inst& inst) noexcept { 118[[nodiscard]] bool IsPhi(const IR::Inst& inst) noexcept {
106 return inst.Opcode() == IR::Opcode::Phi; 119 return inst.Opcode() == IR::Opcode::Phi;
107} 120}
@@ -219,6 +232,9 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) {
219 case IR::Opcode::SetGotoVariable: 232 case IR::Opcode::SetGotoVariable:
220 pass.WriteVariable(GotoVariable{inst.Arg(0).U32()}, block, inst.Arg(1)); 233 pass.WriteVariable(GotoVariable{inst.Arg(0).U32()}, block, inst.Arg(1));
221 break; 234 break;
235 case IR::Opcode::SetIndirectBranchVariable:
236 pass.WriteVariable(IndirectBranchVariable{}, block, inst.Arg(0));
237 break;
222 case IR::Opcode::SetZFlag: 238 case IR::Opcode::SetZFlag:
223 pass.WriteVariable(ZeroFlagTag{}, block, inst.Arg(0)); 239 pass.WriteVariable(ZeroFlagTag{}, block, inst.Arg(0));
224 break; 240 break;
@@ -244,6 +260,9 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) {
244 case IR::Opcode::GetGotoVariable: 260 case IR::Opcode::GetGotoVariable:
245 inst.ReplaceUsesWith(pass.ReadVariable(GotoVariable{inst.Arg(0).U32()}, block)); 261 inst.ReplaceUsesWith(pass.ReadVariable(GotoVariable{inst.Arg(0).U32()}, block));
246 break; 262 break;
263 case IR::Opcode::GetIndirectBranchVariable:
264 inst.ReplaceUsesWith(pass.ReadVariable(IndirectBranchVariable{}, block));
265 break;
247 case IR::Opcode::GetZFlag: 266 case IR::Opcode::GetZFlag:
248 inst.ReplaceUsesWith(pass.ReadVariable(ZeroFlagTag{}, block)); 267 inst.ReplaceUsesWith(pass.ReadVariable(ZeroFlagTag{}, block));
249 break; 268 break;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 8b2816c13..6cde01491 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -47,7 +47,7 @@ auto MakeSpan(Container& container) {
47} 47}
48 48
49u64 MakeCbufKey(u32 index, u32 offset) { 49u64 MakeCbufKey(u32 index, u32 offset) {
50 return (static_cast<u64>(index) << 32) | static_cast<u64>(offset); 50 return (static_cast<u64>(index) << 32) | offset;
51} 51}
52 52
53class GenericEnvironment : public Shader::Environment { 53class GenericEnvironment : public Shader::Environment {
@@ -114,11 +114,13 @@ public:
114 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), code_size); 114 gpu_memory->ReadBlock(program_base + read_lowest, data.get(), code_size);
115 115
116 const u64 num_texture_types{static_cast<u64>(texture_types.size())}; 116 const u64 num_texture_types{static_cast<u64>(texture_types.size())};
117 const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
117 const u32 local_memory_size{LocalMemorySize()}; 118 const u32 local_memory_size{LocalMemorySize()};
118 const u32 texture_bound{TextureBoundBuffer()}; 119 const u32 texture_bound{TextureBoundBuffer()};
119 120
120 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size)) 121 file.write(reinterpret_cast<const char*>(&code_size), sizeof(code_size))
121 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) 122 .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
123 .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
122 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size)) 124 .write(reinterpret_cast<const char*>(&local_memory_size), sizeof(local_memory_size))
123 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound)) 125 .write(reinterpret_cast<const char*>(&texture_bound), sizeof(texture_bound))
124 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) 126 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
@@ -130,6 +132,10 @@ public:
130 file.write(reinterpret_cast<const char*>(&key), sizeof(key)) 132 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
131 .write(reinterpret_cast<const char*>(&type), sizeof(type)); 133 .write(reinterpret_cast<const char*>(&type), sizeof(type));
132 } 134 }
135 for (const auto [key, type] : cbuf_values) {
136 file.write(reinterpret_cast<const char*>(&key), sizeof(key))
137 .write(reinterpret_cast<const char*>(&type), sizeof(type));
138 }
133 if (stage == Shader::Stage::Compute) { 139 if (stage == Shader::Stage::Compute) {
134 const std::array<u32, 3> workgroup_size{WorkgroupSize()}; 140 const std::array<u32, 3> workgroup_size{WorkgroupSize()};
135 const u32 shared_memory_size{SharedMemorySize()}; 141 const u32 shared_memory_size{SharedMemorySize()};
@@ -212,6 +218,7 @@ protected:
212 218
213 std::vector<u64> code; 219 std::vector<u64> code;
214 std::unordered_map<u64, Shader::TextureType> texture_types; 220 std::unordered_map<u64, Shader::TextureType> texture_types;
221 std::unordered_map<u64, u32> cbuf_values;
215 222
216 u32 read_lowest = std::numeric_limits<u32>::max(); 223 u32 read_lowest = std::numeric_limits<u32>::max();
217 u32 read_highest = 0; 224 u32 read_highest = 0;
@@ -267,6 +274,17 @@ public:
267 274
268 ~GraphicsEnvironment() override = default; 275 ~GraphicsEnvironment() override = default;
269 276
277 u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override {
278 const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]};
279 ASSERT(cbuf.enabled);
280 u32 value{};
281 if (cbuf_offset < cbuf.size) {
282 value = gpu_memory->Read<u32>(cbuf.address + cbuf_offset);
283 }
284 cbuf_values.emplace(MakeCbufKey(cbuf_index, cbuf_offset), value);
285 return value;
286 }
287
270 Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { 288 Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override {
271 const auto& regs{maxwell3d->regs}; 289 const auto& regs{maxwell3d->regs};
272 const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]}; 290 const auto& cbuf{maxwell3d->state.shader_stages[stage_index].const_buffers[cbuf_index]};
@@ -312,6 +330,18 @@ public:
312 330
313 ~ComputeEnvironment() override = default; 331 ~ComputeEnvironment() override = default;
314 332
333 u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override {
334 const auto& qmd{kepler_compute->launch_description};
335 ASSERT(((qmd.const_buffer_enable_mask.Value() >> cbuf_index) & 1) != 0);
336 const auto& cbuf{qmd.const_buffer_config[cbuf_index]};
337 u32 value{};
338 if (cbuf_offset < cbuf.size) {
339 value = gpu_memory->Read<u32>(cbuf.Address() + cbuf_offset);
340 }
341 cbuf_values.emplace(MakeCbufKey(cbuf_index, cbuf_offset), value);
342 return value;
343 }
344
315 Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { 345 Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override {
316 const auto& regs{kepler_compute->regs}; 346 const auto& regs{kepler_compute->regs};
317 const auto& qmd{kepler_compute->launch_description}; 347 const auto& qmd{kepler_compute->launch_description};
@@ -386,8 +416,10 @@ public:
386 void Deserialize(std::ifstream& file) { 416 void Deserialize(std::ifstream& file) {
387 u64 code_size{}; 417 u64 code_size{};
388 u64 num_texture_types{}; 418 u64 num_texture_types{};
419 u64 num_cbuf_values{};
389 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) 420 file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
390 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) 421 .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
422 .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
391 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size)) 423 .read(reinterpret_cast<char*>(&local_memory_size), sizeof(local_memory_size))
392 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound)) 424 .read(reinterpret_cast<char*>(&texture_bound), sizeof(texture_bound))
393 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) 425 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
@@ -403,6 +435,13 @@ public:
403 .read(reinterpret_cast<char*>(&type), sizeof(type)); 435 .read(reinterpret_cast<char*>(&type), sizeof(type));
404 texture_types.emplace(key, type); 436 texture_types.emplace(key, type);
405 } 437 }
438 for (size_t i = 0; i < num_cbuf_values; ++i) {
439 u64 key;
440 u32 value;
441 file.read(reinterpret_cast<char*>(&key), sizeof(key))
442 .read(reinterpret_cast<char*>(&value), sizeof(value));
443 cbuf_values.emplace(key, value);
444 }
406 if (stage == Shader::Stage::Compute) { 445 if (stage == Shader::Stage::Compute) {
407 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size)) 446 file.read(reinterpret_cast<char*>(&workgroup_size), sizeof(workgroup_size))
408 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size)); 447 .read(reinterpret_cast<char*>(&shared_memory_size), sizeof(shared_memory_size));
@@ -418,6 +457,14 @@ public:
418 return code[(address - read_lowest) / sizeof(u64)]; 457 return code[(address - read_lowest) / sizeof(u64)];
419 } 458 }
420 459
460 u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override {
461 const auto it{cbuf_values.find(MakeCbufKey(cbuf_index, cbuf_offset))};
462 if (it == cbuf_values.end()) {
463 throw Shader::LogicError("Uncached read texture type");
464 }
465 return it->second;
466 }
467
421 Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override { 468 Shader::TextureType ReadTextureType(u32 cbuf_index, u32 cbuf_offset) override {
422 const auto it{texture_types.find(MakeCbufKey(cbuf_index, cbuf_offset))}; 469 const auto it{texture_types.find(MakeCbufKey(cbuf_index, cbuf_offset))};
423 if (it == texture_types.end()) { 470 if (it == texture_types.end()) {
@@ -445,6 +492,7 @@ public:
445private: 492private:
446 std::unique_ptr<u64[]> code; 493 std::unique_ptr<u64[]> code;
447 std::unordered_map<u64, Shader::TextureType> texture_types; 494 std::unordered_map<u64, Shader::TextureType> texture_types;
495 std::unordered_map<u64, u32> cbuf_values;
448 std::array<u32, 3> workgroup_size{}; 496 std::array<u32, 3> workgroup_size{};
449 u32 local_memory_size{}; 497 u32 local_memory_size{};
450 u32 shared_memory_size{}; 498 u32 shared_memory_size{};