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

377 lines
12 KiB
C++
Raw Normal View History

2021-01-09 01:30:07 -05: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 00:38:22 -05:00
#include <memory>
2021-01-09 01:30:07 -05:00
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/type.h"
#include "shader_recompiler/frontend/ir/value.h"
2021-01-09 01:30:07 -05:00
namespace Shader::IR {
namespace {
void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) {
if (inst && inst->GetOpcode() != opcode) {
2021-01-09 01:30:07 -05:00
throw LogicError("Invalid pseudo-instruction");
}
}
void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) {
2021-01-09 01:30:07 -05:00
if (dest_inst) {
throw LogicError("Only one of each type of pseudo-op allowed");
}
dest_inst = pseudo_inst;
}
void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) {
if (inst->GetOpcode() != expected_opcode) {
2021-01-09 01:30:07 -05:00
throw LogicError("Undoing use of invalid pseudo-op");
}
inst = nullptr;
}
void AllocAssociatedInsts(std::unique_ptr<AssociatedInsts>& associated_insts) {
if (!associated_insts) {
associated_insts = std::make_unique<AssociatedInsts>();
}
}
} // Anonymous namespace
2021-01-09 01:30:07 -05:00
Inst::Inst(IR::Opcode op_, u32 flags_) noexcept : op{op_}, flags{flags_} {
2021-02-06 00:38:22 -05: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 01:30:07 -05:00
bool Inst::MayHaveSideEffects() const noexcept {
switch (op) {
2021-02-02 19:07:00 -05:00
case Opcode::Branch:
case Opcode::BranchConditional:
case Opcode::LoopMerge:
case Opcode::SelectionMerge:
2021-02-02 19:07:00 -05:00
case Opcode::Return:
2021-03-27 17:30:24 -04:00
case Opcode::Unreachable:
case Opcode::DemoteToHelperInvocation:
case Opcode::Barrier:
2021-04-17 02:21:03 -04:00
case Opcode::WorkgroupMemoryBarrier:
case Opcode::DeviceMemoryBarrier:
case Opcode::Prologue:
case Opcode::Epilogue:
2021-04-12 02:48:15 -04:00
case Opcode::EmitVertex:
case Opcode::EndPrimitive:
2021-01-09 01:30:07 -05:00
case Opcode::SetAttribute:
case Opcode::SetAttributeIndexed:
case Opcode::SetPatch:
case Opcode::SetFragColor:
2021-04-16 17:47:26 -04:00
case Opcode::SetSampleMask:
case Opcode::SetFragDepth:
2021-01-09 01:30:07 -05:00
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:
case Opcode::WriteLocal:
case Opcode::WriteSharedU8:
case Opcode::WriteSharedU16:
case Opcode::WriteSharedU32:
case Opcode::WriteSharedU64:
case Opcode::WriteSharedU128:
2021-04-11 02:07:02 -04:00
case Opcode::SharedAtomicIAdd32:
case Opcode::SharedAtomicSMin32:
case Opcode::SharedAtomicUMin32:
case Opcode::SharedAtomicSMax32:
case Opcode::SharedAtomicUMax32:
case Opcode::SharedAtomicInc32:
case Opcode::SharedAtomicDec32:
case Opcode::SharedAtomicAnd32:
case Opcode::SharedAtomicOr32:
case Opcode::SharedAtomicXor32:
case Opcode::SharedAtomicExchange32:
case Opcode::SharedAtomicExchange64:
case Opcode::GlobalAtomicIAdd32:
case Opcode::GlobalAtomicSMin32:
case Opcode::GlobalAtomicUMin32:
case Opcode::GlobalAtomicSMax32:
case Opcode::GlobalAtomicUMax32:
case Opcode::GlobalAtomicInc32:
case Opcode::GlobalAtomicDec32:
case Opcode::GlobalAtomicAnd32:
case Opcode::GlobalAtomicOr32:
case Opcode::GlobalAtomicXor32:
case Opcode::GlobalAtomicExchange32:
case Opcode::GlobalAtomicIAdd64:
case Opcode::GlobalAtomicSMin64:
case Opcode::GlobalAtomicUMin64:
case Opcode::GlobalAtomicSMax64:
case Opcode::GlobalAtomicUMax64:
case Opcode::GlobalAtomicAnd64:
case Opcode::GlobalAtomicOr64:
case Opcode::GlobalAtomicXor64:
case Opcode::GlobalAtomicExchange64:
case Opcode::GlobalAtomicAddF32:
case Opcode::GlobalAtomicAddF16x2:
case Opcode::GlobalAtomicAddF32x2:
case Opcode::GlobalAtomicMinF16x2:
case Opcode::GlobalAtomicMinF32x2:
case Opcode::GlobalAtomicMaxF16x2:
case Opcode::GlobalAtomicMaxF32x2:
case Opcode::StorageAtomicIAdd32:
case Opcode::StorageAtomicSMin32:
case Opcode::StorageAtomicUMin32:
case Opcode::StorageAtomicSMax32:
case Opcode::StorageAtomicUMax32:
case Opcode::StorageAtomicInc32:
case Opcode::StorageAtomicDec32:
case Opcode::StorageAtomicAnd32:
case Opcode::StorageAtomicOr32:
case Opcode::StorageAtomicXor32:
case Opcode::StorageAtomicExchange32:
case Opcode::StorageAtomicIAdd64:
case Opcode::StorageAtomicSMin64:
case Opcode::StorageAtomicUMin64:
case Opcode::StorageAtomicSMax64:
case Opcode::StorageAtomicUMax64:
case Opcode::StorageAtomicAnd64:
case Opcode::StorageAtomicOr64:
case Opcode::StorageAtomicXor64:
case Opcode::StorageAtomicExchange64:
case Opcode::StorageAtomicAddF32:
case Opcode::StorageAtomicAddF16x2:
case Opcode::StorageAtomicAddF32x2:
case Opcode::StorageAtomicMinF16x2:
case Opcode::StorageAtomicMinF32x2:
case Opcode::StorageAtomicMaxF16x2:
case Opcode::StorageAtomicMaxF32x2:
case Opcode::BindlessImageWrite:
case Opcode::BoundImageWrite:
case Opcode::ImageWrite:
2021-01-09 01:30:07 -05: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:
case Opcode::GetSparseFromOp:
2021-03-25 11:31:37 -04:00
case Opcode::GetInBoundsFromOp:
2021-01-09 01:30:07 -05:00
return true;
default:
return false;
}
}
2021-02-06 00:38:22 -05: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 01:30:07 -05:00
Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
if (!associated_insts) {
return nullptr;
}
2021-01-09 01:30:07 -05:00
switch (opcode) {
case Opcode::GetZeroFromOp:
CheckPseudoInstruction(associated_insts->zero_inst, Opcode::GetZeroFromOp);
return associated_insts->zero_inst;
2021-01-09 01:30:07 -05:00
case Opcode::GetSignFromOp:
CheckPseudoInstruction(associated_insts->sign_inst, Opcode::GetSignFromOp);
return associated_insts->sign_inst;
2021-01-09 01:30:07 -05:00
case Opcode::GetCarryFromOp:
CheckPseudoInstruction(associated_insts->carry_inst, Opcode::GetCarryFromOp);
return associated_insts->carry_inst;
2021-01-09 01:30:07 -05:00
case Opcode::GetOverflowFromOp:
CheckPseudoInstruction(associated_insts->overflow_inst, Opcode::GetOverflowFromOp);
return associated_insts->overflow_inst;
case Opcode::GetSparseFromOp:
CheckPseudoInstruction(associated_insts->sparse_inst, Opcode::GetSparseFromOp);
return associated_insts->sparse_inst;
2021-03-25 11:31:37 -04:00
case Opcode::GetInBoundsFromOp:
CheckPseudoInstruction(associated_insts->in_bounds_inst, Opcode::GetInBoundsFromOp);
return associated_insts->in_bounds_inst;
2021-01-09 01:30:07 -05:00
default:
throw InvalidArgument("{} is not a pseudo-instruction", opcode);
}
}
IR::Type Inst::Type() const {
return TypeOf(op);
}
void Inst::SetArg(size_t index, Value value) {
2021-02-13 23:24:32 -05:00
if (index >= NumArgs()) {
2021-01-09 01:30:07 -05:00
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
}
2021-02-13 23:24:32 -05:00
const IR::Value arg{Arg(index)};
if (!arg.IsImmediate()) {
UndoUse(arg);
2021-01-09 01:30:07 -05:00
}
if (!value.IsImmediate()) {
Use(value);
}
2021-02-13 23:24:32 -05:00
if (op == Opcode::Phi) {
phi_args[index].second = value;
} else {
args[index] = value;
}
2021-01-09 01:30:07 -05:00
}
2021-02-06 00:38:22 -05: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-02 19:07:00 -05:00
}
void Inst::AddPhiOperand(Block* predecessor, const Value& value) {
if (!value.IsImmediate()) {
Use(value);
}
2021-02-06 00:38:22 -05:00
phi_args.emplace_back(predecessor, value);
2021-02-02 19:07:00 -05:00
}
2021-01-09 01:30:07 -05:00
void Inst::Invalidate() {
ClearArgs();
ReplaceOpcode(Opcode::Void);
2021-01-09 01:30:07 -05:00
}
void Inst::ClearArgs() {
2021-02-06 00:38:22 -05:00
if (op == Opcode::Phi) {
for (auto& pair : phi_args) {
IR::Value& value{pair.second};
if (!value.IsImmediate()) {
UndoUse(value);
}
2021-01-09 01:30:07 -05:00
}
2021-02-06 00:38:22 -05:00
phi_args.clear();
} else {
for (auto& value : args) {
if (!value.IsImmediate()) {
UndoUse(value);
}
2021-02-02 19:07:00 -05:00
}
// Reset arguments to null
// std::memset was measured to be faster on MSVC than std::ranges:fill
std::memset(&args, 0, sizeof(args));
2021-02-02 19:07:00 -05:00
}
2021-01-09 01:30:07 -05:00
}
void Inst::ReplaceUsesWith(Value replacement) {
Invalidate();
ReplaceOpcode(Opcode::Identity);
2021-01-09 01:30:07 -05:00
if (!replacement.IsImmediate()) {
Use(replacement);
}
args[0] = replacement;
2021-01-09 01:30:07 -05:00
}
2021-02-19 16:10:18 -05:00
void Inst::ReplaceOpcode(IR::Opcode opcode) {
if (opcode == IR::Opcode::Phi) {
throw LogicError("Cannot transition into Phi");
}
if (op == Opcode::Phi) {
// Transition out of phi arguments into non-phi
std::destroy_at(&phi_args);
std::construct_at(&args);
}
2021-02-19 16:10:18 -05:00
op = opcode;
}
2021-01-09 01:30:07 -05:00
void Inst::Use(const Value& value) {
2021-02-06 00:38:22 -05:00
Inst* const inst{value.Inst()};
++inst->use_count;
2021-01-09 01:30:07 -05:00
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
2021-01-09 01:30:07 -05:00
switch (op) {
case Opcode::GetZeroFromOp:
AllocAssociatedInsts(assoc_inst);
SetPseudoInstruction(assoc_inst->zero_inst, this);
2021-01-09 01:30:07 -05:00
break;
case Opcode::GetSignFromOp:
AllocAssociatedInsts(assoc_inst);
SetPseudoInstruction(assoc_inst->sign_inst, this);
2021-01-09 01:30:07 -05:00
break;
case Opcode::GetCarryFromOp:
AllocAssociatedInsts(assoc_inst);
SetPseudoInstruction(assoc_inst->carry_inst, this);
2021-01-09 01:30:07 -05:00
break;
case Opcode::GetOverflowFromOp:
AllocAssociatedInsts(assoc_inst);
SetPseudoInstruction(assoc_inst->overflow_inst, this);
break;
case Opcode::GetSparseFromOp:
AllocAssociatedInsts(assoc_inst);
SetPseudoInstruction(assoc_inst->sparse_inst, this);
2021-01-09 01:30:07 -05:00
break;
2021-03-25 11:31:37 -04:00
case Opcode::GetInBoundsFromOp:
AllocAssociatedInsts(assoc_inst);
SetPseudoInstruction(assoc_inst->in_bounds_inst, this);
break;
2021-01-09 01:30:07 -05:00
default:
break;
}
}
void Inst::UndoUse(const Value& value) {
2021-02-06 00:38:22 -05:00
Inst* const inst{value.Inst()};
--inst->use_count;
2021-01-09 01:30:07 -05:00
std::unique_ptr<AssociatedInsts>& assoc_inst{inst->associated_insts};
2021-01-09 01:30:07 -05:00
switch (op) {
case Opcode::GetZeroFromOp:
AllocAssociatedInsts(assoc_inst);
RemovePseudoInstruction(assoc_inst->zero_inst, Opcode::GetZeroFromOp);
2021-01-09 01:30:07 -05:00
break;
case Opcode::GetSignFromOp:
AllocAssociatedInsts(assoc_inst);
RemovePseudoInstruction(assoc_inst->sign_inst, Opcode::GetSignFromOp);
2021-01-09 01:30:07 -05:00
break;
case Opcode::GetCarryFromOp:
AllocAssociatedInsts(assoc_inst);
RemovePseudoInstruction(assoc_inst->carry_inst, Opcode::GetCarryFromOp);
2021-01-09 01:30:07 -05:00
break;
case Opcode::GetOverflowFromOp:
AllocAssociatedInsts(assoc_inst);
RemovePseudoInstruction(assoc_inst->overflow_inst, Opcode::GetOverflowFromOp);
2021-01-09 01:30:07 -05:00
break;
2021-03-25 11:31:37 -04:00
case Opcode::GetInBoundsFromOp:
AllocAssociatedInsts(assoc_inst);
RemovePseudoInstruction(assoc_inst->in_bounds_inst, Opcode::GetInBoundsFromOp);
break;
2021-01-09 01:30:07 -05:00
default:
break;
}
}
} // namespace Shader::IR