diff --git a/src/graphics/shader/recompiler/ShaderRecompiler.cpp b/src/graphics/shader/recompiler/ShaderRecompiler.cpp index 830e3253..54998f0e 100644 --- a/src/graphics/shader/recompiler/ShaderRecompiler.cpp +++ b/src/graphics/shader/recompiler/ShaderRecompiler.cpp @@ -552,19 +552,10 @@ TranslateResult TranslateProgram(std::span code, const CompileOp if (cfg.irreducible) { LogDispatcherFallback(options, cfg, "build"); } else { - const auto unstructured_cfg = cfg; LOGF("%s phase begin: stage=%s hash=0x%016" PRIx64 " CFG Structurize\n", GetDumpLabel(options), StageName(options.stage), options.shader_hash); if (!CFG::Structurize(cfg)) { - const auto failure_kind = cfg.failure_kind; - const auto failure_block = cfg.failure_block; LogDispatcherFallback(options, cfg, "structurize"); - auto failure_reason = std::move(cfg.unsupported_reason); - cfg = unstructured_cfg; - cfg.unsupported = true; - cfg.failure_kind = failure_kind; - cfg.failure_block = failure_block; - cfg.unsupported_reason = std::move(failure_reason); } else { LOGF("%s structured CFG success: blocks=%" PRIu64 "\n", GetDumpLabel(options), static_cast(cfg.blocks.size())); diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp index c00421d4..bb905837 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp @@ -2193,27 +2193,38 @@ bool StructurizeImpl(Graph& graph) { } // namespace bool Structurize(Graph& graph) { - Graph original = graph; - if (StructurizeImpl(graph)) { + Graph structured = graph; + if (StructurizeImpl(structured)) { + graph = std::move(structured); return true; } - Graph failed_graph = std::move(graph); - graph = std::move(original); + const auto failure_kind = structured.failure_kind; + // Structurization inserts and renumbers blocks. Recover source identity for a + // semantic block; a synthetic block has no corresponding original diagnostic ID. + const auto* failed = structured.FindBlock(structured.failure_block); + const auto original = std::ranges::find_if(graph.blocks, [&](const BasicBlock& block) { + return failed != nullptr && failed->inst_begin != failed->inst_end && + block.inst_begin == failed->inst_begin && block.inst_end == failed->inst_end && + block.start_pc == failed->start_pc && block.end_pc == failed->end_pc; + }); + const auto failure_block = original != graph.blocks.end() ? original->id : UINT32_MAX; + auto failure_reason = std::move(structured.unsupported_reason); + Graph routed = graph; const auto route_budget = static_cast(graph.blocks.size()); // Apply one route at a time and retry. Eagerly routing every matching diamond can // rewrite unrelated selections that were already structurally valid. for (uint32_t route_variable = 0; route_variable < route_budget; route_variable++) { - if (!RouteSharedSelectionArm(graph, route_variable)) { + if (!RouteSharedSelectionArm(routed, route_variable)) { break; } - Graph routed = graph; - if (StructurizeImpl(routed)) { - graph = std::move(routed); + structured = routed; + if (StructurizeImpl(structured)) { + graph = std::move(structured); return true; } } - graph = std::move(failed_graph); + SetFailure(graph, failure_kind, failure_block, failure_reason); return false; } diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h index fb2a6689..f85c093f 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.h @@ -110,6 +110,8 @@ struct Graph { }; Graph BuildGraph(const Decoder::Program& program); +// Commits structured control flow on success; preserves the original graph with +// failure diagnostics on failure. failure_block is an original block ID or UINT32_MAX. bool Structurize(Graph& graph); std::string BranchConditionToString(BranchCondition condition); std::string FailureKindToString(FailureKind kind); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index d009ba15..e07a9c60 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -8624,6 +8624,45 @@ void TestNewShaderRecompilerCfgPrunesUnreachableSelectionEntry() { CheckSpirvBinaryValidates(result.spirv); } +void TestNewShaderRecompilerCfgFailedStructurizationPreservesGraph() { + using namespace ShaderRecompiler; + const uint32_t shader[] = { + EncodeSMovB32(0, 129), + EncodeSopp(0x02, 0xfffeu), // semantic loop header, no exit/merge + 0xbf810000u, // unreachable decoder terminator + }; + Decoder::Program decoded; + Decoder::DecodeProgram(std::span{shader}, decoded); + auto graph = CFG::BuildGraph(decoded); + const auto original = graph; + Check(graph.natural_loops.size() == 1u && !graph.irreducible, + "failed structurization fixture must have one reducible loop"); + Check(!CFG::Structurize(graph) && graph.unsupported && + graph.failure_kind == CFG::FailureKind::StructuredControlFlow && + !graph.unsupported_reason.empty(), + "loop without a merge did not retain its structurization failure"); + Check(graph.failure_block == UINT32_MAX, + "failed structurization exposed a discarded synthetic block ID"); + Check(CfgInstructionCoverage(graph, decoded.instructions.size()) == + CfgInstructionCoverage(original, decoded.instructions.size()), + "failed structurization changed instruction coverage"); + auto topology = graph; + topology.unsupported = original.unsupported; + topology.failure_kind = original.failure_kind; + topology.failure_block = original.failure_block; + topology.unsupported_reason = original.unsupported_reason; + Check(CFG::GraphToString(topology) == CFG::GraphToString(original), + "failed structurization changed CFG topology or analyses"); + + auto options = MakeCompileOptions(ShaderType::Compute); + auto result = RecompileForTest(shader, options); + Check(result.program.dispatcher_fallback && + result.program.cfg_failure_kind == graph.failure_kind && + result.program.fallback_reason == graph.unsupported_reason, + "compiler did not consume CFG failure diagnostics directly"); + CheckSpirvBinaryValidates(result.spirv); +} + void TestNewShaderRecompilerCfgIrreducibleDispatcher() { const uint32_t shader[] = { EncodeSopp(0x05, 2), // entry -> B, fallthrough A @@ -13047,6 +13086,7 @@ int main() { TestNewShaderRecompilerCfgNestedEarlyExitSharedTerminal(); TestNewShaderRecompilerCfgSharedTerminalEarlyExit(); TestNewShaderRecompilerCfgPrunesUnreachableSelectionEntry(); + TestNewShaderRecompilerCfgFailedStructurizationPreservesGraph(); TestNewShaderRecompilerCfgIrreducibleDispatcher(); TestNewShaderRecompilerDispatcherSpillsU32x3(); TestNewShaderRecompilerPlanningOnlyLoads();