summaryrefslogtreecommitdiff
path: root/src/shader_recompiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler')
-rw-r--r--src/shader_recompiler/CMakeLists.txt14
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp25
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp5
-rw-r--r--src/shader_recompiler/frontend/ir/value.h4
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp6
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp47
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp98
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp98
-rw-r--r--src/shader_recompiler/runtime_info.h2
-rw-r--r--src/shader_recompiler/shader_info.h7
14 files changed, 162 insertions, 156 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 189e5cb17..545d69c7e 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -242,24 +242,14 @@ target_link_libraries(shader_recompiler PUBLIC common fmt::fmt sirit)
242if (MSVC) 242if (MSVC)
243 target_compile_options(shader_recompiler PRIVATE 243 target_compile_options(shader_recompiler PRIVATE
244 /W4 244 /W4
245 /WX 245
246 /we4018 # 'expression' : signed/unsigned mismatch 246 /we4242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data
247 /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
248 /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
249 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 247 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
250 /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
251 /we4305 # 'context' : truncation from 'type1' to 'type2'
252 /we4800 # Implicit conversion from 'type' to bool. Possible information loss 248 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
253 /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
254 ) 249 )
255else() 250else()
256 target_compile_options(shader_recompiler PRIVATE 251 target_compile_options(shader_recompiler PRIVATE
257 -Werror
258 -Werror=conversion 252 -Werror=conversion
259 -Werror=ignored-qualifiers
260 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
261 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
262 -Werror=unused-variable
263 253
264 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6. 254 # Bracket depth determines maximum size of a fold expression in Clang since 9c9974c3ccb6.
265 # And this in turns limits the size of a std::array. 255 # And this in turns limits the size of a std::array.
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 1419207e8..3b0176bf6 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -175,7 +175,7 @@ bool IsReference(IR::Inst& inst) {
175} 175}
176 176
177void PrecolorInst(IR::Inst& phi) { 177void PrecolorInst(IR::Inst& phi) {
178 // Insert phi moves before references to avoid overwritting other phis 178 // Insert phi moves before references to avoid overwriting other phis
179 const size_t num_args{phi.NumArgs()}; 179 const size_t num_args{phi.NumArgs()};
180 for (size_t i = 0; i < num_args; ++i) { 180 for (size_t i = 0; i < num_args; ++i) {
181 IR::Block& phi_block{*phi.PhiBlock(i)}; 181 IR::Block& phi_block{*phi.PhiBlock(i)};
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index ee0b0f53d..0a7d42dda 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -13,9 +13,6 @@ namespace Shader::Backend::GLASM {
13namespace { 13namespace {
14void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset, 14void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset,
15 std::string_view size) { 15 std::string_view size) {
16 if (!binding.IsImmediate()) {
17 throw NotImplementedException("Indirect constant buffer loading");
18 }
19 const Register ret{ctx.reg_alloc.Define(inst)}; 16 const Register ret{ctx.reg_alloc.Define(inst)};
20 if (offset.type == Type::U32) { 17 if (offset.type == Type::U32) {
21 // Avoid reading arrays out of bounds, matching hardware's behavior 18 // Avoid reading arrays out of bounds, matching hardware's behavior
@@ -24,7 +21,27 @@ void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU
24 return; 21 return;
25 } 22 }
26 } 23 }
27 ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset); 24
25 if (binding.IsImmediate()) {
26 ctx.Add("LDC.{} {},c{}[{}];", size, ret, binding.U32(), offset);
27 return;
28 }
29
30 const ScalarU32 idx{ctx.reg_alloc.Consume(binding)};
31 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
32 ctx.Add("SEQ.S.CC RC.x,{},{};"
33 "IF NE.x;"
34 "LDC.{} {},c{}[{}];",
35 idx, i, size, ret, i, offset);
36
37 if (i != Info::MAX_INDIRECT_CBUFS - 1) {
38 ctx.Add("ELSE;");
39 }
40 }
41
42 for (u32 i = 0; i < Info::MAX_INDIRECT_CBUFS; i++) {
43 ctx.Add("ENDIF;");
44 }
28} 45}
29 46
30bool IsInputArray(Stage stage) { 47bool IsInputArray(Stage stage) {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index 7094d8e42..1f4ffdd62 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -5,10 +5,6 @@
5#include "shader_recompiler/backend/glasm/glasm_emit_context.h" 5#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
6#include "shader_recompiler/frontend/ir/value.h" 6#include "shader_recompiler/frontend/ir/value.h"
7 7
8#ifdef _MSC_VER
9#pragma warning(disable : 4100)
10#endif
11
12namespace Shader::Backend::GLASM { 8namespace Shader::Backend::GLASM {
13 9
14#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__) 10#define NotImplemented() throw NotImplementedException("GLASM instruction {}", __LINE__)
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index 76c18e488..e8a4390f6 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -101,7 +101,7 @@ bool IsReference(IR::Inst& inst) {
101} 101}
102 102
103void PrecolorInst(IR::Inst& phi) { 103void PrecolorInst(IR::Inst& phi) {
104 // Insert phi moves before references to avoid overwritting other phis 104 // Insert phi moves before references to avoid overwriting other phis
105 const size_t num_args{phi.NumArgs()}; 105 const size_t num_args{phi.NumArgs()};
106 for (size_t i = 0; i < num_args; ++i) { 106 for (size_t i = 0; i < num_args; ++i) {
107 IR::Block& phi_block{*phi.PhiBlock(i)}; 107 IR::Block& phi_block{*phi.PhiBlock(i)};
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
index b03a8ba1e..9f1ed95a4 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -7,10 +7,6 @@
7#include "shader_recompiler/backend/glsl/glsl_emit_context.h" 7#include "shader_recompiler/backend/glsl/glsl_emit_context.h"
8#include "shader_recompiler/frontend/ir/value.h" 8#include "shader_recompiler/frontend/ir/value.h"
9 9
10#ifdef _MSC_VER
11#pragma warning(disable : 4100)
12#endif
13
14namespace Shader::Backend::GLSL { 10namespace Shader::Backend::GLSL {
15 11
16void EmitGetRegister(EmitContext& ctx) { 12void EmitGetRegister(EmitContext& ctx) {
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
index 468782eb1..84417980b 100644
--- a/src/shader_recompiler/frontend/ir/microinstruction.cpp
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -325,11 +325,6 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
325 phi_args.emplace_back(predecessor, value); 325 phi_args.emplace_back(predecessor, value);
326} 326}
327 327
328void Inst::ErasePhiOperand(size_t index) {
329 const auto operand_it{phi_args.begin() + static_cast<ptrdiff_t>(index)};
330 phi_args.erase(operand_it);
331}
332
333void Inst::OrderPhiArgs() { 328void Inst::OrderPhiArgs() {
334 if (op != Opcode::Phi) { 329 if (op != Opcode::Phi) {
335 throw LogicError("{} is not a Phi instruction", op); 330 throw LogicError("{} is not a Phi instruction", op);
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
index 1a2e4ccb6..6a673ca05 100644
--- a/src/shader_recompiler/frontend/ir/value.h
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -178,13 +178,9 @@ public:
178 178
179 /// Get a pointer to the block of a phi argument. 179 /// Get a pointer to the block of a phi argument.
180 [[nodiscard]] Block* PhiBlock(size_t index) const; 180 [[nodiscard]] Block* PhiBlock(size_t index) const;
181
182 /// Add phi operand to a phi instruction. 181 /// Add phi operand to a phi instruction.
183 void AddPhiOperand(Block* predecessor, const Value& value); 182 void AddPhiOperand(Block* predecessor, const Value& value);
184 183
185 // Erase the phi operand at the given index.
186 void ErasePhiOperand(size_t index);
187
188 /// Orders the Phi arguments from farthest away to nearest. 184 /// Orders the Phi arguments from farthest away to nearest.
189 void OrderPhiArgs(); 185 void OrderPhiArgs();
190 186
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 578bc8c1b..ce42475d4 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -964,9 +964,9 @@ private:
964 demote_endif_node.type = Type::EndIf; 964 demote_endif_node.type = Type::EndIf;
965 demote_endif_node.data.end_if.merge = return_block_it->data.block; 965 demote_endif_node.data.end_if.merge = return_block_it->data.block;
966 966
967 asl.insert(return_block_it, demote_endif_node); 967 const auto next_it_1 = asl.insert(return_block_it, demote_endif_node);
968 asl.insert(return_block_it, demote_node); 968 const auto next_it_2 = asl.insert(next_it_1, demote_node);
969 asl.insert(return_block_it, demote_if_node); 969 asl.insert(next_it_2, demote_if_node);
970 } 970 }
971 971
972 ObjectPool<Statement>& stmt_pool; 972 ObjectPool<Statement>& stmt_pool;
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index dafb9deee..b7162f719 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -137,28 +137,35 @@ bool IsLegacyAttribute(IR::Attribute attribute) {
137} 137}
138 138
139std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings( 139std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
140 const VaryingState& state, std::queue<IR::Attribute> ununsed_generics) { 140 const VaryingState& state, std::queue<IR::Attribute> unused_generics,
141 const std::map<IR::Attribute, IR::Attribute>& previous_stage_mapping) {
141 std::map<IR::Attribute, IR::Attribute> mapping; 142 std::map<IR::Attribute, IR::Attribute> mapping;
143 auto update_mapping = [&mapping, &unused_generics, previous_stage_mapping](IR::Attribute attr,
144 size_t count) {
145 if (previous_stage_mapping.find(attr) != previous_stage_mapping.end()) {
146 for (size_t i = 0; i < count; ++i) {
147 mapping.insert({attr + i, previous_stage_mapping.at(attr + i)});
148 }
149 } else {
150 for (size_t i = 0; i < count; ++i) {
151 mapping.insert({attr + i, unused_generics.front() + i});
152 }
153 unused_generics.pop();
154 }
155 };
142 for (size_t index = 0; index < 4; ++index) { 156 for (size_t index = 0; index < 4; ++index) {
143 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4; 157 auto attr = IR::Attribute::ColorFrontDiffuseR + index * 4;
144 if (state.AnyComponent(attr)) { 158 if (state.AnyComponent(attr)) {
145 for (size_t i = 0; i < 4; ++i) { 159 update_mapping(attr, 4);
146 mapping.insert({attr + i, ununsed_generics.front() + i});
147 }
148 ununsed_generics.pop();
149 } 160 }
150 } 161 }
151 if (state[IR::Attribute::FogCoordinate]) { 162 if (state[IR::Attribute::FogCoordinate]) {
152 mapping.insert({IR::Attribute::FogCoordinate, ununsed_generics.front()}); 163 update_mapping(IR::Attribute::FogCoordinate, 1);
153 ununsed_generics.pop();
154 } 164 }
155 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) { 165 for (size_t index = 0; index < IR::NUM_FIXEDFNCTEXTURE; ++index) {
156 auto attr = IR::Attribute::FixedFncTexture0S + index * 4; 166 auto attr = IR::Attribute::FixedFncTexture0S + index * 4;
157 if (state.AnyComponent(attr)) { 167 if (state.AnyComponent(attr)) {
158 for (size_t i = 0; i < 4; ++i) { 168 update_mapping(attr, 4);
159 mapping.insert({attr + i, ununsed_generics.front() + i});
160 }
161 ununsed_generics.pop();
162 } 169 }
163 } 170 }
164 return mapping; 171 return mapping;
@@ -267,21 +274,22 @@ IR::Program MergeDualVertexPrograms(IR::Program& vertex_a, IR::Program& vertex_b
267void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { 274void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) {
268 auto& stores = program.info.stores; 275 auto& stores = program.info.stores;
269 if (stores.Legacy()) { 276 if (stores.Legacy()) {
270 std::queue<IR::Attribute> ununsed_output_generics{}; 277 std::queue<IR::Attribute> unused_output_generics{};
271 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 278 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
272 if (!stores.Generic(index)) { 279 if (!stores.Generic(index)) {
273 ununsed_output_generics.push(IR::Attribute::Generic0X + index * 4); 280 unused_output_generics.push(IR::Attribute::Generic0X + index * 4);
274 } 281 }
275 } 282 }
276 auto mappings = GenerateLegacyToGenericMappings(stores, ununsed_output_generics); 283 program.info.legacy_stores_mapping =
284 GenerateLegacyToGenericMappings(stores, unused_output_generics, {});
277 for (IR::Block* const block : program.post_order_blocks) { 285 for (IR::Block* const block : program.post_order_blocks) {
278 for (IR::Inst& inst : block->Instructions()) { 286 for (IR::Inst& inst : block->Instructions()) {
279 switch (inst.GetOpcode()) { 287 switch (inst.GetOpcode()) {
280 case IR::Opcode::SetAttribute: { 288 case IR::Opcode::SetAttribute: {
281 const auto attr = inst.Arg(0).Attribute(); 289 const auto attr = inst.Arg(0).Attribute();
282 if (IsLegacyAttribute(attr)) { 290 if (IsLegacyAttribute(attr)) {
283 stores.Set(mappings[attr], true); 291 stores.Set(program.info.legacy_stores_mapping[attr], true);
284 inst.SetArg(0, Shader::IR::Value(mappings[attr])); 292 inst.SetArg(0, Shader::IR::Value(program.info.legacy_stores_mapping[attr]));
285 } 293 }
286 break; 294 break;
287 } 295 }
@@ -294,15 +302,16 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
294 302
295 auto& loads = program.info.loads; 303 auto& loads = program.info.loads;
296 if (loads.Legacy()) { 304 if (loads.Legacy()) {
297 std::queue<IR::Attribute> ununsed_input_generics{}; 305 std::queue<IR::Attribute> unused_input_generics{};
298 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) { 306 for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
299 const AttributeType input_type{runtime_info.generic_input_types[index]}; 307 const AttributeType input_type{runtime_info.generic_input_types[index]};
300 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) || 308 if (!runtime_info.previous_stage_stores.Generic(index) || !loads.Generic(index) ||
301 input_type == AttributeType::Disabled) { 309 input_type == AttributeType::Disabled) {
302 ununsed_input_generics.push(IR::Attribute::Generic0X + index * 4); 310 unused_input_generics.push(IR::Attribute::Generic0X + index * 4);
303 } 311 }
304 } 312 }
305 auto mappings = GenerateLegacyToGenericMappings(loads, ununsed_input_generics); 313 auto mappings = GenerateLegacyToGenericMappings(
314 loads, unused_input_generics, runtime_info.previous_stage_legacy_stores_mapping);
306 for (IR::Block* const block : program.post_order_blocks) { 315 for (IR::Block* const block : program.post_order_blocks) {
307 for (IR::Inst& inst : block->Instructions()) { 316 for (IR::Inst& inst : block->Instructions()) {
308 switch (inst.GetOpcode()) { 317 switch (inst.GetOpcode()) {
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
index 9a7d47344..1bd8afd6f 100644
--- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -1,104 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <algorithm>
5
6#include <boost/container/small_vector.hpp>
7
8#include "shader_recompiler/frontend/ir/basic_block.h" 4#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/value.h" 5#include "shader_recompiler/frontend/ir/value.h"
10#include "shader_recompiler/ir_opt/passes.h" 6#include "shader_recompiler/ir_opt/passes.h"
11 7
12namespace Shader::Optimization { 8namespace Shader::Optimization {
13namespace { 9
14template <bool TEST_USES> 10void DeadCodeEliminationPass(IR::Program& program) {
15void DeadInstElimination(IR::Block* const block) {
16 // We iterate over the instructions in reverse order. 11 // We iterate over the instructions in reverse order.
17 // This is because removing an instruction reduces the number of uses for earlier instructions. 12 // This is because removing an instruction reduces the number of uses for earlier instructions.
18 auto it{block->end()}; 13 for (IR::Block* const block : program.post_order_blocks) {
19 while (it != block->begin()) { 14 auto it{block->end()};
20 --it; 15 while (it != block->begin()) {
21 if constexpr (TEST_USES) { 16 --it;
22 if (it->HasUses() || it->MayHaveSideEffects()) { 17 if (!it->HasUses() && !it->MayHaveSideEffects()) {
23 continue; 18 it->Invalidate();
24 } 19 it = block->Instructions().erase(it);
25 }
26 it->Invalidate();
27 it = block->Instructions().erase(it);
28 }
29}
30
31void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
32 for (IR::Block* const block : program.blocks) {
33 for (IR::Inst& phi : *block) {
34 if (!IR::IsPhi(phi)) {
35 continue;
36 }
37 for (size_t i = 0; i < phi.NumArgs(); ++i) {
38 if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
39 continue;
40 }
41 // Phi operand at this index is an unreachable block
42 phi.ErasePhiOperand(i);
43 --i;
44 }
45 }
46 }
47}
48
49void DeadBranchElimination(IR::Program& program) {
50 boost::container::small_vector<const IR::Block*, 3> dead_blocks;
51 const auto begin_it{program.syntax_list.begin()};
52 for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
53 if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
54 continue;
55 }
56 IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
57 const IR::U1 cond{cond_ref->Arg(0)};
58 if (!cond.IsImmediate()) {
59 continue;
60 }
61 if (cond.U1()) {
62 continue;
63 }
64 // False immediate condition. Remove condition ref, erase the entire branch.
65 cond_ref->Invalidate();
66 // Account for nested if-statements within the if(false) branch
67 u32 nested_ifs{1u};
68 while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
69 node_it = program.syntax_list.erase(node_it);
70 switch (node_it->type) {
71 case IR::AbstractSyntaxNode::Type::If:
72 ++nested_ifs;
73 break;
74 case IR::AbstractSyntaxNode::Type::EndIf:
75 --nested_ifs;
76 break;
77 case IR::AbstractSyntaxNode::Type::Block: {
78 IR::Block* const block{node_it->data.block};
79 DeadInstElimination<false>(block);
80 dead_blocks.push_back(block);
81 break;
82 }
83 default:
84 break;
85 } 20 }
86 } 21 }
87 // Erase EndIf node of the if(false) branch
88 node_it = program.syntax_list.erase(node_it);
89 // Account for loop increment
90 --node_it;
91 }
92 if (!dead_blocks.empty()) {
93 DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
94 }
95}
96} // namespace
97
98void DeadCodeEliminationPass(IR::Program& program) {
99 DeadBranchElimination(program);
100 for (IR::Block* const block : program.post_order_blocks) {
101 DeadInstElimination<true>(block);
102 } 22 }
103} 23}
104 24
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 597112ba4..e8be58357 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -19,8 +19,10 @@ namespace {
19struct ConstBufferAddr { 19struct ConstBufferAddr {
20 u32 index; 20 u32 index;
21 u32 offset; 21 u32 offset;
22 u32 shift_left;
22 u32 secondary_index; 23 u32 secondary_index;
23 u32 secondary_offset; 24 u32 secondary_offset;
25 u32 secondary_shift_left;
24 IR::U32 dynamic_offset; 26 IR::U32 dynamic_offset;
25 u32 count; 27 u32 count;
26 bool has_secondary; 28 bool has_secondary;
@@ -172,19 +174,41 @@ bool IsTextureInstruction(const IR::Inst& inst) {
172 return IndexedInstruction(inst) != IR::Opcode::Void; 174 return IndexedInstruction(inst) != IR::Opcode::Void;
173} 175}
174 176
175std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst); 177std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
176 178
177std::optional<ConstBufferAddr> Track(const IR::Value& value) { 179std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
178 return IR::BreadthFirstSearch(value, TryGetConstBuffer); 180 return IR::BreadthFirstSearch(
181 value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
179} 182}
180 183
181std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) { 184std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
185 const IR::Inst* inst = value.InstRecursive();
186 if (inst->GetOpcode() != IR::Opcode::GetCbufU32) {
187 return std::nullopt;
188 }
189 const IR::Value index{inst->Arg(0)};
190 const IR::Value offset{inst->Arg(1)};
191 if (!index.IsImmediate()) {
192 return std::nullopt;
193 }
194 if (!offset.IsImmediate()) {
195 return std::nullopt;
196 }
197 const auto index_number = index.U32();
198 if (index_number != 1) {
199 return std::nullopt;
200 }
201 const auto offset_number = offset.U32();
202 return env.ReadCbufValue(index_number, offset_number);
203}
204
205std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
182 switch (inst->GetOpcode()) { 206 switch (inst->GetOpcode()) {
183 default: 207 default:
184 return std::nullopt; 208 return std::nullopt;
185 case IR::Opcode::BitwiseOr32: { 209 case IR::Opcode::BitwiseOr32: {
186 std::optional lhs{Track(inst->Arg(0))}; 210 std::optional lhs{Track(inst->Arg(0), env)};
187 std::optional rhs{Track(inst->Arg(1))}; 211 std::optional rhs{Track(inst->Arg(1), env)};
188 if (!lhs || !rhs) { 212 if (!lhs || !rhs) {
189 return std::nullopt; 213 return std::nullopt;
190 } 214 }
@@ -194,19 +218,62 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
194 if (lhs->count > 1 || rhs->count > 1) { 218 if (lhs->count > 1 || rhs->count > 1) {
195 return std::nullopt; 219 return std::nullopt;
196 } 220 }
197 if (lhs->index > rhs->index || lhs->offset > rhs->offset) { 221 if (lhs->shift_left > 0 || lhs->index > rhs->index || lhs->offset > rhs->offset) {
198 std::swap(lhs, rhs); 222 std::swap(lhs, rhs);
199 } 223 }
200 return ConstBufferAddr{ 224 return ConstBufferAddr{
201 .index = lhs->index, 225 .index = lhs->index,
202 .offset = lhs->offset, 226 .offset = lhs->offset,
227 .shift_left = lhs->shift_left,
203 .secondary_index = rhs->index, 228 .secondary_index = rhs->index,
204 .secondary_offset = rhs->offset, 229 .secondary_offset = rhs->offset,
230 .secondary_shift_left = rhs->shift_left,
205 .dynamic_offset = {}, 231 .dynamic_offset = {},
206 .count = 1, 232 .count = 1,
207 .has_secondary = true, 233 .has_secondary = true,
208 }; 234 };
209 } 235 }
236 case IR::Opcode::ShiftLeftLogical32: {
237 const IR::Value shift{inst->Arg(1)};
238 if (!shift.IsImmediate()) {
239 return std::nullopt;
240 }
241 std::optional lhs{Track(inst->Arg(0), env)};
242 if (lhs) {
243 lhs->shift_left = shift.U32();
244 }
245 return lhs;
246 break;
247 }
248 case IR::Opcode::BitwiseAnd32: {
249 IR::Value op1{inst->Arg(0)};
250 IR::Value op2{inst->Arg(1)};
251 if (op1.IsImmediate()) {
252 std::swap(op1, op2);
253 }
254 if (!op2.IsImmediate() && !op1.IsImmediate()) {
255 do {
256 auto try_index = TryGetConstant(op1, env);
257 if (try_index) {
258 op1 = op2;
259 op2 = IR::Value{*try_index};
260 break;
261 }
262 auto try_index_2 = TryGetConstant(op2, env);
263 if (try_index_2) {
264 op2 = IR::Value{*try_index_2};
265 break;
266 }
267 return std::nullopt;
268 } while (false);
269 }
270 std::optional lhs{Track(op1, env)};
271 if (lhs) {
272 lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
273 }
274 return lhs;
275 break;
276 }
210 case IR::Opcode::GetCbufU32x2: 277 case IR::Opcode::GetCbufU32x2:
211 case IR::Opcode::GetCbufU32: 278 case IR::Opcode::GetCbufU32:
212 break; 279 break;
@@ -222,8 +289,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
222 return ConstBufferAddr{ 289 return ConstBufferAddr{
223 .index = index.U32(), 290 .index = index.U32(),
224 .offset = offset.U32(), 291 .offset = offset.U32(),
292 .shift_left = 0,
225 .secondary_index = 0, 293 .secondary_index = 0,
226 .secondary_offset = 0, 294 .secondary_offset = 0,
295 .secondary_shift_left = 0,
227 .dynamic_offset = {}, 296 .dynamic_offset = {},
228 .count = 1, 297 .count = 1,
229 .has_secondary = false, 298 .has_secondary = false,
@@ -247,8 +316,10 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
247 return ConstBufferAddr{ 316 return ConstBufferAddr{
248 .index = index.U32(), 317 .index = index.U32(),
249 .offset = base_offset, 318 .offset = base_offset,
319 .shift_left = 0,
250 .secondary_index = 0, 320 .secondary_index = 0,
251 .secondary_offset = 0, 321 .secondary_offset = 0,
322 .secondary_shift_left = 0,
252 .dynamic_offset = dynamic_offset, 323 .dynamic_offset = dynamic_offset,
253 .count = 8, 324 .count = 8,
254 .has_secondary = false, 325 .has_secondary = false,
@@ -258,7 +329,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
258TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { 329TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
259 ConstBufferAddr addr; 330 ConstBufferAddr addr;
260 if (IsBindless(inst)) { 331 if (IsBindless(inst)) {
261 const std::optional<ConstBufferAddr> track_addr{Track(inst.Arg(0))}; 332 const std::optional<ConstBufferAddr> track_addr{Track(inst.Arg(0), env)};
262 if (!track_addr) { 333 if (!track_addr) {
263 throw NotImplementedException("Failed to track bindless texture constant buffer"); 334 throw NotImplementedException("Failed to track bindless texture constant buffer");
264 } 335 }
@@ -267,8 +338,10 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
267 addr = ConstBufferAddr{ 338 addr = ConstBufferAddr{
268 .index = env.TextureBoundBuffer(), 339 .index = env.TextureBoundBuffer(),
269 .offset = inst.Arg(0).U32(), 340 .offset = inst.Arg(0).U32(),
341 .shift_left = 0,
270 .secondary_index = 0, 342 .secondary_index = 0,
271 .secondary_offset = 0, 343 .secondary_offset = 0,
344 .secondary_shift_left = 0,
272 .dynamic_offset = {}, 345 .dynamic_offset = {},
273 .count = 1, 346 .count = 1,
274 .has_secondary = false, 347 .has_secondary = false,
@@ -284,8 +357,9 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
284TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { 357TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
285 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index}; 358 const u32 secondary_index{cbuf.has_secondary ? cbuf.secondary_index : cbuf.index};
286 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset}; 359 const u32 secondary_offset{cbuf.has_secondary ? cbuf.secondary_offset : cbuf.offset};
287 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset)}; 360 const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left};
288 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)}; 361 const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset)
362 << cbuf.secondary_shift_left};
289 return env.ReadTextureType(lhs_raw | rhs_raw); 363 return env.ReadTextureType(lhs_raw | rhs_raw);
290} 364}
291 365
@@ -487,8 +561,10 @@ void TexturePass(Environment& env, IR::Program& program) {
487 .has_secondary = cbuf.has_secondary, 561 .has_secondary = cbuf.has_secondary,
488 .cbuf_index = cbuf.index, 562 .cbuf_index = cbuf.index,
489 .cbuf_offset = cbuf.offset, 563 .cbuf_offset = cbuf.offset,
564 .shift_left = cbuf.shift_left,
490 .secondary_cbuf_index = cbuf.secondary_index, 565 .secondary_cbuf_index = cbuf.secondary_index,
491 .secondary_cbuf_offset = cbuf.secondary_offset, 566 .secondary_cbuf_offset = cbuf.secondary_offset,
567 .secondary_shift_left = cbuf.secondary_shift_left,
492 .count = cbuf.count, 568 .count = cbuf.count,
493 .size_shift = DESCRIPTOR_SIZE_SHIFT, 569 .size_shift = DESCRIPTOR_SIZE_SHIFT,
494 }); 570 });
@@ -499,8 +575,10 @@ void TexturePass(Environment& env, IR::Program& program) {
499 .has_secondary = cbuf.has_secondary, 575 .has_secondary = cbuf.has_secondary,
500 .cbuf_index = cbuf.index, 576 .cbuf_index = cbuf.index,
501 .cbuf_offset = cbuf.offset, 577 .cbuf_offset = cbuf.offset,
578 .shift_left = cbuf.shift_left,
502 .secondary_cbuf_index = cbuf.secondary_index, 579 .secondary_cbuf_index = cbuf.secondary_index,
503 .secondary_cbuf_offset = cbuf.secondary_offset, 580 .secondary_cbuf_offset = cbuf.secondary_offset,
581 .secondary_shift_left = cbuf.secondary_shift_left,
504 .count = cbuf.count, 582 .count = cbuf.count,
505 .size_shift = DESCRIPTOR_SIZE_SHIFT, 583 .size_shift = DESCRIPTOR_SIZE_SHIFT,
506 }); 584 });
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index dcb5ab158..549b81ef7 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -4,6 +4,7 @@
4#pragma once 4#pragma once
5 5
6#include <array> 6#include <array>
7#include <map>
7#include <optional> 8#include <optional>
8#include <vector> 9#include <vector>
9 10
@@ -60,6 +61,7 @@ struct TransformFeedbackVarying {
60struct RuntimeInfo { 61struct RuntimeInfo {
61 std::array<AttributeType, 32> generic_input_types{}; 62 std::array<AttributeType, 32> generic_input_types{};
62 VaryingState previous_stage_stores; 63 VaryingState previous_stage_stores;
64 std::map<IR::Attribute, IR::Attribute> previous_stage_legacy_stores_mapping;
63 65
64 bool convert_depth_mode{}; 66 bool convert_depth_mode{};
65 bool force_early_z{}; 67 bool force_early_z{};
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index e4b5ba567..a479e105e 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -5,6 +5,7 @@
5 5
6#include <array> 6#include <array>
7#include <bitset> 7#include <bitset>
8#include <map>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
10#include "shader_recompiler/frontend/ir/type.h" 11#include "shader_recompiler/frontend/ir/type.h"
@@ -61,8 +62,10 @@ struct TextureBufferDescriptor {
61 bool has_secondary; 62 bool has_secondary;
62 u32 cbuf_index; 63 u32 cbuf_index;
63 u32 cbuf_offset; 64 u32 cbuf_offset;
65 u32 shift_left;
64 u32 secondary_cbuf_index; 66 u32 secondary_cbuf_index;
65 u32 secondary_cbuf_offset; 67 u32 secondary_cbuf_offset;
68 u32 secondary_shift_left;
66 u32 count; 69 u32 count;
67 u32 size_shift; 70 u32 size_shift;
68}; 71};
@@ -85,8 +88,10 @@ struct TextureDescriptor {
85 bool has_secondary; 88 bool has_secondary;
86 u32 cbuf_index; 89 u32 cbuf_index;
87 u32 cbuf_offset; 90 u32 cbuf_offset;
91 u32 shift_left;
88 u32 secondary_cbuf_index; 92 u32 secondary_cbuf_index;
89 u32 secondary_cbuf_offset; 93 u32 secondary_cbuf_offset;
94 u32 secondary_shift_left;
90 u32 count; 95 u32 count;
91 u32 size_shift; 96 u32 size_shift;
92}; 97};
@@ -123,6 +128,8 @@ struct Info {
123 VaryingState stores; 128 VaryingState stores;
124 VaryingState passthrough; 129 VaryingState passthrough;
125 130
131 std::map<IR::Attribute, IR::Attribute> legacy_stores_mapping;
132
126 bool loads_indexed_attributes{}; 133 bool loads_indexed_attributes{};
127 134
128 std::array<bool, 8> stores_frag_color{}; 135 std::array<bool, 8> stores_frag_color{};