diff options
Diffstat (limited to 'src/shader_recompiler')
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) | |||
| 242 | if (MSVC) | 242 | if (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 | ) |
| 255 | else() | 250 | else() |
| 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 | ||
| 177 | void PrecolorInst(IR::Inst& phi) { | 177 | void 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 { | |||
| 13 | namespace { | 13 | namespace { |
| 14 | void GetCbuf(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset, | 14 | void 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 | ||
| 30 | bool IsInputArray(Stage stage) { | 47 | bool 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 | |||
| 12 | namespace Shader::Backend::GLASM { | 8 | namespace 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 | ||
| 103 | void PrecolorInst(IR::Inst& phi) { | 103 | void 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 | |||
| 14 | namespace Shader::Backend::GLSL { | 10 | namespace Shader::Backend::GLSL { |
| 15 | 11 | ||
| 16 | void EmitGetRegister(EmitContext& ctx) { | 12 | void 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 | ||
| 328 | void 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 | |||
| 333 | void Inst::OrderPhiArgs() { | 328 | void 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 | ||
| 139 | std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings( | 139 | std::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 | |||
| 267 | void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& runtime_info) { | 274 | void 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 | ||
| 12 | namespace Shader::Optimization { | 8 | namespace Shader::Optimization { |
| 13 | namespace { | 9 | |
| 14 | template <bool TEST_USES> | 10 | void DeadCodeEliminationPass(IR::Program& program) { |
| 15 | void 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 | |||
| 31 | void 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 | |||
| 49 | void 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 | |||
| 98 | void 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 { | |||
| 19 | struct ConstBufferAddr { | 19 | struct 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 | ||
| 175 | std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst); | 177 | std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env); |
| 176 | 178 | ||
| 177 | std::optional<ConstBufferAddr> Track(const IR::Value& value) { | 179 | std::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 | ||
| 181 | std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) { | 184 | std::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 | |||
| 205 | std::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) { | |||
| 258 | TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) { | 329 | TextureInst 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) { | |||
| 284 | TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { | 357 | TextureType 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 { | |||
| 60 | struct RuntimeInfo { | 61 | struct 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{}; |