From 0ffacf22e61e2a993190396c3ef233ea1560bae1 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 15 Aug 2026 08:48:20 +0200 Subject: [PATCH] graphics: accept phi-mediated SRT cycles --- .../shader/recompiler/ir/passes/SrtWalker.cpp | 13 +++++++++-- tests/ResourceTrackingTests.cpp | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp b/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp index 51d5eb10f..8ffd61bba 100644 --- a/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp +++ b/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp @@ -308,8 +308,17 @@ private: if (inst == nullptr) { return Fail(use_pc, error, "invalid typed planning value"); } - if (std::ranges::find(m_visiting, inst) != m_visiting.end()) { - return inst->GetOpcode() == ValueOpcode::Phi; + const auto cycle = std::ranges::find(m_visiting, inst); + if (cycle != m_visiting.end()) { + const auto contains_phi = std::any_of(cycle, m_visiting.end(), [](const Inst* value) { + return value->GetOpcode() == ValueOpcode::Phi; + }); + if (contains_phi) { + return true; + } + return Fail(use_pc, error, + fmt::format("cyclic typed planning value {} without a phi", + ValueOpcodeName(inst->GetOpcode()))); } if (std::ranges::find(m_visited, inst) != m_visited.end()) { return true; diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 1ef021e75..e0da7f013 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -447,6 +447,28 @@ void TestPhiValidation() { "control-dependent descriptor phi was not rejected transactionally"); } +void TestLoopCycleEnteredThroughRuntimeValue() { + Fixture fixture; + auto *entry = fixture.block; + auto *loop = fixture.AddBlock(); + const auto initial = fixture.UserData(0); + entry->AddBranch(loop); + loop->AddBranch(loop); + auto &phi = loop->AppendNewInst(ValueOpcode::Phi, {}, + static_cast(Type::U32)); + const auto carried = fixture.Emit( + ValueOpcode::BitwiseAnd32, {Value(&phi), Value(0xffffffffu)}, 0, loop); + phi.AddPhiOperand(entry, initial); + phi.AddPhiOperand(loop, carried); + fixture.Emit(ValueOpcode::GetBufferResource, + {carried, Value(0u), Value(0u), Value(0u)}, + MemoryFlags{0, 12}, loop); + + std::string error; + Check(BuildSrtPlan(fixture.program, &error), + "SRT planning rejected a valid loop entered through a runtime value"); +} + void TestInvariantLoopPhi() { Fixture fixture; auto *entry = fixture.block; @@ -610,6 +632,7 @@ int main() { Run("SRT runtime", TestSrtFlatteningAndRuntimeMemoization); Run("dynamic SRT", TestDynamicSrtReadRemainsExplicit); Run("phi validation", TestPhiValidation); + Run("runtime-rooted loop", TestLoopCycleEnteredThroughRuntimeValue); Run("invariant loop phi", TestInvariantLoopPhi); Run("address materialization", TestAddressMaterializationAndSpecialization); Run("shader info and bindings", TestShaderInfoAndBindingLayout);