mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
Remove dead shader instruction cycles with linear liveness traversal
This commit is contained in:
@@ -152,6 +152,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend void EliminateDeadCode(const std::vector<Block*>& blocks);
|
||||||
|
|
||||||
void AddUse(Inst* used, size_t operand);
|
void AddUse(Inst* used, size_t operand);
|
||||||
void RemoveUse(Inst* used, size_t operand);
|
void RemoveUse(Inst* used, size_t operand);
|
||||||
void ClearArgs();
|
void ClearArgs();
|
||||||
@@ -161,6 +163,7 @@ private:
|
|||||||
|
|
||||||
ValueOpcode opcode;
|
ValueOpcode opcode;
|
||||||
uint8_t num_args;
|
uint8_t num_args;
|
||||||
|
bool live = false;
|
||||||
mutable uint32_t evaluation_index = UINT32_MAX;
|
mutable uint32_t evaluation_index = UINT32_MAX;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
Block* parent = nullptr;
|
Block* parent = nullptr;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
|
#include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||||
|
|
||||||
void RemoveIdentities(const BlockList& blocks) {
|
void RemoveIdentities(const BlockList& blocks) {
|
||||||
@@ -18,23 +20,32 @@ void RemoveIdentities(const BlockList& blocks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EliminateDeadCode(const BlockList& blocks) {
|
void EliminateDeadCode(const BlockList& blocks) {
|
||||||
bool changed;
|
std::vector<Inst*> pending;
|
||||||
do {
|
for (auto* block: blocks) {
|
||||||
changed = false;
|
for (auto& inst: *block) {
|
||||||
for (auto block = blocks.rbegin(); block != blocks.rend(); block++) {
|
// Retained resource-planning instructions live outside the block list.
|
||||||
auto& instructions = (*block)->Instructions();
|
inst.live = inst.MayHaveSideEffects() || std::ranges::any_of(inst.Uses(),
|
||||||
auto inst = instructions.end();
|
[](const Use& use) { return use.user->Parent() == nullptr; });
|
||||||
while (inst != instructions.begin()) {
|
if (inst.live) pending.push_back(&inst);
|
||||||
--inst;
|
}
|
||||||
if (inst->HasUses() || inst->MayHaveSideEffects()) {
|
}
|
||||||
continue;
|
while (!pending.empty()) {
|
||||||
}
|
const auto* inst = pending.back();
|
||||||
inst->Invalidate();
|
pending.pop_back();
|
||||||
inst = instructions.erase(inst);
|
for (size_t arg = 0; arg < inst->NumArgs(); ++arg) {
|
||||||
changed = true;
|
auto* input = inst->Arg(arg).TryInstruction();
|
||||||
|
if (input != nullptr && input->Parent() != nullptr && !input->live) {
|
||||||
|
input->live = true;
|
||||||
|
pending.push_back(input);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (changed);
|
}
|
||||||
|
// Unlink every dead operand before freeing nodes in a dead Phi cycle.
|
||||||
|
for (auto* block: blocks)
|
||||||
|
for (auto& inst: *block)
|
||||||
|
if (!inst.live) inst.Invalidate();
|
||||||
|
for (auto* block: blocks)
|
||||||
|
std::erase_if(block->Instructions(), [](const Inst& inst) { return !inst.live; });
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||||
|
|||||||
@@ -650,6 +650,39 @@ void TestControlFlowValueSurvivesReadLaneFolding() {
|
|||||||
ValidateProgram(fixture.program, true);
|
ValidateProgram(fixture.program, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestDeadPhiCyclesAndPlanningRoots() {
|
||||||
|
Fixture fixture(2);
|
||||||
|
auto &entry = fixture.BlockAt(0);
|
||||||
|
auto &loop = fixture.BlockAt(1);
|
||||||
|
entry.AddBranch(&loop);
|
||||||
|
loop.AddBranch(&loop);
|
||||||
|
const auto source = fixture.Emit(ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(2))});
|
||||||
|
const auto identity = fixture.Emit(ValueOpcode::Identity, {source});
|
||||||
|
auto &live = loop.AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32));
|
||||||
|
live.AddPhiOperand(&entry, identity);
|
||||||
|
live.AddPhiOperand(&loop, Value(&live));
|
||||||
|
const auto reference = fixture.Emit(ValueOpcode::ReferenceU32, {Value(&live)}, 0, 1);
|
||||||
|
auto &dead = loop.AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U32));
|
||||||
|
const auto increment = fixture.Emit(ValueOpcode::IAdd32, {Value(&dead), Value(1u)}, 0, 1);
|
||||||
|
dead.AddPhiOperand(&entry, source);
|
||||||
|
dead.AddPhiOperand(&loop, increment);
|
||||||
|
const auto retained = fixture.Emit(ValueOpcode::GetUserData, {Value(static_cast<ScalarReg>(3))});
|
||||||
|
auto &planning = fixture.program.value_storage.emplace_back(ValueOpcode::Identity);
|
||||||
|
planning.SetArg(0, retained);
|
||||||
|
|
||||||
|
EliminateDeadCode(fixture.program.blocks);
|
||||||
|
Check(entry.Instructions().size() == 3 && loop.Instructions().size() == 2,
|
||||||
|
"dead Phi cycle survived or a live/planning dependency was removed");
|
||||||
|
Check(identity.Instruction()->Arg(0) == source && live.Arg(0) == identity &&
|
||||||
|
live.Arg(1) == Value(&live) && planning.Arg(0) == retained,
|
||||||
|
"DCE did not preserve direct Identity operands or live Phi recurrence");
|
||||||
|
// Dropping roots must clear prior marks and remove the newly dead SCC safely.
|
||||||
|
reference.Instruction()->Invalidate();
|
||||||
|
planning.Invalidate();
|
||||||
|
EliminateDeadCode(fixture.program.blocks);
|
||||||
|
Check(entry.empty() && loop.empty(), "DCE reused stale marks after removing its roots");
|
||||||
|
}
|
||||||
|
|
||||||
void TestUndefinedRuntimeValueFails() {
|
void TestUndefinedRuntimeValueFails() {
|
||||||
Fixture fixture;
|
Fixture fixture;
|
||||||
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
|
const auto undef = fixture.Emit(ValueOpcode::UndefU32);
|
||||||
@@ -699,6 +732,7 @@ int main() {
|
|||||||
TestReadLaneElimination();
|
TestReadLaneElimination();
|
||||||
TestOptimizationPipeline();
|
TestOptimizationPipeline();
|
||||||
TestControlFlowValueSurvivesReadLaneFolding();
|
TestControlFlowValueSurvivesReadLaneFolding();
|
||||||
|
TestDeadPhiCyclesAndPlanningRoots();
|
||||||
TestUndefinedRuntimeValueFails();
|
TestUndefinedRuntimeValueFails();
|
||||||
std::cout << "TypedValuePlanningTests: all cases passed\n";
|
std::cout << "TypedValuePlanningTests: all cases passed\n";
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user