From f6f1ac8b40dbcf603c1b88002f2467999c866c03 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 17 Oct 2026 06:20:02 +0200 Subject: [PATCH] shader: batch dependent folds to avoid repeated SSA graph walks --- .../ir/passes/ConstantPropagation.cpp | 52 +++++++++++-------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp b/src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp index 413676da3..57edc068d 100644 --- a/src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ConstantPropagation.cpp @@ -51,11 +51,13 @@ public: } void QueueUsers(const std::vector& users) { - ++generation; - affected.clear(); for (const auto& use: users) affected.push_back(use.user); + } + + void FlushUsers() { + ++generation; // Pattern folds inspect nested operands even when intermediate operations - // stay dynamic. Visit consumers once per change, including cyclic Phi edges. + // stay dynamic. Visit their union once per drain, including cyclic Phi edges. for (size_t i = 0; i < affected.size(); ++i) { auto* inst = affected[i]; const auto found = locations.find(inst); @@ -64,6 +66,7 @@ public: Queue(inst); for (const auto& use: inst->Uses()) affected.push_back(use.user); } + affected.clear(); } Block::iterator Prepend(Block& block, Block::iterator before, ValueOpcode opcode, @@ -977,27 +980,30 @@ void ConstantPropagationPass(const BlockList& blocks, uint32_t wave_size) { std::vector users; std::vector operands; const auto drain = [&] { - for (size_t i = 0; i < worklist.pending.size(); ++i) { - auto* inst = worklist.pending[i]; - auto& location = worklist.locations.at(inst); - location.queued = false; - const auto opcode = inst->GetOpcode(); - if (opcode == ValueOpcode::Identity || opcode == ValueOpcode::Void) continue; - const auto where = location.instruction; - users.assign(inst->Uses().begin(), inst->Uses().end()); - operands.clear(); - for (size_t arg = 0; arg < inst->NumArgs(); ++arg) operands.push_back(inst->Arg(arg)); - mask_projection.Fold(*inst, wave_size); - FoldInstruction(*inst->Parent(), where, worklist); - bool changed = opcode != inst->GetOpcode() || operands.size() != inst->NumArgs(); - for (size_t arg = 0; !changed && arg < operands.size(); ++arg) - changed = operands[arg] != inst->Arg(arg); - if (changed) { - worklist.QueueUsers(users); - worklist.Queue(inst); + do { + for (size_t i = 0; i < worklist.pending.size(); ++i) { + auto* inst = worklist.pending[i]; + auto& location = worklist.locations.at(inst); + location.queued = false; + const auto opcode = inst->GetOpcode(); + if (opcode == ValueOpcode::Identity || opcode == ValueOpcode::Void) continue; + const auto where = location.instruction; + users.assign(inst->Uses().begin(), inst->Uses().end()); + operands.clear(); + for (size_t arg = 0; arg < inst->NumArgs(); ++arg) operands.push_back(inst->Arg(arg)); + mask_projection.Fold(*inst, wave_size); + FoldInstruction(*inst->Parent(), where, worklist); + bool changed = opcode != inst->GetOpcode() || operands.size() != inst->NumArgs(); + for (size_t arg = 0; !changed && arg < operands.size(); ++arg) + changed = operands[arg] != inst->Arg(arg); + if (changed) { + worklist.QueueUsers(users); + worklist.Queue(inst); + } } - } - worklist.pending.clear(); + worklist.pending.clear(); + worklist.FlushUsers(); + } while (!worklist.pending.empty()); }; drain(); // Normalize retained PHI/select values only after every supported field read has