shader: Initial recompiler work
This commit is contained in:
parent
75059c46d6
commit
2d48a7b4d0
57 changed files with 7061 additions and 0 deletions
23
src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
Normal file
23
src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/microinstruction.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
|
||||
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)) {
|
||||
if (!inst.HasUses() && !inst.MayHaveSideEffects()) {
|
||||
inst.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Optimization
|
87
src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
Normal file
87
src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/microinstruction.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
namespace {
|
||||
using Iterator = IR::Block::iterator;
|
||||
|
||||
enum class TrackingType {
|
||||
Reg,
|
||||
};
|
||||
|
||||
struct RegisterInfo {
|
||||
IR::Value register_value;
|
||||
TrackingType tracking_type;
|
||||
Iterator last_set_instruction;
|
||||
bool set_instruction_present = false;
|
||||
};
|
||||
|
||||
void DoSet(IR::Block& block, RegisterInfo& info, IR::Value value, Iterator set_inst,
|
||||
TrackingType tracking_type) {
|
||||
if (info.set_instruction_present) {
|
||||
info.last_set_instruction->Invalidate();
|
||||
block.Instructions().erase(info.last_set_instruction);
|
||||
}
|
||||
info.register_value = value;
|
||||
info.tracking_type = tracking_type;
|
||||
info.set_instruction_present = true;
|
||||
info.last_set_instruction = set_inst;
|
||||
}
|
||||
|
||||
RegisterInfo Nothing(Iterator get_inst, TrackingType tracking_type) {
|
||||
RegisterInfo info{};
|
||||
info.register_value = IR::Value{&*get_inst};
|
||||
info.tracking_type = tracking_type;
|
||||
return info;
|
||||
}
|
||||
|
||||
void DoGet(RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) {
|
||||
if (info.register_value.IsEmpty()) {
|
||||
info = Nothing(get_inst, tracking_type);
|
||||
return;
|
||||
}
|
||||
if (info.tracking_type == tracking_type) {
|
||||
get_inst->ReplaceUsesWith(info.register_value);
|
||||
return;
|
||||
}
|
||||
info = Nothing(get_inst, tracking_type);
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
void GetSetElimination(IR::Block& block) {
|
||||
std::array<RegisterInfo, 255> reg_info;
|
||||
|
||||
for (Iterator inst = block.begin(); inst != block.end(); ++inst) {
|
||||
switch (inst->Opcode()) {
|
||||
case IR::Opcode::GetRegister: {
|
||||
const IR::Reg reg{inst->Arg(0).Reg()};
|
||||
if (reg == IR::Reg::RZ) {
|
||||
break;
|
||||
}
|
||||
const size_t index{static_cast<size_t>(reg)};
|
||||
DoGet(reg_info.at(index), inst, TrackingType::Reg);
|
||||
break;
|
||||
}
|
||||
case IR::Opcode::SetRegister: {
|
||||
const IR::Reg reg{inst->Arg(0).Reg()};
|
||||
if (reg == IR::Reg::RZ) {
|
||||
break;
|
||||
}
|
||||
const size_t index{static_cast<size_t>(reg)};
|
||||
DoSet(block, reg_info.at(index), inst->Arg(1), inst, TrackingType::Reg);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Optimization
|
37
src/shader_recompiler/ir_opt/identity_removal_pass.cpp
Normal file
37
src/shader_recompiler/ir_opt/identity_removal_pass.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/microinstruction.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
|
||||
void IdentityRemovalPass(IR::Block& block) {
|
||||
std::vector<IR::Inst*> to_invalidate;
|
||||
|
||||
for (auto inst = block.begin(); inst != block.end();) {
|
||||
const size_t num_args{inst->NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
IR::Value arg;
|
||||
while ((arg = inst->Arg(i)).IsIdentity()) {
|
||||
inst->SetArg(i, arg.Inst()->Arg(0));
|
||||
}
|
||||
}
|
||||
if (inst->Opcode() == IR::Opcode::Identity || inst->Opcode() == IR::Opcode::Void) {
|
||||
to_invalidate.push_back(&*inst);
|
||||
inst = block.Instructions().erase(inst);
|
||||
} else {
|
||||
++inst;
|
||||
}
|
||||
}
|
||||
|
||||
for (IR::Inst* const inst : to_invalidate) {
|
||||
inst->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Shader::Optimization
|
16
src/shader_recompiler/ir_opt/passes.h
Normal file
16
src/shader_recompiler/ir_opt/passes.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
|
||||
void DeadCodeEliminationPass(IR::Block& block);
|
||||
void GetSetElimination(IR::Block& block);
|
||||
void IdentityRemovalPass(IR::Block& block);
|
||||
void VerificationPass(const IR::Block& block);
|
||||
|
||||
} // namespace Shader::Optimization
|
50
src/shader_recompiler/ir_opt/verification_pass.cpp
Normal file
50
src/shader_recompiler/ir_opt/verification_pass.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "shader_recompiler/exception.h"
|
||||
#include "shader_recompiler/frontend/ir/basic_block.h"
|
||||
#include "shader_recompiler/frontend/ir/microinstruction.h"
|
||||
#include "shader_recompiler/ir_opt/passes.h"
|
||||
|
||||
namespace Shader::Optimization {
|
||||
|
||||
static void ValidateTypes(const IR::Block& block) {
|
||||
for (const IR::Inst& inst : block) {
|
||||
const size_t num_args{inst.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
const IR::Type t1{inst.Arg(i).Type()};
|
||||
const IR::Type t2{IR::ArgTypeOf(inst.Opcode(), i)};
|
||||
if (!IR::AreTypesCompatible(t1, t2)) {
|
||||
throw LogicError("Invalid types in block:\n{}", IR::DumpBlock(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ValidateUses(const IR::Block& block) {
|
||||
std::map<IR::Inst*, int> actual_uses;
|
||||
for (const IR::Inst& inst : block) {
|
||||
const size_t num_args{inst.NumArgs()};
|
||||
for (size_t i = 0; i < num_args; ++i) {
|
||||
const IR::Value arg{inst.Arg(i)};
|
||||
if (!arg.IsImmediate()) {
|
||||
++actual_uses[arg.Inst()];
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto [inst, uses] : actual_uses) {
|
||||
if (inst->UseCount() != uses) {
|
||||
throw LogicError("Invalid uses in block:\n{}", IR::DumpBlock(block));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VerificationPass(const IR::Block& block) {
|
||||
ValidateTypes(block);
|
||||
ValidateUses(block);
|
||||
}
|
||||
|
||||
} // namespace Shader::Optimization
|
Loading…
Add table
Add a link
Reference in a new issue