mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 03:16:54 +02:00
shader_recompiler: add SSA
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/ReadLaneElimination.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
namespace {
|
||||
|
||||
struct ChainResult {
|
||||
Value value;
|
||||
Inst* write = nullptr;
|
||||
};
|
||||
|
||||
ChainResult SearchChain(Value value, uint32_t lane, uint32_t wave_size) {
|
||||
for (;;) {
|
||||
value = value.Resolve();
|
||||
auto* inst = value.TryInstruction();
|
||||
if (inst == nullptr || inst->GetOpcode() != ValueOpcode::WriteLane) {
|
||||
return {value};
|
||||
}
|
||||
const auto selector = inst->Arg(2).Resolve();
|
||||
if (!selector.IsImmediate() || selector.GetType() != Type::U32) {
|
||||
return {value};
|
||||
}
|
||||
if (selector.U32() % wave_size == lane) {
|
||||
return {value, inst};
|
||||
}
|
||||
value = inst->Arg(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPossibleToEliminate(Value source, uint32_t lane, uint32_t wave_size) {
|
||||
std::queue<Value> queue;
|
||||
std::unordered_set<Inst*> visited;
|
||||
queue.push(source);
|
||||
|
||||
while (!queue.empty()) {
|
||||
const auto chain = SearchChain(queue.front(), lane, wave_size);
|
||||
queue.pop();
|
||||
if (chain.write != nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto* inst = chain.value.TryInstruction();
|
||||
if (inst == nullptr || inst->GetOpcode() != ValueOpcode::Phi || inst->NumArgs() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (!visited.insert(inst).second) {
|
||||
continue;
|
||||
}
|
||||
for (size_t index = inst->NumArgs(); index-- > 0;) {
|
||||
queue.push(inst->Arg(index));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
using PhiMap = std::unordered_map<Inst*, Inst*>;
|
||||
|
||||
Value GetRealValue(PhiMap& phi_map, Value source, uint32_t lane, uint32_t wave_size) {
|
||||
const auto chain = SearchChain(source, lane, wave_size);
|
||||
if (chain.write != nullptr) {
|
||||
return chain.write->Arg(1);
|
||||
}
|
||||
|
||||
auto* inst = chain.value.ResolveInstruction();
|
||||
EXIT_IF(inst->GetOpcode() != ValueOpcode::Phi);
|
||||
const auto [entry, is_new] = phi_map.try_emplace(inst);
|
||||
if (!is_new) {
|
||||
return Value(entry->second);
|
||||
}
|
||||
|
||||
auto* block = inst->Parent();
|
||||
auto insertion_point = std::find_if(block->begin(), block->end(),
|
||||
[&](const Inst& candidate) { return &candidate == inst; });
|
||||
EXIT_IF(insertion_point == block->end());
|
||||
auto& phi = *block->PrependNewInst(insertion_point, ValueOpcode::Phi);
|
||||
phi.SetFlags(Type::U32);
|
||||
entry->second = φ
|
||||
|
||||
std::vector<Value> arguments;
|
||||
arguments.reserve(inst->NumArgs());
|
||||
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
||||
arguments.push_back(GetRealValue(phi_map, inst->Arg(index), lane, wave_size));
|
||||
}
|
||||
const auto first = arguments.front().Resolve();
|
||||
if (std::ranges::all_of(arguments,
|
||||
[&](Value argument) { return argument.Resolve() == first; })) {
|
||||
phi.ReplaceUsesWith(first);
|
||||
} else {
|
||||
for (size_t index = 0; index < arguments.size(); index++) {
|
||||
phi.AddPhiOperand(inst->PhiBlock(index), arguments[index]);
|
||||
}
|
||||
}
|
||||
return Value(&phi);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ReadLaneStats EliminateReadLane(ValueProgram& program, uint32_t wave_size) {
|
||||
ReadLaneStats stats;
|
||||
if (wave_size != 32u && wave_size != 64u) {
|
||||
return stats;
|
||||
}
|
||||
|
||||
for (auto* block: program.blocks) {
|
||||
for (auto& inst: *block) {
|
||||
if (inst.GetOpcode() != ValueOpcode::ReadLane) {
|
||||
continue;
|
||||
}
|
||||
const auto selector = inst.Arg(1).Resolve();
|
||||
if (!selector.IsImmediate() || selector.GetType() != Type::U32) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto lane = selector.U32() % wave_size;
|
||||
const auto chain = SearchChain(inst.Arg(0), lane, wave_size);
|
||||
if (chain.write != nullptr) {
|
||||
inst.ReplaceUsesWith(chain.write->Arg(1));
|
||||
stats.rewritten_reads++;
|
||||
continue;
|
||||
}
|
||||
auto* producer = chain.value.TryInstruction();
|
||||
if (producer == nullptr || producer->GetOpcode() != ValueOpcode::Phi ||
|
||||
!IsPossibleToEliminate(chain.value, lane, wave_size)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PhiMap phi_map;
|
||||
inst.ReplaceUsesWith(GetRealValue(phi_map, chain.value, lane, wave_size));
|
||||
stats.rewritten_reads++;
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||
Reference in New Issue
Block a user