summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/frontend/ir
diff options
context:
space:
mode:
authorReinUsesLisp <reinuseslisp@airmail.cc>2021-01-09 07:30:07 +0100
committerameerj <52414509+ameerj@users.noreply.github.com>2021-07-23 03:51:21 +0200
commit2d48a7b4d0666ad16d03a22d85712617a0849046 (patch)
treedd1069afca86f66e77e3438da77421a43adf5091 /src/shader_recompiler/frontend/ir
parentthread_worker: Fix compile time error (diff)
downloadyuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.tar
yuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.tar.gz
yuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.tar.bz2
yuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.tar.lz
yuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.tar.xz
yuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.tar.zst
yuzu-2d48a7b4d0666ad16d03a22d85712617a0849046.zip
Diffstat (limited to '')
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.cpp447
-rw-r--r--src/shader_recompiler/frontend/ir/attribute.h242
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.cpp142
-rw-r--r--src/shader_recompiler/frontend/ir/basic_block.h134
-rw-r--r--src/shader_recompiler/frontend/ir/condition.cpp31
-rw-r--r--src/shader_recompiler/frontend/ir/condition.h60
-rw-r--r--src/shader_recompiler/frontend/ir/flow_test.cpp83
-rw-r--r--src/shader_recompiler/frontend/ir/flow_test.h61
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp533
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h123
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.cpp189
-rw-r--r--src/shader_recompiler/frontend/ir/microinstruction.h82
-rw-r--r--src/shader_recompiler/frontend/ir/opcode.cpp67
-rw-r--r--src/shader_recompiler/frontend/ir/opcode.h44
-rw-r--r--src/shader_recompiler/frontend/ir/opcode.inc142
-rw-r--r--src/shader_recompiler/frontend/ir/pred.h28
-rw-r--r--src/shader_recompiler/frontend/ir/reg.h314
-rw-r--r--src/shader_recompiler/frontend/ir/type.cpp36
-rw-r--r--src/shader_recompiler/frontend/ir/type.h47
-rw-r--r--src/shader_recompiler/frontend/ir/value.cpp124
-rw-r--r--src/shader_recompiler/frontend/ir/value.h98
21 files changed, 3027 insertions, 0 deletions
diff --git a/src/shader_recompiler/frontend/ir/attribute.cpp b/src/shader_recompiler/frontend/ir/attribute.cpp
new file mode 100644
index 000000000..2fb7d576f
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/attribute.cpp
@@ -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 \ No newline at end of file
diff --git a/src/shader_recompiler/frontend/ir/attribute.h b/src/shader_recompiler/frontend/ir/attribute.h
new file mode 100644
index 000000000..bb2cad6af
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/attribute.h
@@ -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));
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp
new file mode 100644
index 000000000..0406726ad
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/basic_block.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/basic_block.h b/src/shader_recompiler/frontend/ir/basic_block.h
new file mode 100644
index 000000000..3ed2eb957
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/basic_block.h
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/condition.cpp b/src/shader_recompiler/frontend/ir/condition.cpp
new file mode 100644
index 000000000..edff35dc7
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/condition.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/condition.h b/src/shader_recompiler/frontend/ir/condition.h
new file mode 100644
index 000000000..52737025c
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/condition.h
@@ -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));
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/flow_test.cpp b/src/shader_recompiler/frontend/ir/flow_test.cpp
new file mode 100644
index 000000000..6ebb4ad89
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/flow_test.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/flow_test.h b/src/shader_recompiler/frontend/ir/flow_test.h
new file mode 100644
index 000000000..ac883da13
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/flow_test.h
@@ -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));
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
new file mode 100644
index 000000000..6450e4b2c
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
new file mode 100644
index 000000000..1af79f41c
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp
new file mode 100644
index 000000000..553fec3b7
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.h b/src/shader_recompiler/frontend/ir/microinstruction.h
new file mode 100644
index 000000000..43460b950
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/microinstruction.h
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/opcode.cpp b/src/shader_recompiler/frontend/ir/opcode.cpp
new file mode 100644
index 000000000..65d074029
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/opcode.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/opcode.h b/src/shader_recompiler/frontend/ir/opcode.h
new file mode 100644
index 000000000..1f4440379
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/opcode.h
@@ -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));
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/opcode.inc b/src/shader_recompiler/frontend/ir/opcode.inc
new file mode 100644
index 000000000..371064bf3
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/opcode.inc
@@ -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, )
diff --git a/src/shader_recompiler/frontend/ir/pred.h b/src/shader_recompiler/frontend/ir/pred.h
new file mode 100644
index 000000000..37cc53006
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/pred.h
@@ -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));
+ }
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/reg.h b/src/shader_recompiler/frontend/ir/reg.h
new file mode 100644
index 000000000..316fc4be8
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/reg.h
@@ -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));
+ }
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/type.cpp b/src/shader_recompiler/frontend/ir/type.cpp
new file mode 100644
index 000000000..da1e2a0f6
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/type.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/type.h b/src/shader_recompiler/frontend/ir/type.h
new file mode 100644
index 000000000..f753628e8
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/type.h
@@ -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));
+ }
+};
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp
new file mode 100644
index 000000000..7b5b35d6c
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/value.cpp
@@ -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
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h
new file mode 100644
index 000000000..664dacf9d
--- /dev/null
+++ b/src/shader_recompiler/frontend/ir/value.h
@@ -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