shader: Initial recompiler work

This commit is contained in:
ReinUsesLisp 2021-01-09 03:30:07 -03:00 committed by ameerj
parent 75059c46d6
commit 2d48a7b4d0
57 changed files with 7061 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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