yuzu/src/shader_recompiler/frontend/ir/microinstruction.cpp

268 lines
7.0 KiB
C++
Raw Normal View History

2021-01-09 06:30:07 +00:00
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
2021-02-06 05:38:22 +00:00
#include <memory>
2021-01-09 06:30:07 +00:00
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/microinstruction.h"
#include "shader_recompiler/frontend/ir/type.h"
namespace Shader::IR {
static void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) {
if (inst && inst->Opcode() != opcode) {
throw LogicError("Invalid pseudo-instruction");
}
}
static void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) {
if (dest_inst) {
throw LogicError("Only one of each type of pseudo-op allowed");
}
dest_inst = pseudo_inst;
}
static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) {
if (inst->Opcode() != expected_opcode) {
throw LogicError("Undoing use of invalid pseudo-op");
}
inst = nullptr;
}
Inst::Inst(IR::Opcode op_, u32 flags_) noexcept : op{op_}, flags{flags_} {
2021-02-06 05:38:22 +00:00
if (op == Opcode::Phi) {
std::construct_at(&phi_args);
} else {
std::construct_at(&args);
}
}
Inst::~Inst() {
if (op == Opcode::Phi) {
std::destroy_at(&phi_args);
} else {
std::destroy_at(&args);
}
}
2021-01-09 06:30:07 +00:00
bool Inst::MayHaveSideEffects() const noexcept {
switch (op) {
2021-02-03 00:07:00 +00:00
case Opcode::Branch:
case Opcode::BranchConditional:
case Opcode::LoopMerge:
case Opcode::SelectionMerge:
2021-02-03 00:07:00 +00:00
case Opcode::Return:
2021-01-09 06:30:07 +00:00
case Opcode::SetAttribute:
case Opcode::SetAttributeIndexed:
case Opcode::WriteGlobalU8:
case Opcode::WriteGlobalS8:
case Opcode::WriteGlobalU16:
case Opcode::WriteGlobalS16:
case Opcode::WriteGlobal32:
case Opcode::WriteGlobal64:
case Opcode::WriteGlobal128:
case Opcode::WriteStorageU8:
case Opcode::WriteStorageS8:
case Opcode::WriteStorageU16:
case Opcode::WriteStorageS16:
case Opcode::WriteStorage32:
case Opcode::WriteStorage64:
case Opcode::WriteStorage128:
2021-01-09 06:30:07 +00:00
return true;
default:
return false;
}
}
bool Inst::IsPseudoInstruction() const noexcept {
switch (op) {
case Opcode::GetZeroFromOp:
case Opcode::GetSignFromOp:
case Opcode::GetCarryFromOp:
case Opcode::GetOverflowFromOp:
return true;
default:
return false;
}
}
2021-02-06 05:38:22 +00:00
bool Inst::AreAllArgsImmediates() const {
if (op == Opcode::Phi) {
throw LogicError("Testing for all arguments are immediates on phi instruction");
}
return std::all_of(args.begin(), args.begin() + NumArgs(),
[](const IR::Value& value) { return value.IsImmediate(); });
}
2021-01-09 06:30:07 +00:00
bool Inst::HasAssociatedPseudoOperation() const noexcept {
return zero_inst || sign_inst || carry_inst || overflow_inst;
2021-01-09 06:30:07 +00:00
}
Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
// This is faster than doing a search through the block.
switch (opcode) {
case Opcode::GetZeroFromOp:
CheckPseudoInstruction(zero_inst, Opcode::GetZeroFromOp);
return zero_inst;
case Opcode::GetSignFromOp:
CheckPseudoInstruction(sign_inst, Opcode::GetSignFromOp);
return sign_inst;
case Opcode::GetCarryFromOp:
CheckPseudoInstruction(carry_inst, Opcode::GetCarryFromOp);
return carry_inst;
case Opcode::GetOverflowFromOp:
CheckPseudoInstruction(overflow_inst, Opcode::GetOverflowFromOp);
return overflow_inst;
default:
throw InvalidArgument("{} is not a pseudo-instruction", opcode);
}
}
size_t Inst::NumArgs() const {
2021-02-06 05:38:22 +00:00
return op == Opcode::Phi ? phi_args.size() : NumArgsOf(op);
2021-01-09 06:30:07 +00:00
}
IR::Type Inst::Type() const {
return TypeOf(op);
}
Value Inst::Arg(size_t index) const {
2021-02-06 05:38:22 +00:00
if (op == Opcode::Phi) {
if (index >= phi_args.size()) {
throw InvalidArgument("Out of bounds argument index {} in phi instruction", index);
}
return phi_args[index].second;
} else {
if (index >= NumArgsOf(op)) {
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
}
return args[index];
2021-01-09 06:30:07 +00:00
}
}
void Inst::SetArg(size_t index, Value value) {
2021-02-14 04:24:32 +00:00
if (index >= NumArgs()) {
2021-01-09 06:30:07 +00:00
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
}
2021-02-14 04:24:32 +00:00
const IR::Value arg{Arg(index)};
if (!arg.IsImmediate()) {
UndoUse(arg);
2021-01-09 06:30:07 +00:00
}
if (!value.IsImmediate()) {
Use(value);
}
2021-02-14 04:24:32 +00:00
if (op == Opcode::Phi) {
phi_args[index].second = value;
} else {
args[index] = value;
}
2021-01-09 06:30:07 +00:00
}
2021-02-06 05:38:22 +00:00
Block* Inst::PhiBlock(size_t index) const {
if (op != Opcode::Phi) {
throw LogicError("{} is not a Phi instruction", op);
}
if (index >= phi_args.size()) {
throw InvalidArgument("Out of bounds argument index {} in phi instruction");
}
return phi_args[index].first;
2021-02-03 00:07:00 +00:00
}
void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
if (!value.IsImmediate()) {
Use(value);
}
2021-02-06 05:38:22 +00:00
phi_args.emplace_back(predecessor, value);
2021-02-03 00:07:00 +00:00
}
2021-01-09 06:30:07 +00:00
void Inst::Invalidate() {
ClearArgs();
op = Opcode::Void;
}
void Inst::ClearArgs() {
2021-02-06 05:38:22 +00:00
if (op == Opcode::Phi) {
for (auto& pair : phi_args) {
IR::Value& value{pair.second};
if (!value.IsImmediate()) {
UndoUse(value);
}
2021-01-09 06:30:07 +00:00
}
2021-02-06 05:38:22 +00:00
phi_args.clear();
} else {
for (auto& value : args) {
if (!value.IsImmediate()) {
UndoUse(value);
}
value = {};
2021-02-03 00:07:00 +00:00
}
}
2021-01-09 06:30:07 +00:00
}
void Inst::ReplaceUsesWith(Value replacement) {
Invalidate();
op = Opcode::Identity;
if (!replacement.IsImmediate()) {
Use(replacement);
}
2021-02-06 05:38:22 +00:00
if (op == Opcode::Phi) {
phi_args[0].second = replacement;
} else {
args[0] = replacement;
}
2021-01-09 06:30:07 +00:00
}
2021-02-19 21:10:18 +00:00
void Inst::ReplaceOpcode(IR::Opcode opcode) {
op = opcode;
}
2021-01-09 06:30:07 +00:00
void Inst::Use(const Value& value) {
2021-02-06 05:38:22 +00:00
Inst* const inst{value.Inst()};
++inst->use_count;
2021-01-09 06:30:07 +00:00
switch (op) {
case Opcode::GetZeroFromOp:
2021-02-06 05:38:22 +00:00
SetPseudoInstruction(inst->zero_inst, this);
2021-01-09 06:30:07 +00:00
break;
case Opcode::GetSignFromOp:
2021-02-06 05:38:22 +00:00
SetPseudoInstruction(inst->sign_inst, this);
2021-01-09 06:30:07 +00:00
break;
case Opcode::GetCarryFromOp:
2021-02-06 05:38:22 +00:00
SetPseudoInstruction(inst->carry_inst, this);
2021-01-09 06:30:07 +00:00
break;
case Opcode::GetOverflowFromOp:
2021-02-06 05:38:22 +00:00
SetPseudoInstruction(inst->overflow_inst, this);
2021-01-09 06:30:07 +00:00
break;
default:
break;
}
}
void Inst::UndoUse(const Value& value) {
2021-02-06 05:38:22 +00:00
Inst* const inst{value.Inst()};
--inst->use_count;
2021-01-09 06:30:07 +00:00
switch (op) {
case Opcode::GetZeroFromOp:
2021-02-06 05:38:22 +00:00
RemovePseudoInstruction(inst->zero_inst, Opcode::GetZeroFromOp);
2021-01-09 06:30:07 +00:00
break;
case Opcode::GetSignFromOp:
2021-02-06 05:38:22 +00:00
RemovePseudoInstruction(inst->sign_inst, Opcode::GetSignFromOp);
2021-01-09 06:30:07 +00:00
break;
case Opcode::GetCarryFromOp:
2021-02-06 05:38:22 +00:00
RemovePseudoInstruction(inst->carry_inst, Opcode::GetCarryFromOp);
2021-01-09 06:30:07 +00:00
break;
case Opcode::GetOverflowFromOp:
2021-02-06 05:38:22 +00:00
RemovePseudoInstruction(inst->overflow_inst, Opcode::GetOverflowFromOp);
2021-01-09 06:30:07 +00:00
break;
default:
break;
}
}
} // namespace Shader::IR