shader: batch dependent folds to avoid repeated SSA graph walks

This commit is contained in:
nmzik committed 2026-10-17 06:20:02 +02:00
1 parent b82512be06
commit f6f1ac8b40
1 file changed
+29 -23
@@ -51,11 +51,13 @@ public:
} }
void QueueUsers(const std::vector<Use>& users) { void QueueUsers(const std::vector<Use>& users) {
++generation;
affected.clear();
for (const auto& use: users) affected.push_back(use.user); for (const auto& use: users) affected.push_back(use.user);
}
void FlushUsers() {
++generation;
// Pattern folds inspect nested operands even when intermediate operations // 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) { for (size_t i = 0; i < affected.size(); ++i) {
auto* inst = affected[i]; auto* inst = affected[i];
const auto found = locations.find(inst); const auto found = locations.find(inst);
@@ -64,6 +66,7 @@ public:
Queue(inst); Queue(inst);
for (const auto& use: inst->Uses()) affected.push_back(use.user); for (const auto& use: inst->Uses()) affected.push_back(use.user);
} }
affected.clear();
} }
Block::iterator Prepend(Block& block, Block::iterator before, ValueOpcode opcode, 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<Use> users; std::vector<Use> users;
std::vector<Value> operands; std::vector<Value> operands;
const auto drain = [&] { const auto drain = [&] {
for (size_t i = 0; i < worklist.pending.size(); ++i) { do {
auto* inst = worklist.pending[i]; for (size_t i = 0; i < worklist.pending.size(); ++i) {
auto& location = worklist.locations.at(inst); auto* inst = worklist.pending[i];
location.queued = false; auto& location = worklist.locations.at(inst);
const auto opcode = inst->GetOpcode(); location.queued = false;
if (opcode == ValueOpcode::Identity || opcode == ValueOpcode::Void) continue; const auto opcode = inst->GetOpcode();
const auto where = location.instruction; if (opcode == ValueOpcode::Identity || opcode == ValueOpcode::Void) continue;
users.assign(inst->Uses().begin(), inst->Uses().end()); const auto where = location.instruction;
operands.clear(); users.assign(inst->Uses().begin(), inst->Uses().end());
for (size_t arg = 0; arg < inst->NumArgs(); ++arg) operands.push_back(inst->Arg(arg)); operands.clear();
mask_projection.Fold(*inst, wave_size); for (size_t arg = 0; arg < inst->NumArgs(); ++arg) operands.push_back(inst->Arg(arg));
FoldInstruction(*inst->Parent(), where, worklist); mask_projection.Fold(*inst, wave_size);
bool changed = opcode != inst->GetOpcode() || operands.size() != inst->NumArgs(); FoldInstruction(*inst->Parent(), where, worklist);
for (size_t arg = 0; !changed && arg < operands.size(); ++arg) bool changed = opcode != inst->GetOpcode() || operands.size() != inst->NumArgs();
changed = operands[arg] != inst->Arg(arg); for (size_t arg = 0; !changed && arg < operands.size(); ++arg)
if (changed) { changed = operands[arg] != inst->Arg(arg);
worklist.QueueUsers(users); if (changed) {
worklist.Queue(inst); worklist.QueueUsers(users);
worklist.Queue(inst);
}
} }
} worklist.pending.clear();
worklist.pending.clear(); worklist.FlushUsers();
} while (!worklist.pending.empty());
}; };
drain(); drain();
// Normalize retained PHI/select values only after every supported field read has // Normalize retained PHI/select values only after every supported field read has