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,447 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/attribute.h"
namespace Shader::IR {
bool IsGeneric(Attribute attribute) noexcept {
return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
}
int GenericAttributeIndex(Attribute attribute) {
if (!IsGeneric(attribute)) {
throw InvalidArgument("Attribute is not generic {}", attribute);
}
return (static_cast<int>(attribute) - static_cast<int>(Attribute::Generic0X)) / 4;
}
std::string NameOf(Attribute attribute) {
switch (attribute) {
case Attribute::PrimitiveId:
return "PrimitiveId";
case Attribute::Layer:
return "Layer";
case Attribute::ViewportIndex:
return "ViewportIndex";
case Attribute::PointSize:
return "PointSize";
case Attribute::PositionX:
return "Position.X";
case Attribute::PositionY:
return "Position.Y";
case Attribute::PositionZ:
return "Position.Z";
case Attribute::PositionW:
return "Position.W";
case Attribute::Generic0X:
return "Generic[0].X";
case Attribute::Generic0Y:
return "Generic[0].Y";
case Attribute::Generic0Z:
return "Generic[0].Z";
case Attribute::Generic0W:
return "Generic[0].W";
case Attribute::Generic1X:
return "Generic[1].X";
case Attribute::Generic1Y:
return "Generic[1].Y";
case Attribute::Generic1Z:
return "Generic[1].Z";
case Attribute::Generic1W:
return "Generic[1].W";
case Attribute::Generic2X:
return "Generic[2].X";
case Attribute::Generic2Y:
return "Generic[2].Y";
case Attribute::Generic2Z:
return "Generic[2].Z";
case Attribute::Generic2W:
return "Generic[2].W";
case Attribute::Generic3X:
return "Generic[3].X";
case Attribute::Generic3Y:
return "Generic[3].Y";
case Attribute::Generic3Z:
return "Generic[3].Z";
case Attribute::Generic3W:
return "Generic[3].W";
case Attribute::Generic4X:
return "Generic[4].X";
case Attribute::Generic4Y:
return "Generic[4].Y";
case Attribute::Generic4Z:
return "Generic[4].Z";
case Attribute::Generic4W:
return "Generic[4].W";
case Attribute::Generic5X:
return "Generic[5].X";
case Attribute::Generic5Y:
return "Generic[5].Y";
case Attribute::Generic5Z:
return "Generic[5].Z";
case Attribute::Generic5W:
return "Generic[5].W";
case Attribute::Generic6X:
return "Generic[6].X";
case Attribute::Generic6Y:
return "Generic[6].Y";
case Attribute::Generic6Z:
return "Generic[6].Z";
case Attribute::Generic6W:
return "Generic[6].W";
case Attribute::Generic7X:
return "Generic[7].X";
case Attribute::Generic7Y:
return "Generic[7].Y";
case Attribute::Generic7Z:
return "Generic[7].Z";
case Attribute::Generic7W:
return "Generic[7].W";
case Attribute::Generic8X:
return "Generic[8].X";
case Attribute::Generic8Y:
return "Generic[8].Y";
case Attribute::Generic8Z:
return "Generic[8].Z";
case Attribute::Generic8W:
return "Generic[8].W";
case Attribute::Generic9X:
return "Generic[9].X";
case Attribute::Generic9Y:
return "Generic[9].Y";
case Attribute::Generic9Z:
return "Generic[9].Z";
case Attribute::Generic9W:
return "Generic[9].W";
case Attribute::Generic10X:
return "Generic[10].X";
case Attribute::Generic10Y:
return "Generic[10].Y";
case Attribute::Generic10Z:
return "Generic[10].Z";
case Attribute::Generic10W:
return "Generic[10].W";
case Attribute::Generic11X:
return "Generic[11].X";
case Attribute::Generic11Y:
return "Generic[11].Y";
case Attribute::Generic11Z:
return "Generic[11].Z";
case Attribute::Generic11W:
return "Generic[11].W";
case Attribute::Generic12X:
return "Generic[12].X";
case Attribute::Generic12Y:
return "Generic[12].Y";
case Attribute::Generic12Z:
return "Generic[12].Z";
case Attribute::Generic12W:
return "Generic[12].W";
case Attribute::Generic13X:
return "Generic[13].X";
case Attribute::Generic13Y:
return "Generic[13].Y";
case Attribute::Generic13Z:
return "Generic[13].Z";
case Attribute::Generic13W:
return "Generic[13].W";
case Attribute::Generic14X:
return "Generic[14].X";
case Attribute::Generic14Y:
return "Generic[14].Y";
case Attribute::Generic14Z:
return "Generic[14].Z";
case Attribute::Generic14W:
return "Generic[14].W";
case Attribute::Generic15X:
return "Generic[15].X";
case Attribute::Generic15Y:
return "Generic[15].Y";
case Attribute::Generic15Z:
return "Generic[15].Z";
case Attribute::Generic15W:
return "Generic[15].W";
case Attribute::Generic16X:
return "Generic[16].X";
case Attribute::Generic16Y:
return "Generic[16].Y";
case Attribute::Generic16Z:
return "Generic[16].Z";
case Attribute::Generic16W:
return "Generic[16].W";
case Attribute::Generic17X:
return "Generic[17].X";
case Attribute::Generic17Y:
return "Generic[17].Y";
case Attribute::Generic17Z:
return "Generic[17].Z";
case Attribute::Generic17W:
return "Generic[17].W";
case Attribute::Generic18X:
return "Generic[18].X";
case Attribute::Generic18Y:
return "Generic[18].Y";
case Attribute::Generic18Z:
return "Generic[18].Z";
case Attribute::Generic18W:
return "Generic[18].W";
case Attribute::Generic19X:
return "Generic[19].X";
case Attribute::Generic19Y:
return "Generic[19].Y";
case Attribute::Generic19Z:
return "Generic[19].Z";
case Attribute::Generic19W:
return "Generic[19].W";
case Attribute::Generic20X:
return "Generic[20].X";
case Attribute::Generic20Y:
return "Generic[20].Y";
case Attribute::Generic20Z:
return "Generic[20].Z";
case Attribute::Generic20W:
return "Generic[20].W";
case Attribute::Generic21X:
return "Generic[21].X";
case Attribute::Generic21Y:
return "Generic[21].Y";
case Attribute::Generic21Z:
return "Generic[21].Z";
case Attribute::Generic21W:
return "Generic[21].W";
case Attribute::Generic22X:
return "Generic[22].X";
case Attribute::Generic22Y:
return "Generic[22].Y";
case Attribute::Generic22Z:
return "Generic[22].Z";
case Attribute::Generic22W:
return "Generic[22].W";
case Attribute::Generic23X:
return "Generic[23].X";
case Attribute::Generic23Y:
return "Generic[23].Y";
case Attribute::Generic23Z:
return "Generic[23].Z";
case Attribute::Generic23W:
return "Generic[23].W";
case Attribute::Generic24X:
return "Generic[24].X";
case Attribute::Generic24Y:
return "Generic[24].Y";
case Attribute::Generic24Z:
return "Generic[24].Z";
case Attribute::Generic24W:
return "Generic[24].W";
case Attribute::Generic25X:
return "Generic[25].X";
case Attribute::Generic25Y:
return "Generic[25].Y";
case Attribute::Generic25Z:
return "Generic[25].Z";
case Attribute::Generic25W:
return "Generic[25].W";
case Attribute::Generic26X:
return "Generic[26].X";
case Attribute::Generic26Y:
return "Generic[26].Y";
case Attribute::Generic26Z:
return "Generic[26].Z";
case Attribute::Generic26W:
return "Generic[26].W";
case Attribute::Generic27X:
return "Generic[27].X";
case Attribute::Generic27Y:
return "Generic[27].Y";
case Attribute::Generic27Z:
return "Generic[27].Z";
case Attribute::Generic27W:
return "Generic[27].W";
case Attribute::Generic28X:
return "Generic[28].X";
case Attribute::Generic28Y:
return "Generic[28].Y";
case Attribute::Generic28Z:
return "Generic[28].Z";
case Attribute::Generic28W:
return "Generic[28].W";
case Attribute::Generic29X:
return "Generic[29].X";
case Attribute::Generic29Y:
return "Generic[29].Y";
case Attribute::Generic29Z:
return "Generic[29].Z";
case Attribute::Generic29W:
return "Generic[29].W";
case Attribute::Generic30X:
return "Generic[30].X";
case Attribute::Generic30Y:
return "Generic[30].Y";
case Attribute::Generic30Z:
return "Generic[30].Z";
case Attribute::Generic30W:
return "Generic[30].W";
case Attribute::Generic31X:
return "Generic[31].X";
case Attribute::Generic31Y:
return "Generic[31].Y";
case Attribute::Generic31Z:
return "Generic[31].Z";
case Attribute::Generic31W:
return "Generic[31].W";
case Attribute::ColorFrontDiffuseR:
return "ColorFrontDiffuse.R";
case Attribute::ColorFrontDiffuseG:
return "ColorFrontDiffuse.G";
case Attribute::ColorFrontDiffuseB:
return "ColorFrontDiffuse.B";
case Attribute::ColorFrontDiffuseA:
return "ColorFrontDiffuse.A";
case Attribute::ColorFrontSpecularR:
return "ColorFrontSpecular.R";
case Attribute::ColorFrontSpecularG:
return "ColorFrontSpecular.G";
case Attribute::ColorFrontSpecularB:
return "ColorFrontSpecular.B";
case Attribute::ColorFrontSpecularA:
return "ColorFrontSpecular.A";
case Attribute::ColorBackDiffuseR:
return "ColorBackDiffuse.R";
case Attribute::ColorBackDiffuseG:
return "ColorBackDiffuse.G";
case Attribute::ColorBackDiffuseB:
return "ColorBackDiffuse.B";
case Attribute::ColorBackDiffuseA:
return "ColorBackDiffuse.A";
case Attribute::ColorBackSpecularR:
return "ColorBackSpecular.R";
case Attribute::ColorBackSpecularG:
return "ColorBackSpecular.G";
case Attribute::ColorBackSpecularB:
return "ColorBackSpecular.B";
case Attribute::ColorBackSpecularA:
return "ColorBackSpecular.A";
case Attribute::ClipDistance0:
return "ClipDistance[0]";
case Attribute::ClipDistance1:
return "ClipDistance[1]";
case Attribute::ClipDistance2:
return "ClipDistance[2]";
case Attribute::ClipDistance3:
return "ClipDistance[3]";
case Attribute::ClipDistance4:
return "ClipDistance[4]";
case Attribute::ClipDistance5:
return "ClipDistance[5]";
case Attribute::ClipDistance6:
return "ClipDistance[6]";
case Attribute::ClipDistance7:
return "ClipDistance[7]";
case Attribute::PointSpriteS:
return "PointSprite.S";
case Attribute::PointSpriteT:
return "PointSprite.T";
case Attribute::FogCoordinate:
return "FogCoordinate";
case Attribute::TessellationEvaluationPointU:
return "TessellationEvaluationPoint.U";
case Attribute::TessellationEvaluationPointV:
return "TessellationEvaluationPoint.V";
case Attribute::InstanceId:
return "InstanceId";
case Attribute::VertexId:
return "VertexId";
case Attribute::FixedFncTexture0S:
return "FixedFncTexture[0].S";
case Attribute::FixedFncTexture0T:
return "FixedFncTexture[0].T";
case Attribute::FixedFncTexture0R:
return "FixedFncTexture[0].R";
case Attribute::FixedFncTexture0Q:
return "FixedFncTexture[0].Q";
case Attribute::FixedFncTexture1S:
return "FixedFncTexture[1].S";
case Attribute::FixedFncTexture1T:
return "FixedFncTexture[1].T";
case Attribute::FixedFncTexture1R:
return "FixedFncTexture[1].R";
case Attribute::FixedFncTexture1Q:
return "FixedFncTexture[1].Q";
case Attribute::FixedFncTexture2S:
return "FixedFncTexture[2].S";
case Attribute::FixedFncTexture2T:
return "FixedFncTexture[2].T";
case Attribute::FixedFncTexture2R:
return "FixedFncTexture[2].R";
case Attribute::FixedFncTexture2Q:
return "FixedFncTexture[2].Q";
case Attribute::FixedFncTexture3S:
return "FixedFncTexture[3].S";
case Attribute::FixedFncTexture3T:
return "FixedFncTexture[3].T";
case Attribute::FixedFncTexture3R:
return "FixedFncTexture[3].R";
case Attribute::FixedFncTexture3Q:
return "FixedFncTexture[3].Q";
case Attribute::FixedFncTexture4S:
return "FixedFncTexture[4].S";
case Attribute::FixedFncTexture4T:
return "FixedFncTexture[4].T";
case Attribute::FixedFncTexture4R:
return "FixedFncTexture[4].R";
case Attribute::FixedFncTexture4Q:
return "FixedFncTexture[4].Q";
case Attribute::FixedFncTexture5S:
return "FixedFncTexture[5].S";
case Attribute::FixedFncTexture5T:
return "FixedFncTexture[5].T";
case Attribute::FixedFncTexture5R:
return "FixedFncTexture[5].R";
case Attribute::FixedFncTexture5Q:
return "FixedFncTexture[5].Q";
case Attribute::FixedFncTexture6S:
return "FixedFncTexture[6].S";
case Attribute::FixedFncTexture6T:
return "FixedFncTexture[6].T";
case Attribute::FixedFncTexture6R:
return "FixedFncTexture[6].R";
case Attribute::FixedFncTexture6Q:
return "FixedFncTexture[6].Q";
case Attribute::FixedFncTexture7S:
return "FixedFncTexture[7].S";
case Attribute::FixedFncTexture7T:
return "FixedFncTexture[7].T";
case Attribute::FixedFncTexture7R:
return "FixedFncTexture[7].R";
case Attribute::FixedFncTexture7Q:
return "FixedFncTexture[7].Q";
case Attribute::FixedFncTexture8S:
return "FixedFncTexture[8].S";
case Attribute::FixedFncTexture8T:
return "FixedFncTexture[8].T";
case Attribute::FixedFncTexture8R:
return "FixedFncTexture[8].R";
case Attribute::FixedFncTexture8Q:
return "FixedFncTexture[8].Q";
case Attribute::FixedFncTexture9S:
return "FixedFncTexture[9].S";
case Attribute::FixedFncTexture9T:
return "FixedFncTexture[9].T";
case Attribute::FixedFncTexture9R:
return "FixedFncTexture[9].R";
case Attribute::FixedFncTexture9Q:
return "FixedFncTexture[9].Q";
case Attribute::ViewportMask:
return "ViewportMask";
case Attribute::FrontFace:
return "FrontFace";
}
return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
}
} // namespace Shader::IR

View file

@ -0,0 +1,242 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <fmt/format.h>
#include "common/common_types.h"
namespace Shader::IR {
enum class Attribute : u64 {
PrimitiveId = 24,
Layer = 25,
ViewportIndex = 26,
PointSize = 27,
PositionX = 28,
PositionY = 29,
PositionZ = 30,
PositionW = 31,
Generic0X = 32,
Generic0Y = 33,
Generic0Z = 34,
Generic0W = 35,
Generic1X = 36,
Generic1Y = 37,
Generic1Z = 38,
Generic1W = 39,
Generic2X = 40,
Generic2Y = 41,
Generic2Z = 42,
Generic2W = 43,
Generic3X = 44,
Generic3Y = 45,
Generic3Z = 46,
Generic3W = 47,
Generic4X = 48,
Generic4Y = 49,
Generic4Z = 50,
Generic4W = 51,
Generic5X = 52,
Generic5Y = 53,
Generic5Z = 54,
Generic5W = 55,
Generic6X = 56,
Generic6Y = 57,
Generic6Z = 58,
Generic6W = 59,
Generic7X = 60,
Generic7Y = 61,
Generic7Z = 62,
Generic7W = 63,
Generic8X = 64,
Generic8Y = 65,
Generic8Z = 66,
Generic8W = 67,
Generic9X = 68,
Generic9Y = 69,
Generic9Z = 70,
Generic9W = 71,
Generic10X = 72,
Generic10Y = 73,
Generic10Z = 74,
Generic10W = 75,
Generic11X = 76,
Generic11Y = 77,
Generic11Z = 78,
Generic11W = 79,
Generic12X = 80,
Generic12Y = 81,
Generic12Z = 82,
Generic12W = 83,
Generic13X = 84,
Generic13Y = 85,
Generic13Z = 86,
Generic13W = 87,
Generic14X = 88,
Generic14Y = 89,
Generic14Z = 90,
Generic14W = 91,
Generic15X = 92,
Generic15Y = 93,
Generic15Z = 94,
Generic15W = 95,
Generic16X = 96,
Generic16Y = 97,
Generic16Z = 98,
Generic16W = 99,
Generic17X = 100,
Generic17Y = 101,
Generic17Z = 102,
Generic17W = 103,
Generic18X = 104,
Generic18Y = 105,
Generic18Z = 106,
Generic18W = 107,
Generic19X = 108,
Generic19Y = 109,
Generic19Z = 110,
Generic19W = 111,
Generic20X = 112,
Generic20Y = 113,
Generic20Z = 114,
Generic20W = 115,
Generic21X = 116,
Generic21Y = 117,
Generic21Z = 118,
Generic21W = 119,
Generic22X = 120,
Generic22Y = 121,
Generic22Z = 122,
Generic22W = 123,
Generic23X = 124,
Generic23Y = 125,
Generic23Z = 126,
Generic23W = 127,
Generic24X = 128,
Generic24Y = 129,
Generic24Z = 130,
Generic24W = 131,
Generic25X = 132,
Generic25Y = 133,
Generic25Z = 134,
Generic25W = 135,
Generic26X = 136,
Generic26Y = 137,
Generic26Z = 138,
Generic26W = 139,
Generic27X = 140,
Generic27Y = 141,
Generic27Z = 142,
Generic27W = 143,
Generic28X = 144,
Generic28Y = 145,
Generic28Z = 146,
Generic28W = 147,
Generic29X = 148,
Generic29Y = 149,
Generic29Z = 150,
Generic29W = 151,
Generic30X = 152,
Generic30Y = 153,
Generic30Z = 154,
Generic30W = 155,
Generic31X = 156,
Generic31Y = 157,
Generic31Z = 158,
Generic31W = 159,
ColorFrontDiffuseR = 160,
ColorFrontDiffuseG = 161,
ColorFrontDiffuseB = 162,
ColorFrontDiffuseA = 163,
ColorFrontSpecularR = 164,
ColorFrontSpecularG = 165,
ColorFrontSpecularB = 166,
ColorFrontSpecularA = 167,
ColorBackDiffuseR = 168,
ColorBackDiffuseG = 169,
ColorBackDiffuseB = 170,
ColorBackDiffuseA = 171,
ColorBackSpecularR = 172,
ColorBackSpecularG = 173,
ColorBackSpecularB = 174,
ColorBackSpecularA = 175,
ClipDistance0 = 176,
ClipDistance1 = 177,
ClipDistance2 = 178,
ClipDistance3 = 179,
ClipDistance4 = 180,
ClipDistance5 = 181,
ClipDistance6 = 182,
ClipDistance7 = 183,
PointSpriteS = 184,
PointSpriteT = 185,
FogCoordinate = 186,
TessellationEvaluationPointU = 188,
TessellationEvaluationPointV = 189,
InstanceId = 190,
VertexId = 191,
FixedFncTexture0S = 192,
FixedFncTexture0T = 193,
FixedFncTexture0R = 194,
FixedFncTexture0Q = 195,
FixedFncTexture1S = 196,
FixedFncTexture1T = 197,
FixedFncTexture1R = 198,
FixedFncTexture1Q = 199,
FixedFncTexture2S = 200,
FixedFncTexture2T = 201,
FixedFncTexture2R = 202,
FixedFncTexture2Q = 203,
FixedFncTexture3S = 204,
FixedFncTexture3T = 205,
FixedFncTexture3R = 206,
FixedFncTexture3Q = 207,
FixedFncTexture4S = 208,
FixedFncTexture4T = 209,
FixedFncTexture4R = 210,
FixedFncTexture4Q = 211,
FixedFncTexture5S = 212,
FixedFncTexture5T = 213,
FixedFncTexture5R = 214,
FixedFncTexture5Q = 215,
FixedFncTexture6S = 216,
FixedFncTexture6T = 217,
FixedFncTexture6R = 218,
FixedFncTexture6Q = 219,
FixedFncTexture7S = 220,
FixedFncTexture7T = 221,
FixedFncTexture7R = 222,
FixedFncTexture7Q = 223,
FixedFncTexture8S = 224,
FixedFncTexture8T = 225,
FixedFncTexture8R = 226,
FixedFncTexture8Q = 227,
FixedFncTexture9S = 228,
FixedFncTexture9T = 229,
FixedFncTexture9R = 230,
FixedFncTexture9Q = 231,
ViewportMask = 232,
FrontFace = 255,
};
[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
[[nodiscard]] int GenericAttributeIndex(Attribute attribute);
[[nodiscard]] std::string NameOf(Attribute attribute);
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Attribute> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) {
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute));
}
};

View file

@ -0,0 +1,142 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <initializer_list>
#include <map>
#include <memory>
#include "common/bit_cast.h"
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::IR {
Block::Block(u32 begin, u32 end) : location_begin{begin}, location_end{end} {}
Block::~Block() = default;
void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
PrependNewInst(end(), op, args);
}
Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
std::initializer_list<Value> args) {
Inst* const inst{std::construct_at(instruction_alloc_pool.allocate(), op)};
const auto result_it{instructions.insert(insertion_point, *inst)};
if (inst->NumArgs() != args.size()) {
throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op);
}
std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable {
inst->SetArg(index, arg);
++index;
});
return result_it;
}
u32 Block::LocationBegin() const noexcept {
return location_begin;
}
u32 Block::LocationEnd() const noexcept {
return location_end;
}
Block::InstructionList& Block::Instructions() noexcept {
return instructions;
}
const Block::InstructionList& Block::Instructions() const noexcept {
return instructions;
}
static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index,
const std::map<const Inst*, size_t>& inst_to_index,
const Value& arg) {
if (arg.IsEmpty()) {
return "<null>";
}
if (arg.IsLabel()) {
if (const auto it{block_to_index.find(arg.Label())}; it != block_to_index.end()) {
return fmt::format("{{Block ${}}}", it->second);
}
return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(arg.Label()));
}
if (!arg.IsImmediate()) {
if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) {
return fmt::format("%{}", it->second);
}
return fmt::format("%<unknown inst {:016x}>", reinterpret_cast<u64>(arg.Inst()));
}
switch (arg.Type()) {
case Type::U1:
return fmt::format("#{}", arg.U1() ? '1' : '0');
case Type::U8:
return fmt::format("#{}", arg.U8());
case Type::U16:
return fmt::format("#{}", arg.U16());
case Type::U32:
return fmt::format("#{}", arg.U32());
case Type::U64:
return fmt::format("#{}", arg.U64());
case Type::Reg:
return fmt::format("{}", arg.Reg());
case Type::Pred:
return fmt::format("{}", arg.Pred());
case Type::Attribute:
return fmt::format("{}", arg.Attribute());
default:
return "<unknown immediate type>";
}
}
std::string DumpBlock(const Block& block) {
size_t inst_index{0};
std::map<const Inst*, size_t> inst_to_index;
return DumpBlock(block, {}, inst_to_index, inst_index);
}
std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index,
std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) {
std::string ret{"Block"};
if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
ret += fmt::format(" ${}", it->second);
}
ret += fmt::format(": begin={:04x} end={:04x}\n", block.LocationBegin(), block.LocationEnd());
for (const Inst& inst : block) {
const Opcode op{inst.Opcode()};
ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
if (TypeOf(op) != Type::Void) {
ret += fmt::format("%{:<5} = {}", inst_index, op);
} else {
ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
}
const size_t arg_count{NumArgsOf(op)};
for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
const Value arg{inst.Arg(arg_index)};
ret += arg_index != 0 ? ", " : " ";
ret += ArgToIndex(block_to_index, inst_to_index, arg);
const Type actual_type{arg.Type()};
const Type expected_type{ArgTypeOf(op, arg_index)};
if (!AreTypesCompatible(actual_type, expected_type)) {
ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
}
}
if (TypeOf(op) != Type::Void) {
ret += fmt::format(" (uses: {})\n", inst.UseCount());
} else {
ret += '\n';
}
inst_to_index.emplace(&inst, inst_index);
++inst_index;
}
return ret;
}
} // namespace Shader::IR

View file

@ -0,0 +1,134 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <initializer_list>
#include <map>
#include <boost/intrusive/list.hpp>
#include <boost/pool/pool_alloc.hpp>
#include "shader_recompiler/frontend/ir/microinstruction.h"
namespace Shader::IR {
class Block {
public:
using InstructionList = boost::intrusive::list<Inst>;
using size_type = InstructionList::size_type;
using iterator = InstructionList::iterator;
using const_iterator = InstructionList::const_iterator;
using reverse_iterator = InstructionList::reverse_iterator;
using const_reverse_iterator = InstructionList::const_reverse_iterator;
explicit Block(u32 begin, u32 end);
~Block();
Block(const Block&) = delete;
Block& operator=(const Block&) = delete;
Block(Block&&) = default;
Block& operator=(Block&&) = default;
/// Appends a new instruction to the end of this basic block.
void AppendNewInst(Opcode op, std::initializer_list<Value> args);
/// Prepends a new instruction to this basic block before the insertion point.
iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args);
/// Gets the starting location of this basic block.
[[nodiscard]] u32 LocationBegin() const noexcept;
/// Gets the end location for this basic block.
[[nodiscard]] u32 LocationEnd() const noexcept;
/// Gets a mutable reference to the instruction list for this basic block.
InstructionList& Instructions() noexcept;
/// Gets an immutable reference to the instruction list for this basic block.
const InstructionList& Instructions() const noexcept;
[[nodiscard]] bool empty() const {
return instructions.empty();
}
[[nodiscard]] size_type size() const {
return instructions.size();
}
[[nodiscard]] Inst& front() {
return instructions.front();
}
[[nodiscard]] const Inst& front() const {
return instructions.front();
}
[[nodiscard]] Inst& back() {
return instructions.back();
}
[[nodiscard]] const Inst& back() const {
return instructions.back();
}
[[nodiscard]] iterator begin() {
return instructions.begin();
}
[[nodiscard]] const_iterator begin() const {
return instructions.begin();
}
[[nodiscard]] iterator end() {
return instructions.end();
}
[[nodiscard]] const_iterator end() const {
return instructions.end();
}
[[nodiscard]] reverse_iterator rbegin() {
return instructions.rbegin();
}
[[nodiscard]] const_reverse_iterator rbegin() const {
return instructions.rbegin();
}
[[nodiscard]] reverse_iterator rend() {
return instructions.rend();
}
[[nodiscard]] const_reverse_iterator rend() const {
return instructions.rend();
}
[[nodiscard]] const_iterator cbegin() const {
return instructions.cbegin();
}
[[nodiscard]] const_iterator cend() const {
return instructions.cend();
}
[[nodiscard]] const_reverse_iterator crbegin() const {
return instructions.crbegin();
}
[[nodiscard]] const_reverse_iterator crend() const {
return instructions.crend();
}
private:
/// Starting location of this block
u32 location_begin;
/// End location of this block
u32 location_end;
/// List of instructions in this block.
InstructionList instructions;
/// Memory pool for instruction list
boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free,
boost::details::pool::null_mutex>
instruction_alloc_pool;
};
[[nodiscard]] std::string DumpBlock(const Block& block);
[[nodiscard]] std::string DumpBlock(const Block& block,
const std::map<const Block*, size_t>& block_to_index,
std::map<const Inst*, size_t>& inst_to_index,
size_t& inst_index);
} // namespace Shader::IR

View file

@ -0,0 +1,31 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <fmt/format.h>
#include "shader_recompiler/frontend/ir/condition.h"
namespace Shader::IR {
std::string NameOf(Condition condition) {
std::string ret;
if (condition.FlowTest() != FlowTest::T) {
ret = fmt::to_string(condition.FlowTest());
}
const auto [pred, negated]{condition.Pred()};
if (pred != Pred::PT || negated) {
if (!ret.empty()) {
ret += '&';
}
if (negated) {
ret += '!';
}
ret += fmt::to_string(pred);
}
return ret;
}
} // namespace Shader::IR

View file

@ -0,0 +1,60 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <compare>
#include <fmt/format.h>
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/flow_test.h"
#include "shader_recompiler/frontend/ir/pred.h"
namespace Shader::IR {
class Condition {
public:
Condition() noexcept = default;
explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept
: flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)},
pred_negated{pred_negated_ ? u8{1} : u8{0}} {}
explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept
: Condition(FlowTest::T, pred_, pred_negated_) {}
Condition(bool value) : Condition(Pred::PT, !value) {}
auto operator<=>(const Condition&) const noexcept = default;
[[nodiscard]] IR::FlowTest FlowTest() const noexcept {
return static_cast<IR::FlowTest>(flow_test);
}
[[nodiscard]] std::pair<IR::Pred, bool> Pred() const noexcept {
return {static_cast<IR::Pred>(pred), pred_negated != 0};
}
private:
u16 flow_test;
u8 pred;
u8 pred_negated;
};
std::string NameOf(Condition condition);
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Condition> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::Condition& cond, FormatContext& ctx) {
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond));
}
};

View file

@ -0,0 +1,83 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <fmt/format.h>
#include "shader_recompiler/frontend/ir/flow_test.h"
namespace Shader::IR {
std::string NameOf(FlowTest flow_test) {
switch (flow_test) {
case FlowTest::F:
return "F";
case FlowTest::LT:
return "LT";
case FlowTest::EQ:
return "EQ";
case FlowTest::LE:
return "LE";
case FlowTest::GT:
return "GT";
case FlowTest::NE:
return "NE";
case FlowTest::GE:
return "GE";
case FlowTest::NUM:
return "NUM";
case FlowTest::NaN:
return "NAN";
case FlowTest::LTU:
return "LTU";
case FlowTest::EQU:
return "EQU";
case FlowTest::LEU:
return "LEU";
case FlowTest::GTU:
return "GTU";
case FlowTest::NEU:
return "NEU";
case FlowTest::GEU:
return "GEU";
case FlowTest::T:
return "T";
case FlowTest::OFF:
return "OFF";
case FlowTest::LO:
return "LO";
case FlowTest::SFF:
return "SFF";
case FlowTest::LS:
return "LS";
case FlowTest::HI:
return "HI";
case FlowTest::SFT:
return "SFT";
case FlowTest::HS:
return "HS";
case FlowTest::OFT:
return "OFT";
case FlowTest::CSM_TA:
return "CSM_TA";
case FlowTest::CSM_TR:
return "CSM_TR";
case FlowTest::CSM_MX:
return "CSM_MX";
case FlowTest::FCSM_TA:
return "FCSM_TA";
case FlowTest::FCSM_TR:
return "FCSM_TR";
case FlowTest::FCSM_MX:
return "FCSM_MX";
case FlowTest::RLE:
return "RLE";
case FlowTest::RGT:
return "RGT";
}
return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
}
} // namespace Shader::IR

View file

@ -0,0 +1,61 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <fmt/format.h>
namespace Shader::IR {
enum class FlowTest {
F,
LT,
EQ,
LE,
GT,
NE,
GE,
NUM,
NaN,
LTU,
EQU,
LEU,
GTU,
NEU,
GEU,
T,
OFF,
LO,
SFF,
LS,
HI,
SFT,
HS,
OFT,
CSM_TA,
CSM_TR,
CSM_MX,
FCSM_TA,
FCSM_TR,
FCSM_MX,
RLE,
RGT,
};
[[nodiscard]] std::string NameOf(FlowTest flow_test);
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::FlowTest> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) {
return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test));
}
};

View file

@ -0,0 +1,533 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/bit_cast.h"
#include "shader_recompiler/frontend/ir/ir_emitter.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::IR {
[[noreturn]] static void ThrowInvalidType(Type type) {
throw InvalidArgument("Invalid type {}", type);
}
U1 IREmitter::Imm1(bool value) const {
return U1{Value{value}};
}
U8 IREmitter::Imm8(u8 value) const {
return U8{Value{value}};
}
U16 IREmitter::Imm16(u16 value) const {
return U16{Value{value}};
}
U32 IREmitter::Imm32(u32 value) const {
return U32{Value{value}};
}
U32 IREmitter::Imm32(s32 value) const {
return U32{Value{static_cast<u32>(value)}};
}
U32 IREmitter::Imm32(f32 value) const {
return U32{Value{Common::BitCast<u32>(value)}};
}
U64 IREmitter::Imm64(u64 value) const {
return U64{Value{value}};
}
U64 IREmitter::Imm64(f64 value) const {
return U64{Value{Common::BitCast<u64>(value)}};
}
void IREmitter::Branch(IR::Block* label) {
Inst(Opcode::Branch, label);
}
void IREmitter::BranchConditional(const U1& cond, IR::Block* true_label, IR::Block* false_label) {
Inst(Opcode::BranchConditional, cond, true_label, false_label);
}
void IREmitter::Exit() {
Inst(Opcode::Exit);
}
void IREmitter::Return() {
Inst(Opcode::Return);
}
void IREmitter::Unreachable() {
Inst(Opcode::Unreachable);
}
U32 IREmitter::GetReg(IR::Reg reg) {
return Inst<U32>(Opcode::GetRegister, reg);
}
void IREmitter::SetReg(IR::Reg reg, const U32& value) {
Inst(Opcode::SetRegister, reg, value);
}
U1 IREmitter::GetPred(IR::Pred pred, bool is_negated) {
const U1 value{Inst<U1>(Opcode::GetPred, pred)};
if (is_negated) {
return Inst<U1>(Opcode::LogicalNot, value);
} else {
return value;
}
}
void IREmitter::SetPred(IR::Pred pred, const U1& value) {
Inst(Opcode::SetPred, pred, value);
}
U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) {
return Inst<U32>(Opcode::GetCbuf, binding, byte_offset);
}
U1 IREmitter::GetZFlag() {
return Inst<U1>(Opcode::GetZFlag);
}
U1 IREmitter::GetSFlag() {
return Inst<U1>(Opcode::GetSFlag);
}
U1 IREmitter::GetCFlag() {
return Inst<U1>(Opcode::GetCFlag);
}
U1 IREmitter::GetOFlag() {
return Inst<U1>(Opcode::GetOFlag);
}
void IREmitter::SetZFlag(const U1& value) {
Inst(Opcode::SetZFlag, value);
}
void IREmitter::SetSFlag(const U1& value) {
Inst(Opcode::SetSFlag, value);
}
void IREmitter::SetCFlag(const U1& value) {
Inst(Opcode::SetCFlag, value);
}
void IREmitter::SetOFlag(const U1& value) {
Inst(Opcode::SetOFlag, value);
}
U32 IREmitter::GetAttribute(IR::Attribute attribute) {
return Inst<U32>(Opcode::GetAttribute, attribute);
}
void IREmitter::SetAttribute(IR::Attribute attribute, const U32& value) {
Inst(Opcode::SetAttribute, attribute, value);
}
void IREmitter::WriteGlobalU8(const U64& address, const U32& value) {
Inst(Opcode::WriteGlobalU8, address, value);
}
void IREmitter::WriteGlobalS8(const U64& address, const U32& value) {
Inst(Opcode::WriteGlobalS8, address, value);
}
void IREmitter::WriteGlobalU16(const U64& address, const U32& value) {
Inst(Opcode::WriteGlobalU16, address, value);
}
void IREmitter::WriteGlobalS16(const U64& address, const U32& value) {
Inst(Opcode::WriteGlobalS16, address, value);
}
void IREmitter::WriteGlobal32(const U64& address, const U32& value) {
Inst(Opcode::WriteGlobal32, address, value);
}
void IREmitter::WriteGlobal64(const U64& address, const IR::Value& vector) {
Inst(Opcode::WriteGlobal64, address, vector);
}
void IREmitter::WriteGlobal128(const U64& address, const IR::Value& vector) {
Inst(Opcode::WriteGlobal128, address, vector);
}
U1 IREmitter::GetZeroFromOp(const Value& op) {
return Inst<U1>(Opcode::GetZeroFromOp, op);
}
U1 IREmitter::GetSignFromOp(const Value& op) {
return Inst<U1>(Opcode::GetSignFromOp, op);
}
U1 IREmitter::GetCarryFromOp(const Value& op) {
return Inst<U1>(Opcode::GetCarryFromOp, op);
}
U1 IREmitter::GetOverflowFromOp(const Value& op) {
return Inst<U1>(Opcode::GetOverflowFromOp, op);
}
U16U32U64 IREmitter::FPAdd(const U16U32U64& a, const U16U32U64& b) {
if (a.Type() != a.Type()) {
throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type());
}
switch (a.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPAdd16, a, b);
case Type::U32:
return Inst<U32>(Opcode::FPAdd32, a, b);
case Type::U64:
return Inst<U64>(Opcode::FPAdd64, a, b);
default:
ThrowInvalidType(a.Type());
}
}
Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2) {
if (e1.Type() != e2.Type()) {
throw InvalidArgument("Incompatible types {} {}", e1.Type(), e2.Type());
}
return Inst(Opcode::CompositeConstruct2, e1, e2);
}
Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3) {
if (e1.Type() != e2.Type() || e1.Type() != e3.Type()) {
throw InvalidArgument("Incompatible types {} {} {}", e1.Type(), e2.Type(), e3.Type());
}
return Inst(Opcode::CompositeConstruct3, e1, e2, e3);
}
Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3,
const UAny& e4) {
if (e1.Type() != e2.Type() || e1.Type() != e3.Type() || e1.Type() != e4.Type()) {
throw InvalidArgument("Incompatible types {} {} {}", e1.Type(), e2.Type(), e3.Type(),
e4.Type());
}
return Inst(Opcode::CompositeConstruct4, e1, e2, e3, e4);
}
UAny IREmitter::CompositeExtract(const Value& vector, size_t element) {
if (element >= 4) {
throw InvalidArgument("Out of bounds element {}", element);
}
return Inst<UAny>(Opcode::CompositeExtract, vector, Imm32(static_cast<u32>(element)));
}
U64 IREmitter::PackUint2x32(const Value& vector) {
return Inst<U64>(Opcode::PackUint2x32, vector);
}
Value IREmitter::UnpackUint2x32(const U64& value) {
return Inst<Value>(Opcode::UnpackUint2x32, value);
}
U32 IREmitter::PackFloat2x16(const Value& vector) {
return Inst<U32>(Opcode::PackFloat2x16, vector);
}
Value IREmitter::UnpackFloat2x16(const U32& value) {
return Inst<Value>(Opcode::UnpackFloat2x16, value);
}
U64 IREmitter::PackDouble2x32(const Value& vector) {
return Inst<U64>(Opcode::PackDouble2x32, vector);
}
Value IREmitter::UnpackDouble2x32(const U64& value) {
return Inst<Value>(Opcode::UnpackDouble2x32, value);
}
U16U32U64 IREmitter::FPMul(const U16U32U64& a, const U16U32U64& b) {
if (a.Type() != b.Type()) {
throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type());
}
switch (a.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPMul16, a, b);
case Type::U32:
return Inst<U32>(Opcode::FPMul32, a, b);
case Type::U64:
return Inst<U64>(Opcode::FPMul64, a, b);
default:
ThrowInvalidType(a.Type());
}
}
U16U32U64 IREmitter::FPAbs(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPAbs16, value);
case Type::U32:
return Inst<U32>(Opcode::FPAbs32, value);
case Type::U64:
return Inst<U64>(Opcode::FPAbs64, value);
default:
ThrowInvalidType(value.Type());
}
}
U16U32U64 IREmitter::FPNeg(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPNeg16, value);
case Type::U32:
return Inst<U32>(Opcode::FPNeg32, value);
case Type::U64:
return Inst<U64>(Opcode::FPNeg64, value);
default:
ThrowInvalidType(value.Type());
}
}
U16U32U64 IREmitter::FPAbsNeg(const U16U32U64& value, bool abs, bool neg) {
U16U32U64 result{value};
if (abs) {
result = FPAbs(value);
}
if (neg) {
result = FPNeg(value);
}
return result;
}
U32 IREmitter::FPCosNotReduced(const U32& value) {
return Inst<U32>(Opcode::FPCosNotReduced, value);
}
U32 IREmitter::FPExp2NotReduced(const U32& value) {
return Inst<U32>(Opcode::FPExp2NotReduced, value);
}
U32 IREmitter::FPLog2(const U32& value) {
return Inst<U32>(Opcode::FPLog2, value);
}
U32U64 IREmitter::FPRecip(const U32U64& value) {
switch (value.Type()) {
case Type::U32:
return Inst<U32>(Opcode::FPRecip32, value);
case Type::U64:
return Inst<U64>(Opcode::FPRecip64, value);
default:
ThrowInvalidType(value.Type());
}
}
U32U64 IREmitter::FPRecipSqrt(const U32U64& value) {
switch (value.Type()) {
case Type::U32:
return Inst<U32>(Opcode::FPRecipSqrt32, value);
case Type::U64:
return Inst<U64>(Opcode::FPRecipSqrt64, value);
default:
ThrowInvalidType(value.Type());
}
}
U32 IREmitter::FPSinNotReduced(const U32& value) {
return Inst<U32>(Opcode::FPSinNotReduced, value);
}
U32 IREmitter::FPSqrt(const U32& value) {
return Inst<U32>(Opcode::FPSqrt, value);
}
U16U32U64 IREmitter::FPSaturate(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPSaturate16, value);
case Type::U32:
return Inst<U32>(Opcode::FPSaturate32, value);
case Type::U64:
return Inst<U64>(Opcode::FPSaturate64, value);
default:
ThrowInvalidType(value.Type());
}
}
U16U32U64 IREmitter::FPRoundEven(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPRoundEven16, value);
case Type::U32:
return Inst<U32>(Opcode::FPRoundEven32, value);
case Type::U64:
return Inst<U64>(Opcode::FPRoundEven64, value);
default:
ThrowInvalidType(value.Type());
}
}
U16U32U64 IREmitter::FPFloor(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPFloor16, value);
case Type::U32:
return Inst<U32>(Opcode::FPFloor32, value);
case Type::U64:
return Inst<U64>(Opcode::FPFloor64, value);
default:
ThrowInvalidType(value.Type());
}
}
U16U32U64 IREmitter::FPCeil(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPCeil16, value);
case Type::U32:
return Inst<U32>(Opcode::FPCeil32, value);
case Type::U64:
return Inst<U64>(Opcode::FPCeil64, value);
default:
ThrowInvalidType(value.Type());
}
}
U16U32U64 IREmitter::FPTrunc(const U16U32U64& value) {
switch (value.Type()) {
case Type::U16:
return Inst<U16>(Opcode::FPTrunc16, value);
case Type::U32:
return Inst<U32>(Opcode::FPTrunc32, value);
case Type::U64:
return Inst<U64>(Opcode::FPTrunc64, value);
default:
ThrowInvalidType(value.Type());
}
}
U1 IREmitter::LogicalOr(const U1& a, const U1& b) {
return Inst<U1>(Opcode::LogicalOr, a, b);
}
U1 IREmitter::LogicalAnd(const U1& a, const U1& b) {
return Inst<U1>(Opcode::LogicalAnd, a, b);
}
U1 IREmitter::LogicalNot(const U1& value) {
return Inst<U1>(Opcode::LogicalNot, value);
}
U32U64 IREmitter::ConvertFToS(size_t bitsize, const U16U32U64& value) {
switch (bitsize) {
case 16:
switch (value.Type()) {
case Type::U16:
return Inst<U32>(Opcode::ConvertS16F16, value);
case Type::U32:
return Inst<U32>(Opcode::ConvertS16F32, value);
case Type::U64:
return Inst<U32>(Opcode::ConvertS16F64, value);
default:
ThrowInvalidType(value.Type());
}
case 32:
switch (value.Type()) {
case Type::U16:
return Inst<U32>(Opcode::ConvertS32F16, value);
case Type::U32:
return Inst<U32>(Opcode::ConvertS32F32, value);
case Type::U64:
return Inst<U32>(Opcode::ConvertS32F64, value);
default:
ThrowInvalidType(value.Type());
}
case 64:
switch (value.Type()) {
case Type::U16:
return Inst<U64>(Opcode::ConvertS64F16, value);
case Type::U32:
return Inst<U64>(Opcode::ConvertS64F32, value);
case Type::U64:
return Inst<U64>(Opcode::ConvertS64F64, value);
default:
ThrowInvalidType(value.Type());
}
default:
throw InvalidArgument("Invalid destination bitsize {}", bitsize);
}
}
U32U64 IREmitter::ConvertFToU(size_t bitsize, const U16U32U64& value) {
switch (bitsize) {
case 16:
switch (value.Type()) {
case Type::U16:
return Inst<U32>(Opcode::ConvertU16F16, value);
case Type::U32:
return Inst<U32>(Opcode::ConvertU16F32, value);
case Type::U64:
return Inst<U32>(Opcode::ConvertU16F64, value);
default:
ThrowInvalidType(value.Type());
}
case 32:
switch (value.Type()) {
case Type::U16:
return Inst<U32>(Opcode::ConvertU32F16, value);
case Type::U32:
return Inst<U32>(Opcode::ConvertU32F32, value);
case Type::U64:
return Inst<U32>(Opcode::ConvertU32F64, value);
default:
ThrowInvalidType(value.Type());
}
case 64:
switch (value.Type()) {
case Type::U16:
return Inst<U64>(Opcode::ConvertU64F16, value);
case Type::U32:
return Inst<U64>(Opcode::ConvertU64F32, value);
case Type::U64:
return Inst<U64>(Opcode::ConvertU64F64, value);
default:
ThrowInvalidType(value.Type());
}
default:
throw InvalidArgument("Invalid destination bitsize {}", bitsize);
}
}
U32U64 IREmitter::ConvertFToI(size_t bitsize, bool is_signed, const U16U32U64& value) {
if (is_signed) {
return ConvertFToS(bitsize, value);
} else {
return ConvertFToU(bitsize, value);
}
}
U32U64 IREmitter::ConvertU(size_t bitsize, const U32U64& value) {
switch (bitsize) {
case 32:
switch (value.Type()) {
case Type::U32:
// Nothing to do
return value;
case Type::U64:
return Inst<U32>(Opcode::ConvertU32U64, value);
default:
break;
}
break;
case 64:
switch (value.Type()) {
case Type::U32:
// Nothing to do
return value;
case Type::U64:
return Inst<U64>(Opcode::ConvertU64U32, value);
default:
break;
}
}
throw NotImplementedException("Conversion from {} to {} bits", value.Type(), bitsize);
}
} // namespace Shader::IR

View file

@ -0,0 +1,123 @@
// 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/attribute.h"
#include "shader_recompiler/frontend/ir/basic_block.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::IR {
class IREmitter {
public:
explicit IREmitter(Block& block_) : block{block_}, insertion_point{block.end()} {}
Block& block;
[[nodiscard]] U1 Imm1(bool value) const;
[[nodiscard]] U8 Imm8(u8 value) const;
[[nodiscard]] U16 Imm16(u16 value) const;
[[nodiscard]] U32 Imm32(u32 value) const;
[[nodiscard]] U32 Imm32(s32 value) const;
[[nodiscard]] U32 Imm32(f32 value) const;
[[nodiscard]] U64 Imm64(u64 value) const;
[[nodiscard]] U64 Imm64(f64 value) const;
void Branch(IR::Block* label);
void BranchConditional(const U1& cond, IR::Block* true_label, IR::Block* false_label);
void Exit();
void Return();
void Unreachable();
[[nodiscard]] U32 GetReg(IR::Reg reg);
void SetReg(IR::Reg reg, const U32& value);
[[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false);
void SetPred(IR::Pred pred, const U1& value);
[[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset);
[[nodiscard]] U1 GetZFlag();
[[nodiscard]] U1 GetSFlag();
[[nodiscard]] U1 GetCFlag();
[[nodiscard]] U1 GetOFlag();
void SetZFlag(const U1& value);
void SetSFlag(const U1& value);
void SetCFlag(const U1& value);
void SetOFlag(const U1& value);
[[nodiscard]] U32 GetAttribute(IR::Attribute attribute);
void SetAttribute(IR::Attribute attribute, const U32& value);
void WriteGlobalU8(const U64& address, const U32& value);
void WriteGlobalS8(const U64& address, const U32& value);
void WriteGlobalU16(const U64& address, const U32& value);
void WriteGlobalS16(const U64& address, const U32& value);
void WriteGlobal32(const U64& address, const U32& value);
void WriteGlobal64(const U64& address, const IR::Value& vector);
void WriteGlobal128(const U64& address, const IR::Value& vector);
[[nodiscard]] U1 GetZeroFromOp(const Value& op);
[[nodiscard]] U1 GetSignFromOp(const Value& op);
[[nodiscard]] U1 GetCarryFromOp(const Value& op);
[[nodiscard]] U1 GetOverflowFromOp(const Value& op);
[[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2);
[[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3);
[[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3,
const UAny& e4);
[[nodiscard]] UAny CompositeExtract(const Value& vector, size_t element);
[[nodiscard]] U64 PackUint2x32(const Value& vector);
[[nodiscard]] Value UnpackUint2x32(const U64& value);
[[nodiscard]] U32 PackFloat2x16(const Value& vector);
[[nodiscard]] Value UnpackFloat2x16(const U32& value);
[[nodiscard]] U64 PackDouble2x32(const Value& vector);
[[nodiscard]] Value UnpackDouble2x32(const U64& value);
[[nodiscard]] U16U32U64 FPAdd(const U16U32U64& a, const U16U32U64& b);
[[nodiscard]] U16U32U64 FPMul(const U16U32U64& a, const U16U32U64& b);
[[nodiscard]] U16U32U64 FPAbs(const U16U32U64& value);
[[nodiscard]] U16U32U64 FPNeg(const U16U32U64& value);
[[nodiscard]] U16U32U64 FPAbsNeg(const U16U32U64& value, bool abs, bool neg);
[[nodiscard]] U32 FPCosNotReduced(const U32& value);
[[nodiscard]] U32 FPExp2NotReduced(const U32& value);
[[nodiscard]] U32 FPLog2(const U32& value);
[[nodiscard]] U32U64 FPRecip(const U32U64& value);
[[nodiscard]] U32U64 FPRecipSqrt(const U32U64& value);
[[nodiscard]] U32 FPSinNotReduced(const U32& value);
[[nodiscard]] U32 FPSqrt(const U32& value);
[[nodiscard]] U16U32U64 FPSaturate(const U16U32U64& value);
[[nodiscard]] U16U32U64 FPRoundEven(const U16U32U64& value);
[[nodiscard]] U16U32U64 FPFloor(const U16U32U64& value);
[[nodiscard]] U16U32U64 FPCeil(const U16U32U64& value);
[[nodiscard]] U16U32U64 FPTrunc(const U16U32U64& value);
[[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
[[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);
[[nodiscard]] U1 LogicalNot(const U1& value);
[[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const U16U32U64& value);
[[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const U16U32U64& value);
[[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const U16U32U64& value);
[[nodiscard]] U32U64 ConvertU(size_t bitsize, const U32U64& value);
private:
IR::Block::iterator insertion_point;
template <typename T = Value, typename... Args>
T Inst(Opcode op, Args... args) {
auto it{block.PrependNewInst(insertion_point, op, {Value{args}...})};
return T{Value{&*it}};
}
};
} // namespace Shader::IR

View file

@ -0,0 +1,189 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#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;
}
bool Inst::MayHaveSideEffects() const noexcept {
switch (op) {
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:
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::GetZSCOFromOp:
return true;
default:
return false;
}
}
bool Inst::HasAssociatedPseudoOperation() const noexcept {
return zero_inst || sign_inst || carry_inst || overflow_inst || zsco_inst;
}
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;
case Opcode::GetZSCOFromOp:
CheckPseudoInstruction(zsco_inst, Opcode::GetZSCOFromOp);
return zsco_inst;
default:
throw InvalidArgument("{} is not a pseudo-instruction", opcode);
}
}
size_t Inst::NumArgs() const {
return NumArgsOf(op);
}
IR::Type Inst::Type() const {
return TypeOf(op);
}
Value Inst::Arg(size_t index) const {
if (index >= NumArgsOf(op)) {
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
}
return args[index];
}
void Inst::SetArg(size_t index, Value value) {
if (index >= NumArgsOf(op)) {
throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
}
if (!args[index].IsImmediate()) {
UndoUse(args[index]);
}
if (!value.IsImmediate()) {
Use(value);
}
args[index] = value;
}
void Inst::Invalidate() {
ClearArgs();
op = Opcode::Void;
}
void Inst::ClearArgs() {
for (auto& value : args) {
if (!value.IsImmediate()) {
UndoUse(value);
}
value = {};
}
}
void Inst::ReplaceUsesWith(Value replacement) {
Invalidate();
op = Opcode::Identity;
if (!replacement.IsImmediate()) {
Use(replacement);
}
args[0] = replacement;
}
void Inst::Use(const Value& value) {
++value.Inst()->use_count;
switch (op) {
case Opcode::GetZeroFromOp:
SetPseudoInstruction(value.Inst()->zero_inst, this);
break;
case Opcode::GetSignFromOp:
SetPseudoInstruction(value.Inst()->sign_inst, this);
break;
case Opcode::GetCarryFromOp:
SetPseudoInstruction(value.Inst()->carry_inst, this);
break;
case Opcode::GetOverflowFromOp:
SetPseudoInstruction(value.Inst()->overflow_inst, this);
break;
case Opcode::GetZSCOFromOp:
SetPseudoInstruction(value.Inst()->zsco_inst, this);
break;
default:
break;
}
}
void Inst::UndoUse(const Value& value) {
--value.Inst()->use_count;
switch (op) {
case Opcode::GetZeroFromOp:
RemovePseudoInstruction(value.Inst()->zero_inst, Opcode::GetZeroFromOp);
break;
case Opcode::GetSignFromOp:
RemovePseudoInstruction(value.Inst()->sign_inst, Opcode::GetSignFromOp);
break;
case Opcode::GetCarryFromOp:
RemovePseudoInstruction(value.Inst()->carry_inst, Opcode::GetCarryFromOp);
break;
case Opcode::GetOverflowFromOp:
RemovePseudoInstruction(value.Inst()->overflow_inst, Opcode::GetOverflowFromOp);
break;
case Opcode::GetZSCOFromOp:
RemovePseudoInstruction(value.Inst()->zsco_inst, Opcode::GetZSCOFromOp);
break;
default:
break;
}
}
} // namespace Shader::IR

View file

@ -0,0 +1,82 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <boost/intrusive/list.hpp>
#include "common/common_types.h"
#include "shader_recompiler/frontend/ir/opcode.h"
#include "shader_recompiler/frontend/ir/type.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::IR {
constexpr size_t MAX_ARG_COUNT = 4;
class Inst : public boost::intrusive::list_base_hook<> {
public:
explicit Inst(Opcode op_) noexcept : op(op_) {}
/// Get the number of uses this instruction has.
[[nodiscard]] int UseCount() const noexcept {
return use_count;
}
/// Determines whether this instruction has uses or not.
[[nodiscard]] bool HasUses() const noexcept {
return use_count > 0;
}
/// Get the opcode this microinstruction represents.
[[nodiscard]] IR::Opcode Opcode() const noexcept {
return op;
}
/// Determines whether or not this instruction may have side effects.
[[nodiscard]] bool MayHaveSideEffects() const noexcept;
/// Determines whether or not this instruction is a pseudo-instruction.
/// Pseudo-instructions depend on their parent instructions for their semantics.
[[nodiscard]] bool IsPseudoInstruction() const noexcept;
/// Determines if there is a pseudo-operation associated with this instruction.
[[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept;
/// Gets a pseudo-operation associated with this instruction
[[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode);
/// Get the number of arguments this instruction has.
[[nodiscard]] size_t NumArgs() const;
/// Get the type this instruction returns.
[[nodiscard]] IR::Type Type() const;
/// Get the value of a given argument index.
[[nodiscard]] Value Arg(size_t index) const;
/// Set the value of a given argument index.
void SetArg(size_t index, Value value);
void Invalidate();
void ClearArgs();
void ReplaceUsesWith(Value replacement);
private:
void Use(const Value& value);
void UndoUse(const Value& value);
IR::Opcode op{};
int use_count{};
std::array<Value, MAX_ARG_COUNT> args{};
Inst* zero_inst{};
Inst* sign_inst{};
Inst* carry_inst{};
Inst* overflow_inst{};
Inst* zsco_inst{};
u64 flags{};
};
} // namespace Shader::IR

View file

@ -0,0 +1,67 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <string_view>
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/opcode.h"
namespace Shader::IR {
namespace {
struct OpcodeMeta {
std::string_view name;
Type type;
std::array<Type, 4> arg_types;
};
using enum Type;
constexpr std::array META_TABLE{
#define OPCODE(name_token, type_token, ...) \
OpcodeMeta{ \
.name{#name_token}, \
.type{type_token}, \
.arg_types{__VA_ARGS__}, \
},
#include "opcode.inc"
#undef OPCODE
};
void ValidateOpcode(Opcode op) {
const size_t raw{static_cast<size_t>(op)};
if (raw >= META_TABLE.size()) {
throw InvalidArgument("Invalid opcode with raw value {}", raw);
}
}
} // Anonymous namespace
Type TypeOf(Opcode op) {
ValidateOpcode(op);
return META_TABLE[static_cast<size_t>(op)].type;
}
size_t NumArgsOf(Opcode op) {
ValidateOpcode(op);
const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
const auto distance{std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void))};
return static_cast<size_t>(distance);
}
Type ArgTypeOf(Opcode op, size_t arg_index) {
ValidateOpcode(op);
const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
if (arg_index >= arg_types.size() || arg_types[arg_index] == Type::Void) {
throw InvalidArgument("Out of bounds argument");
}
return arg_types[arg_index];
}
std::string_view NameOf(Opcode op) {
ValidateOpcode(op);
return META_TABLE[static_cast<size_t>(op)].name;
}
} // namespace Shader::IR

View file

@ -0,0 +1,44 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string_view>
#include <fmt/format.h>
#include "shader_recompiler/frontend/ir/type.h"
namespace Shader::IR {
enum class Opcode {
#define OPCODE(name, ...) name,
#include "opcode.inc"
#undef OPCODE
};
/// Get return type of an opcode
[[nodiscard]] Type TypeOf(Opcode op);
/// Get the number of arguments an opcode accepts
[[nodiscard]] size_t NumArgsOf(Opcode op);
/// Get the required type of an argument of an opcode
[[nodiscard]] Type ArgTypeOf(Opcode op, size_t arg_index);
/// Get the name of an opcode
[[nodiscard]] std::string_view NameOf(Opcode op);
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Opcode> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
return format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
}
};

View file

@ -0,0 +1,142 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ...
OPCODE(Void, Void, )
OPCODE(Identity, Opaque, Opaque, )
// Control flow
OPCODE(Branch, Void, Label, )
OPCODE(BranchConditional, Void, U1, Label, Label, )
OPCODE(Exit, Void, )
OPCODE(Return, Void, )
OPCODE(Unreachable, Void, )
// Context getters/setters
OPCODE(GetRegister, U32, Reg, )
OPCODE(SetRegister, Void, Reg, U32, )
OPCODE(GetPred, U1, Pred, )
OPCODE(SetPred, Void, Pred, U1, )
OPCODE(GetCbuf, U32, U32, U32, )
OPCODE(GetAttribute, U32, Attribute, )
OPCODE(SetAttribute, U32, Attribute, )
OPCODE(GetAttributeIndexed, U32, U32, )
OPCODE(SetAttributeIndexed, U32, U32, )
OPCODE(GetZSCORaw, U32, )
OPCODE(SetZSCORaw, Void, U32, )
OPCODE(SetZSCO, Void, ZSCO, )
OPCODE(GetZFlag, U1, Void, )
OPCODE(GetSFlag, U1, Void, )
OPCODE(GetCFlag, U1, Void, )
OPCODE(GetOFlag, U1, Void, )
OPCODE(SetZFlag, Void, U1, )
OPCODE(SetSFlag, Void, U1, )
OPCODE(SetCFlag, Void, U1, )
OPCODE(SetOFlag, Void, U1, )
// Memory operations
OPCODE(WriteGlobalU8, Void, U64, U32, )
OPCODE(WriteGlobalS8, Void, U64, U32, )
OPCODE(WriteGlobalU16, Void, U64, U32, )
OPCODE(WriteGlobalS16, Void, U64, U32, )
OPCODE(WriteGlobal32, Void, U64, U32, )
OPCODE(WriteGlobal64, Void, U64, Opaque, )
OPCODE(WriteGlobal128, Void, U64, Opaque, )
// Vector utility
OPCODE(CompositeConstruct2, Opaque, Opaque, Opaque, )
OPCODE(CompositeConstruct3, Opaque, Opaque, Opaque, Opaque, )
OPCODE(CompositeConstruct4, Opaque, Opaque, Opaque, Opaque, Opaque, )
OPCODE(CompositeExtract, Opaque, Opaque, U32, )
// Bitwise conversions
OPCODE(PackUint2x32, U64, Opaque, )
OPCODE(UnpackUint2x32, Opaque, U64, )
OPCODE(PackFloat2x16, U32, Opaque, )
OPCODE(UnpackFloat2x16, Opaque, U32, )
OPCODE(PackDouble2x32, U64, Opaque, )
OPCODE(UnpackDouble2x32, Opaque, U64, )
// Pseudo-operation, handled specially at final emit
OPCODE(GetZeroFromOp, U1, Opaque, )
OPCODE(GetSignFromOp, U1, Opaque, )
OPCODE(GetCarryFromOp, U1, Opaque, )
OPCODE(GetOverflowFromOp, U1, Opaque, )
OPCODE(GetZSCOFromOp, ZSCO, Opaque, )
// Floating-point operations
OPCODE(FPAbs16, U16, U16 )
OPCODE(FPAbs32, U32, U32 )
OPCODE(FPAbs64, U64, U64 )
OPCODE(FPAdd16, U16, U16, U16 )
OPCODE(FPAdd32, U32, U32, U32 )
OPCODE(FPAdd64, U64, U64, U64 )
OPCODE(FPFma16, U16, U16, U16 )
OPCODE(FPFma32, U32, U32, U32 )
OPCODE(FPFma64, U64, U64, U64 )
OPCODE(FPMax32, U32, U32, U32 )
OPCODE(FPMax64, U64, U64, U64 )
OPCODE(FPMin32, U32, U32, U32 )
OPCODE(FPMin64, U64, U64, U64 )
OPCODE(FPMul16, U16, U16, U16 )
OPCODE(FPMul32, U32, U32, U32 )
OPCODE(FPMul64, U64, U64, U64 )
OPCODE(FPNeg16, U16, U16 )
OPCODE(FPNeg32, U32, U32 )
OPCODE(FPNeg64, U64, U64 )
OPCODE(FPRecip32, U32, U32 )
OPCODE(FPRecip64, U64, U64 )
OPCODE(FPRecipSqrt32, U32, U32 )
OPCODE(FPRecipSqrt64, U64, U64 )
OPCODE(FPSqrt, U32, U32 )
OPCODE(FPSin, U32, U32 )
OPCODE(FPSinNotReduced, U32, U32 )
OPCODE(FPExp2, U32, U32 )
OPCODE(FPExp2NotReduced, U32, U32 )
OPCODE(FPCos, U32, U32 )
OPCODE(FPCosNotReduced, U32, U32 )
OPCODE(FPLog2, U32, U32 )
OPCODE(FPSaturate16, U16, U16 )
OPCODE(FPSaturate32, U32, U32 )
OPCODE(FPSaturate64, U64, U64 )
OPCODE(FPRoundEven16, U16, U16 )
OPCODE(FPRoundEven32, U32, U32 )
OPCODE(FPRoundEven64, U64, U64 )
OPCODE(FPFloor16, U16, U16 )
OPCODE(FPFloor32, U32, U32 )
OPCODE(FPFloor64, U64, U64 )
OPCODE(FPCeil16, U16, U16 )
OPCODE(FPCeil32, U32, U32 )
OPCODE(FPCeil64, U64, U64 )
OPCODE(FPTrunc16, U16, U16 )
OPCODE(FPTrunc32, U32, U32 )
OPCODE(FPTrunc64, U64, U64 )
// Logical operations
OPCODE(LogicalOr, U1, U1, U1, )
OPCODE(LogicalAnd, U1, U1, U1, )
OPCODE(LogicalNot, U1, U1, )
// Conversion operations
OPCODE(ConvertS16F16, U32, U16, )
OPCODE(ConvertS16F32, U32, U32, )
OPCODE(ConvertS16F64, U32, U64, )
OPCODE(ConvertS32F16, U32, U16, )
OPCODE(ConvertS32F32, U32, U32, )
OPCODE(ConvertS32F64, U32, U64, )
OPCODE(ConvertS64F16, U64, U16, )
OPCODE(ConvertS64F32, U64, U32, )
OPCODE(ConvertS64F64, U64, U64, )
OPCODE(ConvertU16F16, U32, U16, )
OPCODE(ConvertU16F32, U32, U32, )
OPCODE(ConvertU16F64, U32, U64, )
OPCODE(ConvertU32F16, U32, U16, )
OPCODE(ConvertU32F32, U32, U32, )
OPCODE(ConvertU32F64, U32, U64, )
OPCODE(ConvertU64F16, U64, U16, )
OPCODE(ConvertU64F32, U64, U32, )
OPCODE(ConvertU64F64, U64, U64, )
OPCODE(ConvertU64U32, U64, U32, )
OPCODE(ConvertU32U64, U32, U64, )

View file

@ -0,0 +1,28 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <fmt/format.h>
namespace Shader::IR {
enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT };
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Pred> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::Pred& pred, FormatContext& ctx) {
if (pred == Shader::IR::Pred::PT) {
return fmt::format_to(ctx.out(), "PT");
} else {
return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred));
}
}
};

View file

@ -0,0 +1,314 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <fmt/format.h>
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
enum class Reg : u64 {
R0,
R1,
R2,
R3,
R4,
R5,
R6,
R7,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
R16,
R17,
R18,
R19,
R20,
R21,
R22,
R23,
R24,
R25,
R26,
R27,
R28,
R29,
R30,
R31,
R32,
R33,
R34,
R35,
R36,
R37,
R38,
R39,
R40,
R41,
R42,
R43,
R44,
R45,
R46,
R47,
R48,
R49,
R50,
R51,
R52,
R53,
R54,
R55,
R56,
R57,
R58,
R59,
R60,
R61,
R62,
R63,
R64,
R65,
R66,
R67,
R68,
R69,
R70,
R71,
R72,
R73,
R74,
R75,
R76,
R77,
R78,
R79,
R80,
R81,
R82,
R83,
R84,
R85,
R86,
R87,
R88,
R89,
R90,
R91,
R92,
R93,
R94,
R95,
R96,
R97,
R98,
R99,
R100,
R101,
R102,
R103,
R104,
R105,
R106,
R107,
R108,
R109,
R110,
R111,
R112,
R113,
R114,
R115,
R116,
R117,
R118,
R119,
R120,
R121,
R122,
R123,
R124,
R125,
R126,
R127,
R128,
R129,
R130,
R131,
R132,
R133,
R134,
R135,
R136,
R137,
R138,
R139,
R140,
R141,
R142,
R143,
R144,
R145,
R146,
R147,
R148,
R149,
R150,
R151,
R152,
R153,
R154,
R155,
R156,
R157,
R158,
R159,
R160,
R161,
R162,
R163,
R164,
R165,
R166,
R167,
R168,
R169,
R170,
R171,
R172,
R173,
R174,
R175,
R176,
R177,
R178,
R179,
R180,
R181,
R182,
R183,
R184,
R185,
R186,
R187,
R188,
R189,
R190,
R191,
R192,
R193,
R194,
R195,
R196,
R197,
R198,
R199,
R200,
R201,
R202,
R203,
R204,
R205,
R206,
R207,
R208,
R209,
R210,
R211,
R212,
R213,
R214,
R215,
R216,
R217,
R218,
R219,
R220,
R221,
R222,
R223,
R224,
R225,
R226,
R227,
R228,
R229,
R230,
R231,
R232,
R233,
R234,
R235,
R236,
R237,
R238,
R239,
R240,
R241,
R242,
R243,
R244,
R245,
R246,
R247,
R248,
R249,
R250,
R251,
R252,
R253,
R254,
RZ,
};
static_assert(static_cast<int>(Reg::RZ) == 255);
[[nodiscard]] constexpr Reg operator+(Reg reg, int num) {
if (reg == Reg::RZ) {
// Adding or subtracting registers from RZ yields RZ
return Reg::RZ;
}
const int result{static_cast<int>(reg) + num};
if (result >= static_cast<int>(Reg::RZ)) {
throw LogicError("Overflow on register arithmetic");
}
if (result < 0) {
throw LogicError("Underflow on register arithmetic");
}
return static_cast<Reg>(result);
}
[[nodiscard]] constexpr Reg operator-(Reg reg, int num) {
return reg + (-num);
}
[[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) {
return (static_cast<size_t>(reg) / align) * align == static_cast<size_t>(reg);
}
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Reg> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::Reg& reg, FormatContext& ctx) {
if (reg == Shader::IR::Reg::RZ) {
return fmt::format_to(ctx.out(), "RZ");
} else if (static_cast<int>(reg) >= 0 && static_cast<int>(reg) < 255) {
return fmt::format_to(ctx.out(), "R{}", static_cast<int>(reg));
} else {
throw Shader::LogicError("Invalid register with raw value {}", static_cast<int>(reg));
}
}
};

View file

@ -0,0 +1,36 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <string>
#include "shader_recompiler/frontend/ir/type.h"
namespace Shader::IR {
std::string NameOf(Type type) {
static constexpr std::array names{
"Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", "U64", "ZSCO",
};
const size_t bits{static_cast<size_t>(type)};
if (bits == 0) {
return "Void";
}
std::string result;
for (size_t i = 0; i < names.size(); i++) {
if ((bits & (size_t{1} << i)) != 0) {
if (!result.empty()) {
result += '|';
}
result += names[i];
}
}
return result;
}
bool AreTypesCompatible(Type lhs, Type rhs) noexcept {
return lhs == rhs || lhs == Type::Opaque || rhs == Type::Opaque;
}
} // namespace Shader::IR

View file

@ -0,0 +1,47 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include <fmt/format.h>
#include "common/common_funcs.h"
#include "shader_recompiler/exception.h"
namespace Shader::IR {
enum class Type {
Void = 0,
Opaque = 1 << 0,
Label = 1 << 1,
Reg = 1 << 2,
Pred = 1 << 3,
Attribute = 1 << 4,
U1 = 1 << 5,
U8 = 1 << 6,
U16 = 1 << 7,
U32 = 1 << 8,
U64 = 1 << 9,
ZSCO = 1 << 10,
};
DECLARE_ENUM_FLAG_OPERATORS(Type)
[[nodiscard]] std::string NameOf(Type type);
[[nodiscard]] bool AreTypesCompatible(Type lhs, Type rhs) noexcept;
} // namespace Shader::IR
template <>
struct fmt::formatter<Shader::IR::Type> {
constexpr auto parse(format_parse_context& ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const Shader::IR::Type& type, FormatContext& ctx) {
return fmt::format_to(ctx.out(), "{}", NameOf(type));
}
};

View file

@ -0,0 +1,124 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "shader_recompiler/frontend/ir/microinstruction.h"
#include "shader_recompiler/frontend/ir/opcode.h"
#include "shader_recompiler/frontend/ir/value.h"
namespace Shader::IR {
Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {}
Value::Value(IR::Block* value) noexcept : type{Type::Label}, label{value} {}
Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {}
Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {}
Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {}
Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {}
Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
bool Value::IsIdentity() const noexcept {
return type == Type::Opaque && inst->Opcode() == Opcode::Identity;
}
bool Value::IsEmpty() const noexcept {
return type == Type::Void;
}
bool Value::IsImmediate() const noexcept {
if (IsIdentity()) {
return inst->Arg(0).IsImmediate();
}
return type != Type::Opaque;
}
bool Value::IsLabel() const noexcept {
return type == Type::Label;
}
IR::Type Value::Type() const noexcept {
if (IsIdentity()) {
return inst->Arg(0).Type();
}
if (type == Type::Opaque) {
return inst->Type();
}
return type;
}
IR::Inst* Value::Inst() const {
ValidateAccess(Type::Opaque);
return inst;
}
IR::Block* Value::Label() const {
ValidateAccess(Type::Label);
return label;
}
IR::Inst* Value::InstRecursive() const {
ValidateAccess(Type::Opaque);
if (IsIdentity()) {
return inst->Arg(0).InstRecursive();
}
return inst;
}
IR::Reg Value::Reg() const {
ValidateAccess(Type::Reg);
return reg;
}
IR::Pred Value::Pred() const {
ValidateAccess(Type::Pred);
return pred;
}
IR::Attribute Value::Attribute() const {
ValidateAccess(Type::Attribute);
return attribute;
}
bool Value::U1() const {
ValidateAccess(Type::U1);
return imm_u1;
}
u8 Value::U8() const {
ValidateAccess(Type::U8);
return imm_u8;
}
u16 Value::U16() const {
ValidateAccess(Type::U16);
return imm_u16;
}
u32 Value::U32() const {
ValidateAccess(Type::U32);
return imm_u32;
}
u64 Value::U64() const {
ValidateAccess(Type::U64);
return imm_u64;
}
void Value::ValidateAccess(IR::Type expected) const {
if (type != expected) {
throw LogicError("Reading {} out of {}", expected, type);
}
}
} // namespace Shader::IR

View file

@ -0,0 +1,98 @@
// Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
#include "shader_recompiler/exception.h"
#include "shader_recompiler/frontend/ir/attribute.h"
#include "shader_recompiler/frontend/ir/pred.h"
#include "shader_recompiler/frontend/ir/reg.h"
#include "shader_recompiler/frontend/ir/type.h"
namespace Shader::IR {
class Block;
class Inst;
class Value {
public:
Value() noexcept : type{IR::Type::Void}, inst{nullptr} {}
explicit Value(IR::Inst* value) noexcept;
explicit Value(IR::Block* value) noexcept;
explicit Value(IR::Reg value) noexcept;
explicit Value(IR::Pred value) noexcept;
explicit Value(IR::Attribute value) noexcept;
explicit Value(bool value) noexcept;
explicit Value(u8 value) noexcept;
explicit Value(u16 value) noexcept;
explicit Value(u32 value) noexcept;
explicit Value(u64 value) noexcept;
[[nodiscard]] bool IsIdentity() const noexcept;
[[nodiscard]] bool IsEmpty() const noexcept;
[[nodiscard]] bool IsImmediate() const noexcept;
[[nodiscard]] bool IsLabel() const noexcept;
[[nodiscard]] IR::Type Type() const noexcept;
[[nodiscard]] IR::Inst* Inst() const;
[[nodiscard]] IR::Block* Label() const;
[[nodiscard]] IR::Inst* InstRecursive() const;
[[nodiscard]] IR::Reg Reg() const;
[[nodiscard]] IR::Pred Pred() const;
[[nodiscard]] IR::Attribute Attribute() const;
[[nodiscard]] bool U1() const;
[[nodiscard]] u8 U8() const;
[[nodiscard]] u16 U16() const;
[[nodiscard]] u32 U32() const;
[[nodiscard]] u64 U64() const;
private:
void ValidateAccess(IR::Type expected) const;
IR::Type type;
union {
IR::Inst* inst;
IR::Block* label;
IR::Reg reg;
IR::Pred pred;
IR::Attribute attribute;
bool imm_u1;
u8 imm_u8;
u16 imm_u16;
u32 imm_u32;
u64 imm_u64;
};
};
template <IR::Type type_>
class TypedValue : public Value {
public:
TypedValue() = default;
template <IR::Type other_type>
requires((other_type & type_) != IR::Type::Void) explicit(false)
TypedValue(const TypedValue<other_type>& value)
: Value(value) {}
explicit TypedValue(const Value& value) : Value(value) {
if ((value.Type() & type_) == IR::Type::Void) {
throw InvalidArgument("Incompatible types {} and {}", type_, value.Type());
}
}
explicit TypedValue(IR::Inst* inst) : TypedValue(Value(inst)) {}
};
using U1 = TypedValue<Type::U1>;
using U8 = TypedValue<Type::U8>;
using U16 = TypedValue<Type::U16>;
using U32 = TypedValue<Type::U32>;
using U64 = TypedValue<Type::U64>;
using U32U64 = TypedValue<Type::U32 | Type::U64>;
using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>;
using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>;
using ZSCO = TypedValue<Type::ZSCO>;
} // namespace Shader::IR