diff --git a/src/graphics/shader/recompiler/ShaderRecompiler.cpp b/src/graphics/shader/recompiler/ShaderRecompiler.cpp index c0aba2d8c..02c52c337 100644 --- a/src/graphics/shader/recompiler/ShaderRecompiler.cpp +++ b/src/graphics/shader/recompiler/ShaderRecompiler.cpp @@ -504,8 +504,11 @@ void PrepareCallTargets(ShaderSource& source, const CompileOptions& options) { }; Register = [&](uint32_t before, uint32_t code) -> Value { std::vector crossed_calls; - const auto index = CFG::FindScalarDefinition(source.decoded, graph, before, code, - source.wave_size, crossed_calls); + const auto definition = CFG::FindScalarDefinition(source.decoded, graph, before, code, + source.wave_size, crossed_calls); + // Several reaching definitions select the value on the GPU, so no SRT walk evaluates it. + if (!definition) return Value(&block.AppendNewInst(ValueOpcode::UndefU32)); + const auto index = *definition; for (const auto crossed: crossed_calls) { std::ranges::find(source.calls, crossed, &ShaderSource::Call::instruction)->preserved_registers.set(code); } diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp index 4b87933d2..dd50b2929 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp @@ -1720,9 +1720,9 @@ bool MayWriteScalarRegister(const Decoder::Instruction& inst, uint32_t code, uin return InstructionWritesScalarCode(inst, code); } -uint32_t FindScalarDefinition(const Decoder::Program& program, const Graph& graph, - uint32_t before, uint32_t code, uint32_t wave_size, - std::vector& crossed_calls) { +std::optional FindScalarDefinition(const Decoder::Program& program, const Graph& graph, + uint32_t before, uint32_t code, uint32_t wave_size, + std::vector& crossed_calls) { EXIT_IF(before >= program.instructions.size()); const auto found = std::ranges::find_if(graph.blocks, [before](const auto& block) { return block.inst_begin <= before && before < block.inst_end; @@ -1748,7 +1748,7 @@ uint32_t FindScalarDefinition(const Decoder::Program& program, const Graph& grap if (MayWriteScalarRegister(inst, code, wave_size)) { index = i; break; } } if (index != UINT32_MAX || block->id == graph.entry_block) { - EXIT_IF(definition && *definition != index); + if (definition && *definition != index) return std::nullopt; definition = index; continue; } @@ -1758,7 +1758,7 @@ uint32_t FindScalarDefinition(const Decoder::Program& program, const Graph& grap } } EXIT_IF(!definition); - return *definition; + return definition; } std::string BranchConditionToString(BranchCondition condition) { diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h index 2e636c8d5..a9561b65e 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h @@ -5,6 +5,7 @@ #include "common/stringUtils.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" +#include #include namespace Libs::Graphics::ShaderRecompiler::CFG { @@ -114,9 +115,11 @@ struct Graph { Graph BuildGraph(const Decoder::Program& program); bool MayWriteScalarRegister(const Decoder::Instruction& inst, uint32_t code, uint32_t wave_size); -uint32_t FindScalarDefinition(const Decoder::Program& program, const Graph& graph, - uint32_t before, uint32_t code, uint32_t wave_size, - std::vector& crossed_calls); +// Returns the one instruction defining `code` that reaches `before` (UINT32_MAX: the entry value), +// or nullopt if several do. +std::optional FindScalarDefinition(const Decoder::Program& program, const Graph& graph, + uint32_t before, uint32_t code, uint32_t wave_size, + std::vector& crossed_calls); // Returns structured control flow or failure diagnostics without changing the native graph. // On failure, failure_block is an original block ID or UINT32_MAX. Graph Structurize(const Graph& graph); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 53a4fc715..6fdc1efa4 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -11125,6 +11125,14 @@ void TestMemoryFedScalarLeafCall() { const auto skipped = PrepareShaderSource(gpu_callable, options); Check(skipped.unsupported_call_pc == 12u && skipped.calls.empty(), "GPU-buffer callable did not report its unsupported call site"); + // The table index s20 is loop-carried, so two definitions reach the call. + const uint32_t loop_callable[] = {EncodeSmem0(0x08, 20, 0), 0xfa000000u, + EncodeSmem0(0x09, 14, 0), 20u << 25u, EncodeSop1(0x21, 14, 14), + EncodeSmem0(0x08, 20, 0), 20u << 25u, EncodeSopc(0x07, 20, 128), + EncodeSopp(0x05, 0xfff9u), EncodeSopp(0x01)}; + const auto loop_skipped = PrepareShaderSource(loop_callable, options); + Check(loop_skipped.unsupported_call_pc == 16u && loop_skipped.calls.empty(), + "loop-selected GPU-buffer callable did not report its unsupported call site"); auto source = PrepareShaderSource(shader, options); Check(source.calls.size() == 1 && source.decoded.has_swap_pc && source.decoded.instructions[source.calls.front().instruction].branch_target == UINT32_MAX,