mirror of
https://github.com/yuzu-emu/yuzu.git
synced 2025-06-12 10:27:57 -05:00
shader: Support SSA loops on IR
This commit is contained in:
@ -13,7 +13,7 @@ namespace Shader::Optimization {
|
||||
void DeadCodeEliminationPass(IR::Block& block) {
|
||||
// We iterate over the instructions in reverse order.
|
||||
// This is because removing an instruction reduces the number of uses for earlier instructions.
|
||||
for (IR::Inst& inst : std::views::reverse(block)) {
|
||||
for (IR::Inst& inst : block | std::views::reverse) {
|
||||
if (!inst.HasUses() && !inst.MayHaveSideEffects()) {
|
||||
inst.Invalidate();
|
||||
}
|
||||
|
@ -4,14 +4,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/function.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
|
||||
template <typename Func>
|
||||
void Invoke(Func&& func, IR::Function& function) {
|
||||
for (const auto& block : function.blocks) {
|
||||
void PostOrderInvoke(Func&& func, IR::Function& function) {
|
||||
for (const auto& block : function.post_order_blocks) {
|
||||
func(*block);
|
||||
}
|
||||
}
|
||||
@ -20,7 +22,7 @@ void ConstantPropagationPass(IR::Block& block);
|
||||
void DeadCodeEliminationPass(IR::Block& block);
|
||||
void GlobalMemoryToStorageBufferPass(IR::Block& block);
|
||||
void IdentityRemovalPass(IR::Function& function);
|
||||
void SsaRewritePass(IR::Function& function);
|
||||
void SsaRewritePass(std::span<IR::Block* const> post_order_blocks);
|
||||
void VerificationPass(const IR::Function& function);
|
||||
|
||||
} // namespace Shader::Optimization
|
||||
|
@ -14,7 +14,13 @@
|
||||
// https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
|
||||
//
|
||||
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/container/flat_map.hpp>
|
||||
#include <boost/container/flat_set.hpp>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/function.h"
|
||||
@ -26,9 +32,9 @@
|
||||
|
||||
namespace Shader::Optimization {
|
||||
namespace {
|
||||
using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>;
|
||||
|
||||
struct FlagTag {};
|
||||
struct FlagTag {
|
||||
auto operator<=>(const FlagTag&) const noexcept = default;
|
||||
};
|
||||
struct ZeroFlagTag : FlagTag {};
|
||||
struct SignFlagTag : FlagTag {};
|
||||
struct CarryFlagTag : FlagTag {};
|
||||
@ -38,9 +44,15 @@ struct GotoVariable : FlagTag {
|
||||
GotoVariable() = default;
|
||||
explicit GotoVariable(u32 index_) : index{index_} {}
|
||||
|
||||
auto operator<=>(const GotoVariable&) const noexcept = default;
|
||||
|
||||
u32 index;
|
||||
};
|
||||
|
||||
using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag,
|
||||
OverflowFlagTag, GotoVariable>;
|
||||
using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>;
|
||||
|
||||
struct DefTable {
|
||||
[[nodiscard]] ValueMap& operator[](IR::Reg variable) noexcept {
|
||||
return regs[IR::RegIndex(variable)];
|
||||
@ -102,19 +114,35 @@ public:
|
||||
}
|
||||
|
||||
IR::Value ReadVariable(auto variable, IR::Block* block) {
|
||||
auto& def{current_def[variable]};
|
||||
const ValueMap& def{current_def[variable]};
|
||||
if (const auto it{def.find(block)}; it != def.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return ReadVariableRecursive(variable, block);
|
||||
}
|
||||
|
||||
void SealBlock(IR::Block* block) {
|
||||
const auto it{incomplete_phis.find(block)};
|
||||
if (it != incomplete_phis.end()) {
|
||||
for (auto& [variant, phi] : it->second) {
|
||||
std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant);
|
||||
}
|
||||
}
|
||||
sealed_blocks.insert(block);
|
||||
}
|
||||
|
||||
private:
|
||||
IR::Value ReadVariableRecursive(auto variable, IR::Block* block) {
|
||||
IR::Value val;
|
||||
if (const std::span preds{block->ImmediatePredecessors()}; preds.size() == 1) {
|
||||
if (!sealed_blocks.contains(block)) {
|
||||
// Incomplete CFG
|
||||
IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)};
|
||||
incomplete_phis[block].insert_or_assign(variable, phi);
|
||||
val = IR::Value{&*phi};
|
||||
} else if (const std::span imm_preds{block->ImmediatePredecessors()};
|
||||
imm_preds.size() == 1) {
|
||||
// Optimize the common case of one predecessor: no phi needed
|
||||
val = ReadVariable(variable, preds.front());
|
||||
val = ReadVariable(variable, imm_preds.front());
|
||||
} else {
|
||||
// Break potential cycles with operandless phi
|
||||
IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)};
|
||||
@ -127,8 +155,8 @@ private:
|
||||
}
|
||||
|
||||
IR::Value AddPhiOperands(auto variable, IR::Inst& phi, IR::Block* block) {
|
||||
for (IR::Block* const pred : block->ImmediatePredecessors()) {
|
||||
phi.AddPhiOperand(pred, ReadVariable(variable, pred));
|
||||
for (IR::Block* const imm_pred : block->ImmediatePredecessors()) {
|
||||
phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred));
|
||||
}
|
||||
return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable));
|
||||
}
|
||||
@ -159,6 +187,9 @@ private:
|
||||
return same;
|
||||
}
|
||||
|
||||
boost::container::flat_set<IR::Block*> sealed_blocks;
|
||||
boost::container::flat_map<IR::Block*, boost::container::flat_map<Variant, IR::Inst*>>
|
||||
incomplete_phis;
|
||||
DefTable current_def;
|
||||
};
|
||||
|
||||
@ -218,14 +249,19 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void VisitBlock(Pass& pass, IR::Block* block) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
VisitInst(pass, block, inst);
|
||||
}
|
||||
pass.SealBlock(block);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void SsaRewritePass(IR::Function& function) {
|
||||
void SsaRewritePass(std::span<IR::Block* const> post_order_blocks) {
|
||||
Pass pass;
|
||||
for (IR::Block* const block : function.blocks) {
|
||||
for (IR::Inst& inst : block->Instructions()) {
|
||||
VisitInst(pass, block, inst);
|
||||
}
|
||||
for (IR::Block* const block : post_order_blocks | std::views::reverse) {
|
||||
VisitBlock(pass, block);
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user