summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/ir
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler/frontend/ir')
-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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <fmt/format.h>
6
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/ir/attribute.h"
9
10namespace Shader::IR {
11
12bool IsGeneric(Attribute attribute) noexcept {
13 return attribute >= Attribute::Generic0X && attribute <= Attribute::Generic31X;
14}
15
16int GenericAttributeIndex(Attribute attribute) {
17 if (!IsGeneric(attribute)) {
18 throw InvalidArgument("Attribute is not generic {}", attribute);
19 }
20 return (static_cast<int>(attribute) - static_cast<int>(Attribute::Generic0X)) / 4;
21}
22
23std::string NameOf(Attribute attribute) {
24 switch (attribute) {
25 case Attribute::PrimitiveId:
26 return "PrimitiveId";
27 case Attribute::Layer:
28 return "Layer";
29 case Attribute::ViewportIndex:
30 return "ViewportIndex";
31 case Attribute::PointSize:
32 return "PointSize";
33 case Attribute::PositionX:
34 return "Position.X";
35 case Attribute::PositionY:
36 return "Position.Y";
37 case Attribute::PositionZ:
38 return "Position.Z";
39 case Attribute::PositionW:
40 return "Position.W";
41 case Attribute::Generic0X:
42 return "Generic[0].X";
43 case Attribute::Generic0Y:
44 return "Generic[0].Y";
45 case Attribute::Generic0Z:
46 return "Generic[0].Z";
47 case Attribute::Generic0W:
48 return "Generic[0].W";
49 case Attribute::Generic1X:
50 return "Generic[1].X";
51 case Attribute::Generic1Y:
52 return "Generic[1].Y";
53 case Attribute::Generic1Z:
54 return "Generic[1].Z";
55 case Attribute::Generic1W:
56 return "Generic[1].W";
57 case Attribute::Generic2X:
58 return "Generic[2].X";
59 case Attribute::Generic2Y:
60 return "Generic[2].Y";
61 case Attribute::Generic2Z:
62 return "Generic[2].Z";
63 case Attribute::Generic2W:
64 return "Generic[2].W";
65 case Attribute::Generic3X:
66 return "Generic[3].X";
67 case Attribute::Generic3Y:
68 return "Generic[3].Y";
69 case Attribute::Generic3Z:
70 return "Generic[3].Z";
71 case Attribute::Generic3W:
72 return "Generic[3].W";
73 case Attribute::Generic4X:
74 return "Generic[4].X";
75 case Attribute::Generic4Y:
76 return "Generic[4].Y";
77 case Attribute::Generic4Z:
78 return "Generic[4].Z";
79 case Attribute::Generic4W:
80 return "Generic[4].W";
81 case Attribute::Generic5X:
82 return "Generic[5].X";
83 case Attribute::Generic5Y:
84 return "Generic[5].Y";
85 case Attribute::Generic5Z:
86 return "Generic[5].Z";
87 case Attribute::Generic5W:
88 return "Generic[5].W";
89 case Attribute::Generic6X:
90 return "Generic[6].X";
91 case Attribute::Generic6Y:
92 return "Generic[6].Y";
93 case Attribute::Generic6Z:
94 return "Generic[6].Z";
95 case Attribute::Generic6W:
96 return "Generic[6].W";
97 case Attribute::Generic7X:
98 return "Generic[7].X";
99 case Attribute::Generic7Y:
100 return "Generic[7].Y";
101 case Attribute::Generic7Z:
102 return "Generic[7].Z";
103 case Attribute::Generic7W:
104 return "Generic[7].W";
105 case Attribute::Generic8X:
106 return "Generic[8].X";
107 case Attribute::Generic8Y:
108 return "Generic[8].Y";
109 case Attribute::Generic8Z:
110 return "Generic[8].Z";
111 case Attribute::Generic8W:
112 return "Generic[8].W";
113 case Attribute::Generic9X:
114 return "Generic[9].X";
115 case Attribute::Generic9Y:
116 return "Generic[9].Y";
117 case Attribute::Generic9Z:
118 return "Generic[9].Z";
119 case Attribute::Generic9W:
120 return "Generic[9].W";
121 case Attribute::Generic10X:
122 return "Generic[10].X";
123 case Attribute::Generic10Y:
124 return "Generic[10].Y";
125 case Attribute::Generic10Z:
126 return "Generic[10].Z";
127 case Attribute::Generic10W:
128 return "Generic[10].W";
129 case Attribute::Generic11X:
130 return "Generic[11].X";
131 case Attribute::Generic11Y:
132 return "Generic[11].Y";
133 case Attribute::Generic11Z:
134 return "Generic[11].Z";
135 case Attribute::Generic11W:
136 return "Generic[11].W";
137 case Attribute::Generic12X:
138 return "Generic[12].X";
139 case Attribute::Generic12Y:
140 return "Generic[12].Y";
141 case Attribute::Generic12Z:
142 return "Generic[12].Z";
143 case Attribute::Generic12W:
144 return "Generic[12].W";
145 case Attribute::Generic13X:
146 return "Generic[13].X";
147 case Attribute::Generic13Y:
148 return "Generic[13].Y";
149 case Attribute::Generic13Z:
150 return "Generic[13].Z";
151 case Attribute::Generic13W:
152 return "Generic[13].W";
153 case Attribute::Generic14X:
154 return "Generic[14].X";
155 case Attribute::Generic14Y:
156 return "Generic[14].Y";
157 case Attribute::Generic14Z:
158 return "Generic[14].Z";
159 case Attribute::Generic14W:
160 return "Generic[14].W";
161 case Attribute::Generic15X:
162 return "Generic[15].X";
163 case Attribute::Generic15Y:
164 return "Generic[15].Y";
165 case Attribute::Generic15Z:
166 return "Generic[15].Z";
167 case Attribute::Generic15W:
168 return "Generic[15].W";
169 case Attribute::Generic16X:
170 return "Generic[16].X";
171 case Attribute::Generic16Y:
172 return "Generic[16].Y";
173 case Attribute::Generic16Z:
174 return "Generic[16].Z";
175 case Attribute::Generic16W:
176 return "Generic[16].W";
177 case Attribute::Generic17X:
178 return "Generic[17].X";
179 case Attribute::Generic17Y:
180 return "Generic[17].Y";
181 case Attribute::Generic17Z:
182 return "Generic[17].Z";
183 case Attribute::Generic17W:
184 return "Generic[17].W";
185 case Attribute::Generic18X:
186 return "Generic[18].X";
187 case Attribute::Generic18Y:
188 return "Generic[18].Y";
189 case Attribute::Generic18Z:
190 return "Generic[18].Z";
191 case Attribute::Generic18W:
192 return "Generic[18].W";
193 case Attribute::Generic19X:
194 return "Generic[19].X";
195 case Attribute::Generic19Y:
196 return "Generic[19].Y";
197 case Attribute::Generic19Z:
198 return "Generic[19].Z";
199 case Attribute::Generic19W:
200 return "Generic[19].W";
201 case Attribute::Generic20X:
202 return "Generic[20].X";
203 case Attribute::Generic20Y:
204 return "Generic[20].Y";
205 case Attribute::Generic20Z:
206 return "Generic[20].Z";
207 case Attribute::Generic20W:
208 return "Generic[20].W";
209 case Attribute::Generic21X:
210 return "Generic[21].X";
211 case Attribute::Generic21Y:
212 return "Generic[21].Y";
213 case Attribute::Generic21Z:
214 return "Generic[21].Z";
215 case Attribute::Generic21W:
216 return "Generic[21].W";
217 case Attribute::Generic22X:
218 return "Generic[22].X";
219 case Attribute::Generic22Y:
220 return "Generic[22].Y";
221 case Attribute::Generic22Z:
222 return "Generic[22].Z";
223 case Attribute::Generic22W:
224 return "Generic[22].W";
225 case Attribute::Generic23X:
226 return "Generic[23].X";
227 case Attribute::Generic23Y:
228 return "Generic[23].Y";
229 case Attribute::Generic23Z:
230 return "Generic[23].Z";
231 case Attribute::Generic23W:
232 return "Generic[23].W";
233 case Attribute::Generic24X:
234 return "Generic[24].X";
235 case Attribute::Generic24Y:
236 return "Generic[24].Y";
237 case Attribute::Generic24Z:
238 return "Generic[24].Z";
239 case Attribute::Generic24W:
240 return "Generic[24].W";
241 case Attribute::Generic25X:
242 return "Generic[25].X";
243 case Attribute::Generic25Y:
244 return "Generic[25].Y";
245 case Attribute::Generic25Z:
246 return "Generic[25].Z";
247 case Attribute::Generic25W:
248 return "Generic[25].W";
249 case Attribute::Generic26X:
250 return "Generic[26].X";
251 case Attribute::Generic26Y:
252 return "Generic[26].Y";
253 case Attribute::Generic26Z:
254 return "Generic[26].Z";
255 case Attribute::Generic26W:
256 return "Generic[26].W";
257 case Attribute::Generic27X:
258 return "Generic[27].X";
259 case Attribute::Generic27Y:
260 return "Generic[27].Y";
261 case Attribute::Generic27Z:
262 return "Generic[27].Z";
263 case Attribute::Generic27W:
264 return "Generic[27].W";
265 case Attribute::Generic28X:
266 return "Generic[28].X";
267 case Attribute::Generic28Y:
268 return "Generic[28].Y";
269 case Attribute::Generic28Z:
270 return "Generic[28].Z";
271 case Attribute::Generic28W:
272 return "Generic[28].W";
273 case Attribute::Generic29X:
274 return "Generic[29].X";
275 case Attribute::Generic29Y:
276 return "Generic[29].Y";
277 case Attribute::Generic29Z:
278 return "Generic[29].Z";
279 case Attribute::Generic29W:
280 return "Generic[29].W";
281 case Attribute::Generic30X:
282 return "Generic[30].X";
283 case Attribute::Generic30Y:
284 return "Generic[30].Y";
285 case Attribute::Generic30Z:
286 return "Generic[30].Z";
287 case Attribute::Generic30W:
288 return "Generic[30].W";
289 case Attribute::Generic31X:
290 return "Generic[31].X";
291 case Attribute::Generic31Y:
292 return "Generic[31].Y";
293 case Attribute::Generic31Z:
294 return "Generic[31].Z";
295 case Attribute::Generic31W:
296 return "Generic[31].W";
297 case Attribute::ColorFrontDiffuseR:
298 return "ColorFrontDiffuse.R";
299 case Attribute::ColorFrontDiffuseG:
300 return "ColorFrontDiffuse.G";
301 case Attribute::ColorFrontDiffuseB:
302 return "ColorFrontDiffuse.B";
303 case Attribute::ColorFrontDiffuseA:
304 return "ColorFrontDiffuse.A";
305 case Attribute::ColorFrontSpecularR:
306 return "ColorFrontSpecular.R";
307 case Attribute::ColorFrontSpecularG:
308 return "ColorFrontSpecular.G";
309 case Attribute::ColorFrontSpecularB:
310 return "ColorFrontSpecular.B";
311 case Attribute::ColorFrontSpecularA:
312 return "ColorFrontSpecular.A";
313 case Attribute::ColorBackDiffuseR:
314 return "ColorBackDiffuse.R";
315 case Attribute::ColorBackDiffuseG:
316 return "ColorBackDiffuse.G";
317 case Attribute::ColorBackDiffuseB:
318 return "ColorBackDiffuse.B";
319 case Attribute::ColorBackDiffuseA:
320 return "ColorBackDiffuse.A";
321 case Attribute::ColorBackSpecularR:
322 return "ColorBackSpecular.R";
323 case Attribute::ColorBackSpecularG:
324 return "ColorBackSpecular.G";
325 case Attribute::ColorBackSpecularB:
326 return "ColorBackSpecular.B";
327 case Attribute::ColorBackSpecularA:
328 return "ColorBackSpecular.A";
329 case Attribute::ClipDistance0:
330 return "ClipDistance[0]";
331 case Attribute::ClipDistance1:
332 return "ClipDistance[1]";
333 case Attribute::ClipDistance2:
334 return "ClipDistance[2]";
335 case Attribute::ClipDistance3:
336 return "ClipDistance[3]";
337 case Attribute::ClipDistance4:
338 return "ClipDistance[4]";
339 case Attribute::ClipDistance5:
340 return "ClipDistance[5]";
341 case Attribute::ClipDistance6:
342 return "ClipDistance[6]";
343 case Attribute::ClipDistance7:
344 return "ClipDistance[7]";
345 case Attribute::PointSpriteS:
346 return "PointSprite.S";
347 case Attribute::PointSpriteT:
348 return "PointSprite.T";
349 case Attribute::FogCoordinate:
350 return "FogCoordinate";
351 case Attribute::TessellationEvaluationPointU:
352 return "TessellationEvaluationPoint.U";
353 case Attribute::TessellationEvaluationPointV:
354 return "TessellationEvaluationPoint.V";
355 case Attribute::InstanceId:
356 return "InstanceId";
357 case Attribute::VertexId:
358 return "VertexId";
359 case Attribute::FixedFncTexture0S:
360 return "FixedFncTexture[0].S";
361 case Attribute::FixedFncTexture0T:
362 return "FixedFncTexture[0].T";
363 case Attribute::FixedFncTexture0R:
364 return "FixedFncTexture[0].R";
365 case Attribute::FixedFncTexture0Q:
366 return "FixedFncTexture[0].Q";
367 case Attribute::FixedFncTexture1S:
368 return "FixedFncTexture[1].S";
369 case Attribute::FixedFncTexture1T:
370 return "FixedFncTexture[1].T";
371 case Attribute::FixedFncTexture1R:
372 return "FixedFncTexture[1].R";
373 case Attribute::FixedFncTexture1Q:
374 return "FixedFncTexture[1].Q";
375 case Attribute::FixedFncTexture2S:
376 return "FixedFncTexture[2].S";
377 case Attribute::FixedFncTexture2T:
378 return "FixedFncTexture[2].T";
379 case Attribute::FixedFncTexture2R:
380 return "FixedFncTexture[2].R";
381 case Attribute::FixedFncTexture2Q:
382 return "FixedFncTexture[2].Q";
383 case Attribute::FixedFncTexture3S:
384 return "FixedFncTexture[3].S";
385 case Attribute::FixedFncTexture3T:
386 return "FixedFncTexture[3].T";
387 case Attribute::FixedFncTexture3R:
388 return "FixedFncTexture[3].R";
389 case Attribute::FixedFncTexture3Q:
390 return "FixedFncTexture[3].Q";
391 case Attribute::FixedFncTexture4S:
392 return "FixedFncTexture[4].S";
393 case Attribute::FixedFncTexture4T:
394 return "FixedFncTexture[4].T";
395 case Attribute::FixedFncTexture4R:
396 return "FixedFncTexture[4].R";
397 case Attribute::FixedFncTexture4Q:
398 return "FixedFncTexture[4].Q";
399 case Attribute::FixedFncTexture5S:
400 return "FixedFncTexture[5].S";
401 case Attribute::FixedFncTexture5T:
402 return "FixedFncTexture[5].T";
403 case Attribute::FixedFncTexture5R:
404 return "FixedFncTexture[5].R";
405 case Attribute::FixedFncTexture5Q:
406 return "FixedFncTexture[5].Q";
407 case Attribute::FixedFncTexture6S:
408 return "FixedFncTexture[6].S";
409 case Attribute::FixedFncTexture6T:
410 return "FixedFncTexture[6].T";
411 case Attribute::FixedFncTexture6R:
412 return "FixedFncTexture[6].R";
413 case Attribute::FixedFncTexture6Q:
414 return "FixedFncTexture[6].Q";
415 case Attribute::FixedFncTexture7S:
416 return "FixedFncTexture[7].S";
417 case Attribute::FixedFncTexture7T:
418 return "FixedFncTexture[7].T";
419 case Attribute::FixedFncTexture7R:
420 return "FixedFncTexture[7].R";
421 case Attribute::FixedFncTexture7Q:
422 return "FixedFncTexture[7].Q";
423 case Attribute::FixedFncTexture8S:
424 return "FixedFncTexture[8].S";
425 case Attribute::FixedFncTexture8T:
426 return "FixedFncTexture[8].T";
427 case Attribute::FixedFncTexture8R:
428 return "FixedFncTexture[8].R";
429 case Attribute::FixedFncTexture8Q:
430 return "FixedFncTexture[8].Q";
431 case Attribute::FixedFncTexture9S:
432 return "FixedFncTexture[9].S";
433 case Attribute::FixedFncTexture9T:
434 return "FixedFncTexture[9].T";
435 case Attribute::FixedFncTexture9R:
436 return "FixedFncTexture[9].R";
437 case Attribute::FixedFncTexture9Q:
438 return "FixedFncTexture[9].Q";
439 case Attribute::ViewportMask:
440 return "ViewportMask";
441 case Attribute::FrontFace:
442 return "FrontFace";
443 }
444 return fmt::format("<reserved attribute {}>", static_cast<int>(attribute));
445}
446
447} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <fmt/format.h>
8
9#include "common/common_types.h"
10
11namespace Shader::IR {
12
13enum class Attribute : u64 {
14 PrimitiveId = 24,
15 Layer = 25,
16 ViewportIndex = 26,
17 PointSize = 27,
18 PositionX = 28,
19 PositionY = 29,
20 PositionZ = 30,
21 PositionW = 31,
22 Generic0X = 32,
23 Generic0Y = 33,
24 Generic0Z = 34,
25 Generic0W = 35,
26 Generic1X = 36,
27 Generic1Y = 37,
28 Generic1Z = 38,
29 Generic1W = 39,
30 Generic2X = 40,
31 Generic2Y = 41,
32 Generic2Z = 42,
33 Generic2W = 43,
34 Generic3X = 44,
35 Generic3Y = 45,
36 Generic3Z = 46,
37 Generic3W = 47,
38 Generic4X = 48,
39 Generic4Y = 49,
40 Generic4Z = 50,
41 Generic4W = 51,
42 Generic5X = 52,
43 Generic5Y = 53,
44 Generic5Z = 54,
45 Generic5W = 55,
46 Generic6X = 56,
47 Generic6Y = 57,
48 Generic6Z = 58,
49 Generic6W = 59,
50 Generic7X = 60,
51 Generic7Y = 61,
52 Generic7Z = 62,
53 Generic7W = 63,
54 Generic8X = 64,
55 Generic8Y = 65,
56 Generic8Z = 66,
57 Generic8W = 67,
58 Generic9X = 68,
59 Generic9Y = 69,
60 Generic9Z = 70,
61 Generic9W = 71,
62 Generic10X = 72,
63 Generic10Y = 73,
64 Generic10Z = 74,
65 Generic10W = 75,
66 Generic11X = 76,
67 Generic11Y = 77,
68 Generic11Z = 78,
69 Generic11W = 79,
70 Generic12X = 80,
71 Generic12Y = 81,
72 Generic12Z = 82,
73 Generic12W = 83,
74 Generic13X = 84,
75 Generic13Y = 85,
76 Generic13Z = 86,
77 Generic13W = 87,
78 Generic14X = 88,
79 Generic14Y = 89,
80 Generic14Z = 90,
81 Generic14W = 91,
82 Generic15X = 92,
83 Generic15Y = 93,
84 Generic15Z = 94,
85 Generic15W = 95,
86 Generic16X = 96,
87 Generic16Y = 97,
88 Generic16Z = 98,
89 Generic16W = 99,
90 Generic17X = 100,
91 Generic17Y = 101,
92 Generic17Z = 102,
93 Generic17W = 103,
94 Generic18X = 104,
95 Generic18Y = 105,
96 Generic18Z = 106,
97 Generic18W = 107,
98 Generic19X = 108,
99 Generic19Y = 109,
100 Generic19Z = 110,
101 Generic19W = 111,
102 Generic20X = 112,
103 Generic20Y = 113,
104 Generic20Z = 114,
105 Generic20W = 115,
106 Generic21X = 116,
107 Generic21Y = 117,
108 Generic21Z = 118,
109 Generic21W = 119,
110 Generic22X = 120,
111 Generic22Y = 121,
112 Generic22Z = 122,
113 Generic22W = 123,
114 Generic23X = 124,
115 Generic23Y = 125,
116 Generic23Z = 126,
117 Generic23W = 127,
118 Generic24X = 128,
119 Generic24Y = 129,
120 Generic24Z = 130,
121 Generic24W = 131,
122 Generic25X = 132,
123 Generic25Y = 133,
124 Generic25Z = 134,
125 Generic25W = 135,
126 Generic26X = 136,
127 Generic26Y = 137,
128 Generic26Z = 138,
129 Generic26W = 139,
130 Generic27X = 140,
131 Generic27Y = 141,
132 Generic27Z = 142,
133 Generic27W = 143,
134 Generic28X = 144,
135 Generic28Y = 145,
136 Generic28Z = 146,
137 Generic28W = 147,
138 Generic29X = 148,
139 Generic29Y = 149,
140 Generic29Z = 150,
141 Generic29W = 151,
142 Generic30X = 152,
143 Generic30Y = 153,
144 Generic30Z = 154,
145 Generic30W = 155,
146 Generic31X = 156,
147 Generic31Y = 157,
148 Generic31Z = 158,
149 Generic31W = 159,
150 ColorFrontDiffuseR = 160,
151 ColorFrontDiffuseG = 161,
152 ColorFrontDiffuseB = 162,
153 ColorFrontDiffuseA = 163,
154 ColorFrontSpecularR = 164,
155 ColorFrontSpecularG = 165,
156 ColorFrontSpecularB = 166,
157 ColorFrontSpecularA = 167,
158 ColorBackDiffuseR = 168,
159 ColorBackDiffuseG = 169,
160 ColorBackDiffuseB = 170,
161 ColorBackDiffuseA = 171,
162 ColorBackSpecularR = 172,
163 ColorBackSpecularG = 173,
164 ColorBackSpecularB = 174,
165 ColorBackSpecularA = 175,
166 ClipDistance0 = 176,
167 ClipDistance1 = 177,
168 ClipDistance2 = 178,
169 ClipDistance3 = 179,
170 ClipDistance4 = 180,
171 ClipDistance5 = 181,
172 ClipDistance6 = 182,
173 ClipDistance7 = 183,
174 PointSpriteS = 184,
175 PointSpriteT = 185,
176 FogCoordinate = 186,
177 TessellationEvaluationPointU = 188,
178 TessellationEvaluationPointV = 189,
179 InstanceId = 190,
180 VertexId = 191,
181 FixedFncTexture0S = 192,
182 FixedFncTexture0T = 193,
183 FixedFncTexture0R = 194,
184 FixedFncTexture0Q = 195,
185 FixedFncTexture1S = 196,
186 FixedFncTexture1T = 197,
187 FixedFncTexture1R = 198,
188 FixedFncTexture1Q = 199,
189 FixedFncTexture2S = 200,
190 FixedFncTexture2T = 201,
191 FixedFncTexture2R = 202,
192 FixedFncTexture2Q = 203,
193 FixedFncTexture3S = 204,
194 FixedFncTexture3T = 205,
195 FixedFncTexture3R = 206,
196 FixedFncTexture3Q = 207,
197 FixedFncTexture4S = 208,
198 FixedFncTexture4T = 209,
199 FixedFncTexture4R = 210,
200 FixedFncTexture4Q = 211,
201 FixedFncTexture5S = 212,
202 FixedFncTexture5T = 213,
203 FixedFncTexture5R = 214,
204 FixedFncTexture5Q = 215,
205 FixedFncTexture6S = 216,
206 FixedFncTexture6T = 217,
207 FixedFncTexture6R = 218,
208 FixedFncTexture6Q = 219,
209 FixedFncTexture7S = 220,
210 FixedFncTexture7T = 221,
211 FixedFncTexture7R = 222,
212 FixedFncTexture7Q = 223,
213 FixedFncTexture8S = 224,
214 FixedFncTexture8T = 225,
215 FixedFncTexture8R = 226,
216 FixedFncTexture8Q = 227,
217 FixedFncTexture9S = 228,
218 FixedFncTexture9T = 229,
219 FixedFncTexture9R = 230,
220 FixedFncTexture9Q = 231,
221 ViewportMask = 232,
222 FrontFace = 255,
223};
224
225[[nodiscard]] bool IsGeneric(Attribute attribute) noexcept;
226
227[[nodiscard]] int GenericAttributeIndex(Attribute attribute);
228
229[[nodiscard]] std::string NameOf(Attribute attribute);
230
231} // namespace Shader::IR
232
233template <>
234struct fmt::formatter<Shader::IR::Attribute> {
235 constexpr auto parse(format_parse_context& ctx) {
236 return ctx.begin();
237 }
238 template <typename FormatContext>
239 auto format(const Shader::IR::Attribute& attribute, FormatContext& ctx) {
240 return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(attribute));
241 }
242};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <initializer_list>
7#include <map>
8#include <memory>
9
10#include "common/bit_cast.h"
11#include "common/common_types.h"
12#include "shader_recompiler/frontend/ir/basic_block.h"
13#include "shader_recompiler/frontend/ir/value.h"
14
15namespace Shader::IR {
16
17Block::Block(u32 begin, u32 end) : location_begin{begin}, location_end{end} {}
18
19Block::~Block() = default;
20
21void Block::AppendNewInst(Opcode op, std::initializer_list<Value> args) {
22 PrependNewInst(end(), op, args);
23}
24
25Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op,
26 std::initializer_list<Value> args) {
27 Inst* const inst{std::construct_at(instruction_alloc_pool.allocate(), op)};
28 const auto result_it{instructions.insert(insertion_point, *inst)};
29
30 if (inst->NumArgs() != args.size()) {
31 throw InvalidArgument("Invalid number of arguments {} in {}", args.size(), op);
32 }
33 std::ranges::for_each(args, [inst, index = size_t{0}](const Value& arg) mutable {
34 inst->SetArg(index, arg);
35 ++index;
36 });
37 return result_it;
38}
39
40u32 Block::LocationBegin() const noexcept {
41 return location_begin;
42}
43
44u32 Block::LocationEnd() const noexcept {
45 return location_end;
46}
47
48Block::InstructionList& Block::Instructions() noexcept {
49 return instructions;
50}
51
52const Block::InstructionList& Block::Instructions() const noexcept {
53 return instructions;
54}
55
56static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index,
57 const std::map<const Inst*, size_t>& inst_to_index,
58 const Value& arg) {
59 if (arg.IsEmpty()) {
60 return "<null>";
61 }
62 if (arg.IsLabel()) {
63 if (const auto it{block_to_index.find(arg.Label())}; it != block_to_index.end()) {
64 return fmt::format("{{Block ${}}}", it->second);
65 }
66 return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(arg.Label()));
67 }
68 if (!arg.IsImmediate()) {
69 if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) {
70 return fmt::format("%{}", it->second);
71 }
72 return fmt::format("%<unknown inst {:016x}>", reinterpret_cast<u64>(arg.Inst()));
73 }
74 switch (arg.Type()) {
75 case Type::U1:
76 return fmt::format("#{}", arg.U1() ? '1' : '0');
77 case Type::U8:
78 return fmt::format("#{}", arg.U8());
79 case Type::U16:
80 return fmt::format("#{}", arg.U16());
81 case Type::U32:
82 return fmt::format("#{}", arg.U32());
83 case Type::U64:
84 return fmt::format("#{}", arg.U64());
85 case Type::Reg:
86 return fmt::format("{}", arg.Reg());
87 case Type::Pred:
88 return fmt::format("{}", arg.Pred());
89 case Type::Attribute:
90 return fmt::format("{}", arg.Attribute());
91 default:
92 return "<unknown immediate type>";
93 }
94}
95
96std::string DumpBlock(const Block& block) {
97 size_t inst_index{0};
98 std::map<const Inst*, size_t> inst_to_index;
99 return DumpBlock(block, {}, inst_to_index, inst_index);
100}
101
102std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& block_to_index,
103 std::map<const Inst*, size_t>& inst_to_index, size_t& inst_index) {
104 std::string ret{"Block"};
105 if (const auto it{block_to_index.find(&block)}; it != block_to_index.end()) {
106 ret += fmt::format(" ${}", it->second);
107 }
108 ret += fmt::format(": begin={:04x} end={:04x}\n", block.LocationBegin(), block.LocationEnd());
109
110 for (const Inst& inst : block) {
111 const Opcode op{inst.Opcode()};
112 ret += fmt::format("[{:016x}] ", reinterpret_cast<u64>(&inst));
113 if (TypeOf(op) != Type::Void) {
114 ret += fmt::format("%{:<5} = {}", inst_index, op);
115 } else {
116 ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces
117 }
118 const size_t arg_count{NumArgsOf(op)};
119 for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) {
120 const Value arg{inst.Arg(arg_index)};
121 ret += arg_index != 0 ? ", " : " ";
122 ret += ArgToIndex(block_to_index, inst_to_index, arg);
123
124 const Type actual_type{arg.Type()};
125 const Type expected_type{ArgTypeOf(op, arg_index)};
126 if (!AreTypesCompatible(actual_type, expected_type)) {
127 ret += fmt::format("<type error: {} != {}>", actual_type, expected_type);
128 }
129 }
130 if (TypeOf(op) != Type::Void) {
131 ret += fmt::format(" (uses: {})\n", inst.UseCount());
132 } else {
133 ret += '\n';
134 }
135
136 inst_to_index.emplace(&inst, inst_index);
137 ++inst_index;
138 }
139 return ret;
140}
141
142} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <initializer_list>
8#include <map>
9
10#include <boost/intrusive/list.hpp>
11#include <boost/pool/pool_alloc.hpp>
12
13#include "shader_recompiler/frontend/ir/microinstruction.h"
14
15namespace Shader::IR {
16
17class Block {
18public:
19 using InstructionList = boost::intrusive::list<Inst>;
20 using size_type = InstructionList::size_type;
21 using iterator = InstructionList::iterator;
22 using const_iterator = InstructionList::const_iterator;
23 using reverse_iterator = InstructionList::reverse_iterator;
24 using const_reverse_iterator = InstructionList::const_reverse_iterator;
25
26 explicit Block(u32 begin, u32 end);
27 ~Block();
28
29 Block(const Block&) = delete;
30 Block& operator=(const Block&) = delete;
31
32 Block(Block&&) = default;
33 Block& operator=(Block&&) = default;
34
35 /// Appends a new instruction to the end of this basic block.
36 void AppendNewInst(Opcode op, std::initializer_list<Value> args);
37
38 /// Prepends a new instruction to this basic block before the insertion point.
39 iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args);
40
41 /// Gets the starting location of this basic block.
42 [[nodiscard]] u32 LocationBegin() const noexcept;
43 /// Gets the end location for this basic block.
44 [[nodiscard]] u32 LocationEnd() const noexcept;
45
46 /// Gets a mutable reference to the instruction list for this basic block.
47 InstructionList& Instructions() noexcept;
48 /// Gets an immutable reference to the instruction list for this basic block.
49 const InstructionList& Instructions() const noexcept;
50
51 [[nodiscard]] bool empty() const {
52 return instructions.empty();
53 }
54 [[nodiscard]] size_type size() const {
55 return instructions.size();
56 }
57
58 [[nodiscard]] Inst& front() {
59 return instructions.front();
60 }
61 [[nodiscard]] const Inst& front() const {
62 return instructions.front();
63 }
64
65 [[nodiscard]] Inst& back() {
66 return instructions.back();
67 }
68 [[nodiscard]] const Inst& back() const {
69 return instructions.back();
70 }
71
72 [[nodiscard]] iterator begin() {
73 return instructions.begin();
74 }
75 [[nodiscard]] const_iterator begin() const {
76 return instructions.begin();
77 }
78 [[nodiscard]] iterator end() {
79 return instructions.end();
80 }
81 [[nodiscard]] const_iterator end() const {
82 return instructions.end();
83 }
84
85 [[nodiscard]] reverse_iterator rbegin() {
86 return instructions.rbegin();
87 }
88 [[nodiscard]] const_reverse_iterator rbegin() const {
89 return instructions.rbegin();
90 }
91 [[nodiscard]] reverse_iterator rend() {
92 return instructions.rend();
93 }
94 [[nodiscard]] const_reverse_iterator rend() const {
95 return instructions.rend();
96 }
97
98 [[nodiscard]] const_iterator cbegin() const {
99 return instructions.cbegin();
100 }
101 [[nodiscard]] const_iterator cend() const {
102 return instructions.cend();
103 }
104
105 [[nodiscard]] const_reverse_iterator crbegin() const {
106 return instructions.crbegin();
107 }
108 [[nodiscard]] const_reverse_iterator crend() const {
109 return instructions.crend();
110 }
111
112private:
113 /// Starting location of this block
114 u32 location_begin;
115 /// End location of this block
116 u32 location_end;
117
118 /// List of instructions in this block.
119 InstructionList instructions;
120
121 /// Memory pool for instruction list
122 boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free,
123 boost::details::pool::null_mutex>
124 instruction_alloc_pool;
125};
126
127[[nodiscard]] std::string DumpBlock(const Block& block);
128
129[[nodiscard]] std::string DumpBlock(const Block& block,
130 const std::map<const Block*, size_t>& block_to_index,
131 std::map<const Inst*, size_t>& inst_to_index,
132 size_t& inst_index);
133
134} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include <fmt/format.h>
8
9#include "shader_recompiler/frontend/ir/condition.h"
10
11namespace Shader::IR {
12
13std::string NameOf(Condition condition) {
14 std::string ret;
15 if (condition.FlowTest() != FlowTest::T) {
16 ret = fmt::to_string(condition.FlowTest());
17 }
18 const auto [pred, negated]{condition.Pred()};
19 if (pred != Pred::PT || negated) {
20 if (!ret.empty()) {
21 ret += '&';
22 }
23 if (negated) {
24 ret += '!';
25 }
26 ret += fmt::to_string(pred);
27 }
28 return ret;
29}
30
31} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <compare>
9
10#include <fmt/format.h>
11
12#include "common/common_types.h"
13#include "shader_recompiler/frontend/ir/flow_test.h"
14#include "shader_recompiler/frontend/ir/pred.h"
15
16namespace Shader::IR {
17
18class Condition {
19public:
20 Condition() noexcept = default;
21
22 explicit Condition(FlowTest flow_test_, Pred pred_, bool pred_negated_ = false) noexcept
23 : flow_test{static_cast<u16>(flow_test_)}, pred{static_cast<u8>(pred_)},
24 pred_negated{pred_negated_ ? u8{1} : u8{0}} {}
25
26 explicit Condition(Pred pred_, bool pred_negated_ = false) noexcept
27 : Condition(FlowTest::T, pred_, pred_negated_) {}
28
29 Condition(bool value) : Condition(Pred::PT, !value) {}
30
31 auto operator<=>(const Condition&) const noexcept = default;
32
33 [[nodiscard]] IR::FlowTest FlowTest() const noexcept {
34 return static_cast<IR::FlowTest>(flow_test);
35 }
36
37 [[nodiscard]] std::pair<IR::Pred, bool> Pred() const noexcept {
38 return {static_cast<IR::Pred>(pred), pred_negated != 0};
39 }
40
41private:
42 u16 flow_test;
43 u8 pred;
44 u8 pred_negated;
45};
46
47std::string NameOf(Condition condition);
48
49} // namespace Shader::IR
50
51template <>
52struct fmt::formatter<Shader::IR::Condition> {
53 constexpr auto parse(format_parse_context& ctx) {
54 return ctx.begin();
55 }
56 template <typename FormatContext>
57 auto format(const Shader::IR::Condition& cond, FormatContext& ctx) {
58 return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(cond));
59 }
60};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6
7#include <fmt/format.h>
8
9#include "shader_recompiler/frontend/ir/flow_test.h"
10
11namespace Shader::IR {
12
13std::string NameOf(FlowTest flow_test) {
14 switch (flow_test) {
15 case FlowTest::F:
16 return "F";
17 case FlowTest::LT:
18 return "LT";
19 case FlowTest::EQ:
20 return "EQ";
21 case FlowTest::LE:
22 return "LE";
23 case FlowTest::GT:
24 return "GT";
25 case FlowTest::NE:
26 return "NE";
27 case FlowTest::GE:
28 return "GE";
29 case FlowTest::NUM:
30 return "NUM";
31 case FlowTest::NaN:
32 return "NAN";
33 case FlowTest::LTU:
34 return "LTU";
35 case FlowTest::EQU:
36 return "EQU";
37 case FlowTest::LEU:
38 return "LEU";
39 case FlowTest::GTU:
40 return "GTU";
41 case FlowTest::NEU:
42 return "NEU";
43 case FlowTest::GEU:
44 return "GEU";
45 case FlowTest::T:
46 return "T";
47 case FlowTest::OFF:
48 return "OFF";
49 case FlowTest::LO:
50 return "LO";
51 case FlowTest::SFF:
52 return "SFF";
53 case FlowTest::LS:
54 return "LS";
55 case FlowTest::HI:
56 return "HI";
57 case FlowTest::SFT:
58 return "SFT";
59 case FlowTest::HS:
60 return "HS";
61 case FlowTest::OFT:
62 return "OFT";
63 case FlowTest::CSM_TA:
64 return "CSM_TA";
65 case FlowTest::CSM_TR:
66 return "CSM_TR";
67 case FlowTest::CSM_MX:
68 return "CSM_MX";
69 case FlowTest::FCSM_TA:
70 return "FCSM_TA";
71 case FlowTest::FCSM_TR:
72 return "FCSM_TR";
73 case FlowTest::FCSM_MX:
74 return "FCSM_MX";
75 case FlowTest::RLE:
76 return "RLE";
77 case FlowTest::RGT:
78 return "RGT";
79 }
80 return fmt::format("<invalid flow test {}>", static_cast<int>(flow_test));
81}
82
83} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8
9#include <fmt/format.h>
10
11namespace Shader::IR {
12
13enum class FlowTest {
14 F,
15 LT,
16 EQ,
17 LE,
18 GT,
19 NE,
20 GE,
21 NUM,
22 NaN,
23 LTU,
24 EQU,
25 LEU,
26 GTU,
27 NEU,
28 GEU,
29 T,
30 OFF,
31 LO,
32 SFF,
33 LS,
34 HI,
35 SFT,
36 HS,
37 OFT,
38 CSM_TA,
39 CSM_TR,
40 CSM_MX,
41 FCSM_TA,
42 FCSM_TR,
43 FCSM_MX,
44 RLE,
45 RGT,
46};
47
48[[nodiscard]] std::string NameOf(FlowTest flow_test);
49
50} // namespace Shader::IR
51
52template <>
53struct fmt::formatter<Shader::IR::FlowTest> {
54 constexpr auto parse(format_parse_context& ctx) {
55 return ctx.begin();
56 }
57 template <typename FormatContext>
58 auto format(const Shader::IR::FlowTest& flow_test, FormatContext& ctx) {
59 return fmt::format_to(ctx.out(), "{}", Shader::IR::NameOf(flow_test));
60 }
61};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/bit_cast.h"
6#include "shader_recompiler/frontend/ir/ir_emitter.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::IR {
10
11[[noreturn]] static void ThrowInvalidType(Type type) {
12 throw InvalidArgument("Invalid type {}", type);
13}
14
15U1 IREmitter::Imm1(bool value) const {
16 return U1{Value{value}};
17}
18
19U8 IREmitter::Imm8(u8 value) const {
20 return U8{Value{value}};
21}
22
23U16 IREmitter::Imm16(u16 value) const {
24 return U16{Value{value}};
25}
26
27U32 IREmitter::Imm32(u32 value) const {
28 return U32{Value{value}};
29}
30
31U32 IREmitter::Imm32(s32 value) const {
32 return U32{Value{static_cast<u32>(value)}};
33}
34
35U32 IREmitter::Imm32(f32 value) const {
36 return U32{Value{Common::BitCast<u32>(value)}};
37}
38
39U64 IREmitter::Imm64(u64 value) const {
40 return U64{Value{value}};
41}
42
43U64 IREmitter::Imm64(f64 value) const {
44 return U64{Value{Common::BitCast<u64>(value)}};
45}
46
47void IREmitter::Branch(IR::Block* label) {
48 Inst(Opcode::Branch, label);
49}
50
51void IREmitter::BranchConditional(const U1& cond, IR::Block* true_label, IR::Block* false_label) {
52 Inst(Opcode::BranchConditional, cond, true_label, false_label);
53}
54
55void IREmitter::Exit() {
56 Inst(Opcode::Exit);
57}
58
59void IREmitter::Return() {
60 Inst(Opcode::Return);
61}
62
63void IREmitter::Unreachable() {
64 Inst(Opcode::Unreachable);
65}
66
67U32 IREmitter::GetReg(IR::Reg reg) {
68 return Inst<U32>(Opcode::GetRegister, reg);
69}
70
71void IREmitter::SetReg(IR::Reg reg, const U32& value) {
72 Inst(Opcode::SetRegister, reg, value);
73}
74
75U1 IREmitter::GetPred(IR::Pred pred, bool is_negated) {
76 const U1 value{Inst<U1>(Opcode::GetPred, pred)};
77 if (is_negated) {
78 return Inst<U1>(Opcode::LogicalNot, value);
79 } else {
80 return value;
81 }
82}
83
84void IREmitter::SetPred(IR::Pred pred, const U1& value) {
85 Inst(Opcode::SetPred, pred, value);
86}
87
88U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) {
89 return Inst<U32>(Opcode::GetCbuf, binding, byte_offset);
90}
91
92U1 IREmitter::GetZFlag() {
93 return Inst<U1>(Opcode::GetZFlag);
94}
95
96U1 IREmitter::GetSFlag() {
97 return Inst<U1>(Opcode::GetSFlag);
98}
99
100U1 IREmitter::GetCFlag() {
101 return Inst<U1>(Opcode::GetCFlag);
102}
103
104U1 IREmitter::GetOFlag() {
105 return Inst<U1>(Opcode::GetOFlag);
106}
107
108void IREmitter::SetZFlag(const U1& value) {
109 Inst(Opcode::SetZFlag, value);
110}
111
112void IREmitter::SetSFlag(const U1& value) {
113 Inst(Opcode::SetSFlag, value);
114}
115
116void IREmitter::SetCFlag(const U1& value) {
117 Inst(Opcode::SetCFlag, value);
118}
119
120void IREmitter::SetOFlag(const U1& value) {
121 Inst(Opcode::SetOFlag, value);
122}
123
124U32 IREmitter::GetAttribute(IR::Attribute attribute) {
125 return Inst<U32>(Opcode::GetAttribute, attribute);
126}
127
128void IREmitter::SetAttribute(IR::Attribute attribute, const U32& value) {
129 Inst(Opcode::SetAttribute, attribute, value);
130}
131
132void IREmitter::WriteGlobalU8(const U64& address, const U32& value) {
133 Inst(Opcode::WriteGlobalU8, address, value);
134}
135
136void IREmitter::WriteGlobalS8(const U64& address, const U32& value) {
137 Inst(Opcode::WriteGlobalS8, address, value);
138}
139
140void IREmitter::WriteGlobalU16(const U64& address, const U32& value) {
141 Inst(Opcode::WriteGlobalU16, address, value);
142}
143
144void IREmitter::WriteGlobalS16(const U64& address, const U32& value) {
145 Inst(Opcode::WriteGlobalS16, address, value);
146}
147
148void IREmitter::WriteGlobal32(const U64& address, const U32& value) {
149 Inst(Opcode::WriteGlobal32, address, value);
150}
151
152void IREmitter::WriteGlobal64(const U64& address, const IR::Value& vector) {
153 Inst(Opcode::WriteGlobal64, address, vector);
154}
155
156void IREmitter::WriteGlobal128(const U64& address, const IR::Value& vector) {
157 Inst(Opcode::WriteGlobal128, address, vector);
158}
159
160U1 IREmitter::GetZeroFromOp(const Value& op) {
161 return Inst<U1>(Opcode::GetZeroFromOp, op);
162}
163
164U1 IREmitter::GetSignFromOp(const Value& op) {
165 return Inst<U1>(Opcode::GetSignFromOp, op);
166}
167
168U1 IREmitter::GetCarryFromOp(const Value& op) {
169 return Inst<U1>(Opcode::GetCarryFromOp, op);
170}
171
172U1 IREmitter::GetOverflowFromOp(const Value& op) {
173 return Inst<U1>(Opcode::GetOverflowFromOp, op);
174}
175
176U16U32U64 IREmitter::FPAdd(const U16U32U64& a, const U16U32U64& b) {
177 if (a.Type() != a.Type()) {
178 throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type());
179 }
180 switch (a.Type()) {
181 case Type::U16:
182 return Inst<U16>(Opcode::FPAdd16, a, b);
183 case Type::U32:
184 return Inst<U32>(Opcode::FPAdd32, a, b);
185 case Type::U64:
186 return Inst<U64>(Opcode::FPAdd64, a, b);
187 default:
188 ThrowInvalidType(a.Type());
189 }
190}
191
192Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2) {
193 if (e1.Type() != e2.Type()) {
194 throw InvalidArgument("Incompatible types {} {}", e1.Type(), e2.Type());
195 }
196 return Inst(Opcode::CompositeConstruct2, e1, e2);
197}
198
199Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3) {
200 if (e1.Type() != e2.Type() || e1.Type() != e3.Type()) {
201 throw InvalidArgument("Incompatible types {} {} {}", e1.Type(), e2.Type(), e3.Type());
202 }
203 return Inst(Opcode::CompositeConstruct3, e1, e2, e3);
204}
205
206Value IREmitter::CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3,
207 const UAny& e4) {
208 if (e1.Type() != e2.Type() || e1.Type() != e3.Type() || e1.Type() != e4.Type()) {
209 throw InvalidArgument("Incompatible types {} {} {}", e1.Type(), e2.Type(), e3.Type(),
210 e4.Type());
211 }
212 return Inst(Opcode::CompositeConstruct4, e1, e2, e3, e4);
213}
214
215UAny IREmitter::CompositeExtract(const Value& vector, size_t element) {
216 if (element >= 4) {
217 throw InvalidArgument("Out of bounds element {}", element);
218 }
219 return Inst<UAny>(Opcode::CompositeExtract, vector, Imm32(static_cast<u32>(element)));
220}
221
222U64 IREmitter::PackUint2x32(const Value& vector) {
223 return Inst<U64>(Opcode::PackUint2x32, vector);
224}
225
226Value IREmitter::UnpackUint2x32(const U64& value) {
227 return Inst<Value>(Opcode::UnpackUint2x32, value);
228}
229
230U32 IREmitter::PackFloat2x16(const Value& vector) {
231 return Inst<U32>(Opcode::PackFloat2x16, vector);
232}
233
234Value IREmitter::UnpackFloat2x16(const U32& value) {
235 return Inst<Value>(Opcode::UnpackFloat2x16, value);
236}
237
238U64 IREmitter::PackDouble2x32(const Value& vector) {
239 return Inst<U64>(Opcode::PackDouble2x32, vector);
240}
241
242Value IREmitter::UnpackDouble2x32(const U64& value) {
243 return Inst<Value>(Opcode::UnpackDouble2x32, value);
244}
245
246U16U32U64 IREmitter::FPMul(const U16U32U64& a, const U16U32U64& b) {
247 if (a.Type() != b.Type()) {
248 throw InvalidArgument("Mismatching types {} and {}", a.Type(), b.Type());
249 }
250 switch (a.Type()) {
251 case Type::U16:
252 return Inst<U16>(Opcode::FPMul16, a, b);
253 case Type::U32:
254 return Inst<U32>(Opcode::FPMul32, a, b);
255 case Type::U64:
256 return Inst<U64>(Opcode::FPMul64, a, b);
257 default:
258 ThrowInvalidType(a.Type());
259 }
260}
261
262U16U32U64 IREmitter::FPAbs(const U16U32U64& value) {
263 switch (value.Type()) {
264 case Type::U16:
265 return Inst<U16>(Opcode::FPAbs16, value);
266 case Type::U32:
267 return Inst<U32>(Opcode::FPAbs32, value);
268 case Type::U64:
269 return Inst<U64>(Opcode::FPAbs64, value);
270 default:
271 ThrowInvalidType(value.Type());
272 }
273}
274
275U16U32U64 IREmitter::FPNeg(const U16U32U64& value) {
276 switch (value.Type()) {
277 case Type::U16:
278 return Inst<U16>(Opcode::FPNeg16, value);
279 case Type::U32:
280 return Inst<U32>(Opcode::FPNeg32, value);
281 case Type::U64:
282 return Inst<U64>(Opcode::FPNeg64, value);
283 default:
284 ThrowInvalidType(value.Type());
285 }
286}
287
288U16U32U64 IREmitter::FPAbsNeg(const U16U32U64& value, bool abs, bool neg) {
289 U16U32U64 result{value};
290 if (abs) {
291 result = FPAbs(value);
292 }
293 if (neg) {
294 result = FPNeg(value);
295 }
296 return result;
297}
298
299U32 IREmitter::FPCosNotReduced(const U32& value) {
300 return Inst<U32>(Opcode::FPCosNotReduced, value);
301}
302
303U32 IREmitter::FPExp2NotReduced(const U32& value) {
304 return Inst<U32>(Opcode::FPExp2NotReduced, value);
305}
306
307U32 IREmitter::FPLog2(const U32& value) {
308 return Inst<U32>(Opcode::FPLog2, value);
309}
310
311U32U64 IREmitter::FPRecip(const U32U64& value) {
312 switch (value.Type()) {
313 case Type::U32:
314 return Inst<U32>(Opcode::FPRecip32, value);
315 case Type::U64:
316 return Inst<U64>(Opcode::FPRecip64, value);
317 default:
318 ThrowInvalidType(value.Type());
319 }
320}
321
322U32U64 IREmitter::FPRecipSqrt(const U32U64& value) {
323 switch (value.Type()) {
324 case Type::U32:
325 return Inst<U32>(Opcode::FPRecipSqrt32, value);
326 case Type::U64:
327 return Inst<U64>(Opcode::FPRecipSqrt64, value);
328 default:
329 ThrowInvalidType(value.Type());
330 }
331}
332
333U32 IREmitter::FPSinNotReduced(const U32& value) {
334 return Inst<U32>(Opcode::FPSinNotReduced, value);
335}
336
337U32 IREmitter::FPSqrt(const U32& value) {
338 return Inst<U32>(Opcode::FPSqrt, value);
339}
340
341U16U32U64 IREmitter::FPSaturate(const U16U32U64& value) {
342 switch (value.Type()) {
343 case Type::U16:
344 return Inst<U16>(Opcode::FPSaturate16, value);
345 case Type::U32:
346 return Inst<U32>(Opcode::FPSaturate32, value);
347 case Type::U64:
348 return Inst<U64>(Opcode::FPSaturate64, value);
349 default:
350 ThrowInvalidType(value.Type());
351 }
352}
353
354U16U32U64 IREmitter::FPRoundEven(const U16U32U64& value) {
355 switch (value.Type()) {
356 case Type::U16:
357 return Inst<U16>(Opcode::FPRoundEven16, value);
358 case Type::U32:
359 return Inst<U32>(Opcode::FPRoundEven32, value);
360 case Type::U64:
361 return Inst<U64>(Opcode::FPRoundEven64, value);
362 default:
363 ThrowInvalidType(value.Type());
364 }
365}
366
367U16U32U64 IREmitter::FPFloor(const U16U32U64& value) {
368 switch (value.Type()) {
369 case Type::U16:
370 return Inst<U16>(Opcode::FPFloor16, value);
371 case Type::U32:
372 return Inst<U32>(Opcode::FPFloor32, value);
373 case Type::U64:
374 return Inst<U64>(Opcode::FPFloor64, value);
375 default:
376 ThrowInvalidType(value.Type());
377 }
378}
379
380U16U32U64 IREmitter::FPCeil(const U16U32U64& value) {
381 switch (value.Type()) {
382 case Type::U16:
383 return Inst<U16>(Opcode::FPCeil16, value);
384 case Type::U32:
385 return Inst<U32>(Opcode::FPCeil32, value);
386 case Type::U64:
387 return Inst<U64>(Opcode::FPCeil64, value);
388 default:
389 ThrowInvalidType(value.Type());
390 }
391}
392
393U16U32U64 IREmitter::FPTrunc(const U16U32U64& value) {
394 switch (value.Type()) {
395 case Type::U16:
396 return Inst<U16>(Opcode::FPTrunc16, value);
397 case Type::U32:
398 return Inst<U32>(Opcode::FPTrunc32, value);
399 case Type::U64:
400 return Inst<U64>(Opcode::FPTrunc64, value);
401 default:
402 ThrowInvalidType(value.Type());
403 }
404}
405
406U1 IREmitter::LogicalOr(const U1& a, const U1& b) {
407 return Inst<U1>(Opcode::LogicalOr, a, b);
408}
409
410U1 IREmitter::LogicalAnd(const U1& a, const U1& b) {
411 return Inst<U1>(Opcode::LogicalAnd, a, b);
412}
413
414U1 IREmitter::LogicalNot(const U1& value) {
415 return Inst<U1>(Opcode::LogicalNot, value);
416}
417
418U32U64 IREmitter::ConvertFToS(size_t bitsize, const U16U32U64& value) {
419 switch (bitsize) {
420 case 16:
421 switch (value.Type()) {
422 case Type::U16:
423 return Inst<U32>(Opcode::ConvertS16F16, value);
424 case Type::U32:
425 return Inst<U32>(Opcode::ConvertS16F32, value);
426 case Type::U64:
427 return Inst<U32>(Opcode::ConvertS16F64, value);
428 default:
429 ThrowInvalidType(value.Type());
430 }
431 case 32:
432 switch (value.Type()) {
433 case Type::U16:
434 return Inst<U32>(Opcode::ConvertS32F16, value);
435 case Type::U32:
436 return Inst<U32>(Opcode::ConvertS32F32, value);
437 case Type::U64:
438 return Inst<U32>(Opcode::ConvertS32F64, value);
439 default:
440 ThrowInvalidType(value.Type());
441 }
442 case 64:
443 switch (value.Type()) {
444 case Type::U16:
445 return Inst<U64>(Opcode::ConvertS64F16, value);
446 case Type::U32:
447 return Inst<U64>(Opcode::ConvertS64F32, value);
448 case Type::U64:
449 return Inst<U64>(Opcode::ConvertS64F64, value);
450 default:
451 ThrowInvalidType(value.Type());
452 }
453 default:
454 throw InvalidArgument("Invalid destination bitsize {}", bitsize);
455 }
456}
457
458U32U64 IREmitter::ConvertFToU(size_t bitsize, const U16U32U64& value) {
459 switch (bitsize) {
460 case 16:
461 switch (value.Type()) {
462 case Type::U16:
463 return Inst<U32>(Opcode::ConvertU16F16, value);
464 case Type::U32:
465 return Inst<U32>(Opcode::ConvertU16F32, value);
466 case Type::U64:
467 return Inst<U32>(Opcode::ConvertU16F64, value);
468 default:
469 ThrowInvalidType(value.Type());
470 }
471 case 32:
472 switch (value.Type()) {
473 case Type::U16:
474 return Inst<U32>(Opcode::ConvertU32F16, value);
475 case Type::U32:
476 return Inst<U32>(Opcode::ConvertU32F32, value);
477 case Type::U64:
478 return Inst<U32>(Opcode::ConvertU32F64, value);
479 default:
480 ThrowInvalidType(value.Type());
481 }
482 case 64:
483 switch (value.Type()) {
484 case Type::U16:
485 return Inst<U64>(Opcode::ConvertU64F16, value);
486 case Type::U32:
487 return Inst<U64>(Opcode::ConvertU64F32, value);
488 case Type::U64:
489 return Inst<U64>(Opcode::ConvertU64F64, value);
490 default:
491 ThrowInvalidType(value.Type());
492 }
493 default:
494 throw InvalidArgument("Invalid destination bitsize {}", bitsize);
495 }
496}
497
498U32U64 IREmitter::ConvertFToI(size_t bitsize, bool is_signed, const U16U32U64& value) {
499 if (is_signed) {
500 return ConvertFToS(bitsize, value);
501 } else {
502 return ConvertFToU(bitsize, value);
503 }
504}
505
506U32U64 IREmitter::ConvertU(size_t bitsize, const U32U64& value) {
507 switch (bitsize) {
508 case 32:
509 switch (value.Type()) {
510 case Type::U32:
511 // Nothing to do
512 return value;
513 case Type::U64:
514 return Inst<U32>(Opcode::ConvertU32U64, value);
515 default:
516 break;
517 }
518 break;
519 case 64:
520 switch (value.Type()) {
521 case Type::U32:
522 // Nothing to do
523 return value;
524 case Type::U64:
525 return Inst<U64>(Opcode::ConvertU64U32, value);
526 default:
527 break;
528 }
529 }
530 throw NotImplementedException("Conversion from {} to {} bits", value.Type(), bitsize);
531}
532
533} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "shader_recompiler/frontend/ir/attribute.h"
8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/value.h"
10
11namespace Shader::IR {
12
13class IREmitter {
14public:
15 explicit IREmitter(Block& block_) : block{block_}, insertion_point{block.end()} {}
16
17 Block& block;
18
19 [[nodiscard]] U1 Imm1(bool value) const;
20 [[nodiscard]] U8 Imm8(u8 value) const;
21 [[nodiscard]] U16 Imm16(u16 value) const;
22 [[nodiscard]] U32 Imm32(u32 value) const;
23 [[nodiscard]] U32 Imm32(s32 value) const;
24 [[nodiscard]] U32 Imm32(f32 value) const;
25 [[nodiscard]] U64 Imm64(u64 value) const;
26 [[nodiscard]] U64 Imm64(f64 value) const;
27
28 void Branch(IR::Block* label);
29 void BranchConditional(const U1& cond, IR::Block* true_label, IR::Block* false_label);
30 void Exit();
31 void Return();
32 void Unreachable();
33
34 [[nodiscard]] U32 GetReg(IR::Reg reg);
35 void SetReg(IR::Reg reg, const U32& value);
36
37 [[nodiscard]] U1 GetPred(IR::Pred pred, bool is_negated = false);
38 void SetPred(IR::Pred pred, const U1& value);
39
40 [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset);
41
42 [[nodiscard]] U1 GetZFlag();
43 [[nodiscard]] U1 GetSFlag();
44 [[nodiscard]] U1 GetCFlag();
45 [[nodiscard]] U1 GetOFlag();
46
47 void SetZFlag(const U1& value);
48 void SetSFlag(const U1& value);
49 void SetCFlag(const U1& value);
50 void SetOFlag(const U1& value);
51
52 [[nodiscard]] U32 GetAttribute(IR::Attribute attribute);
53 void SetAttribute(IR::Attribute attribute, const U32& value);
54
55 void WriteGlobalU8(const U64& address, const U32& value);
56 void WriteGlobalS8(const U64& address, const U32& value);
57 void WriteGlobalU16(const U64& address, const U32& value);
58 void WriteGlobalS16(const U64& address, const U32& value);
59 void WriteGlobal32(const U64& address, const U32& value);
60 void WriteGlobal64(const U64& address, const IR::Value& vector);
61 void WriteGlobal128(const U64& address, const IR::Value& vector);
62
63 [[nodiscard]] U1 GetZeroFromOp(const Value& op);
64 [[nodiscard]] U1 GetSignFromOp(const Value& op);
65 [[nodiscard]] U1 GetCarryFromOp(const Value& op);
66 [[nodiscard]] U1 GetOverflowFromOp(const Value& op);
67
68 [[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2);
69 [[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3);
70 [[nodiscard]] Value CompositeConstruct(const UAny& e1, const UAny& e2, const UAny& e3,
71 const UAny& e4);
72 [[nodiscard]] UAny CompositeExtract(const Value& vector, size_t element);
73
74 [[nodiscard]] U64 PackUint2x32(const Value& vector);
75 [[nodiscard]] Value UnpackUint2x32(const U64& value);
76
77 [[nodiscard]] U32 PackFloat2x16(const Value& vector);
78 [[nodiscard]] Value UnpackFloat2x16(const U32& value);
79
80 [[nodiscard]] U64 PackDouble2x32(const Value& vector);
81 [[nodiscard]] Value UnpackDouble2x32(const U64& value);
82
83 [[nodiscard]] U16U32U64 FPAdd(const U16U32U64& a, const U16U32U64& b);
84 [[nodiscard]] U16U32U64 FPMul(const U16U32U64& a, const U16U32U64& b);
85
86 [[nodiscard]] U16U32U64 FPAbs(const U16U32U64& value);
87 [[nodiscard]] U16U32U64 FPNeg(const U16U32U64& value);
88 [[nodiscard]] U16U32U64 FPAbsNeg(const U16U32U64& value, bool abs, bool neg);
89
90 [[nodiscard]] U32 FPCosNotReduced(const U32& value);
91 [[nodiscard]] U32 FPExp2NotReduced(const U32& value);
92 [[nodiscard]] U32 FPLog2(const U32& value);
93 [[nodiscard]] U32U64 FPRecip(const U32U64& value);
94 [[nodiscard]] U32U64 FPRecipSqrt(const U32U64& value);
95 [[nodiscard]] U32 FPSinNotReduced(const U32& value);
96 [[nodiscard]] U32 FPSqrt(const U32& value);
97 [[nodiscard]] U16U32U64 FPSaturate(const U16U32U64& value);
98 [[nodiscard]] U16U32U64 FPRoundEven(const U16U32U64& value);
99 [[nodiscard]] U16U32U64 FPFloor(const U16U32U64& value);
100 [[nodiscard]] U16U32U64 FPCeil(const U16U32U64& value);
101 [[nodiscard]] U16U32U64 FPTrunc(const U16U32U64& value);
102
103 [[nodiscard]] U1 LogicalOr(const U1& a, const U1& b);
104 [[nodiscard]] U1 LogicalAnd(const U1& a, const U1& b);
105 [[nodiscard]] U1 LogicalNot(const U1& value);
106
107 [[nodiscard]] U32U64 ConvertFToS(size_t bitsize, const U16U32U64& value);
108 [[nodiscard]] U32U64 ConvertFToU(size_t bitsize, const U16U32U64& value);
109 [[nodiscard]] U32U64 ConvertFToI(size_t bitsize, bool is_signed, const U16U32U64& value);
110
111 [[nodiscard]] U32U64 ConvertU(size_t bitsize, const U32U64& value);
112
113private:
114 IR::Block::iterator insertion_point;
115
116 template <typename T = Value, typename... Args>
117 T Inst(Opcode op, Args... args) {
118 auto it{block.PrependNewInst(insertion_point, op, {Value{args}...})};
119 return T{Value{&*it}};
120 }
121};
122
123} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/exception.h"
6#include "shader_recompiler/frontend/ir/microinstruction.h"
7#include "shader_recompiler/frontend/ir/type.h"
8
9namespace Shader::IR {
10
11static void CheckPseudoInstruction(IR::Inst* inst, IR::Opcode opcode) {
12 if (inst && inst->Opcode() != opcode) {
13 throw LogicError("Invalid pseudo-instruction");
14 }
15}
16
17static void SetPseudoInstruction(IR::Inst*& dest_inst, IR::Inst* pseudo_inst) {
18 if (dest_inst) {
19 throw LogicError("Only one of each type of pseudo-op allowed");
20 }
21 dest_inst = pseudo_inst;
22}
23
24static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) {
25 if (inst->Opcode() != expected_opcode) {
26 throw LogicError("Undoing use of invalid pseudo-op");
27 }
28 inst = nullptr;
29}
30
31bool Inst::MayHaveSideEffects() const noexcept {
32 switch (op) {
33 case Opcode::SetAttribute:
34 case Opcode::SetAttributeIndexed:
35 case Opcode::WriteGlobalU8:
36 case Opcode::WriteGlobalS8:
37 case Opcode::WriteGlobalU16:
38 case Opcode::WriteGlobalS16:
39 case Opcode::WriteGlobal32:
40 case Opcode::WriteGlobal64:
41 case Opcode::WriteGlobal128:
42 return true;
43 default:
44 return false;
45 }
46}
47
48bool Inst::IsPseudoInstruction() const noexcept {
49 switch (op) {
50 case Opcode::GetZeroFromOp:
51 case Opcode::GetSignFromOp:
52 case Opcode::GetCarryFromOp:
53 case Opcode::GetOverflowFromOp:
54 case Opcode::GetZSCOFromOp:
55 return true;
56 default:
57 return false;
58 }
59}
60
61bool Inst::HasAssociatedPseudoOperation() const noexcept {
62 return zero_inst || sign_inst || carry_inst || overflow_inst || zsco_inst;
63}
64
65Inst* Inst::GetAssociatedPseudoOperation(IR::Opcode opcode) {
66 // This is faster than doing a search through the block.
67 switch (opcode) {
68 case Opcode::GetZeroFromOp:
69 CheckPseudoInstruction(zero_inst, Opcode::GetZeroFromOp);
70 return zero_inst;
71 case Opcode::GetSignFromOp:
72 CheckPseudoInstruction(sign_inst, Opcode::GetSignFromOp);
73 return sign_inst;
74 case Opcode::GetCarryFromOp:
75 CheckPseudoInstruction(carry_inst, Opcode::GetCarryFromOp);
76 return carry_inst;
77 case Opcode::GetOverflowFromOp:
78 CheckPseudoInstruction(overflow_inst, Opcode::GetOverflowFromOp);
79 return overflow_inst;
80 case Opcode::GetZSCOFromOp:
81 CheckPseudoInstruction(zsco_inst, Opcode::GetZSCOFromOp);
82 return zsco_inst;
83 default:
84 throw InvalidArgument("{} is not a pseudo-instruction", opcode);
85 }
86}
87
88size_t Inst::NumArgs() const {
89 return NumArgsOf(op);
90}
91
92IR::Type Inst::Type() const {
93 return TypeOf(op);
94}
95
96Value Inst::Arg(size_t index) const {
97 if (index >= NumArgsOf(op)) {
98 throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
99 }
100 return args[index];
101}
102
103void Inst::SetArg(size_t index, Value value) {
104 if (index >= NumArgsOf(op)) {
105 throw InvalidArgument("Out of bounds argument index {} in opcode {}", index, op);
106 }
107 if (!args[index].IsImmediate()) {
108 UndoUse(args[index]);
109 }
110 if (!value.IsImmediate()) {
111 Use(value);
112 }
113 args[index] = value;
114}
115
116void Inst::Invalidate() {
117 ClearArgs();
118 op = Opcode::Void;
119}
120
121void Inst::ClearArgs() {
122 for (auto& value : args) {
123 if (!value.IsImmediate()) {
124 UndoUse(value);
125 }
126 value = {};
127 }
128}
129
130void Inst::ReplaceUsesWith(Value replacement) {
131 Invalidate();
132
133 op = Opcode::Identity;
134
135 if (!replacement.IsImmediate()) {
136 Use(replacement);
137 }
138 args[0] = replacement;
139}
140
141void Inst::Use(const Value& value) {
142 ++value.Inst()->use_count;
143
144 switch (op) {
145 case Opcode::GetZeroFromOp:
146 SetPseudoInstruction(value.Inst()->zero_inst, this);
147 break;
148 case Opcode::GetSignFromOp:
149 SetPseudoInstruction(value.Inst()->sign_inst, this);
150 break;
151 case Opcode::GetCarryFromOp:
152 SetPseudoInstruction(value.Inst()->carry_inst, this);
153 break;
154 case Opcode::GetOverflowFromOp:
155 SetPseudoInstruction(value.Inst()->overflow_inst, this);
156 break;
157 case Opcode::GetZSCOFromOp:
158 SetPseudoInstruction(value.Inst()->zsco_inst, this);
159 break;
160 default:
161 break;
162 }
163}
164
165void Inst::UndoUse(const Value& value) {
166 --value.Inst()->use_count;
167
168 switch (op) {
169 case Opcode::GetZeroFromOp:
170 RemovePseudoInstruction(value.Inst()->zero_inst, Opcode::GetZeroFromOp);
171 break;
172 case Opcode::GetSignFromOp:
173 RemovePseudoInstruction(value.Inst()->sign_inst, Opcode::GetSignFromOp);
174 break;
175 case Opcode::GetCarryFromOp:
176 RemovePseudoInstruction(value.Inst()->carry_inst, Opcode::GetCarryFromOp);
177 break;
178 case Opcode::GetOverflowFromOp:
179 RemovePseudoInstruction(value.Inst()->overflow_inst, Opcode::GetOverflowFromOp);
180 break;
181 case Opcode::GetZSCOFromOp:
182 RemovePseudoInstruction(value.Inst()->zsco_inst, Opcode::GetZSCOFromOp);
183 break;
184 default:
185 break;
186 }
187}
188
189} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <array>
8
9#include <boost/intrusive/list.hpp>
10
11#include "common/common_types.h"
12#include "shader_recompiler/frontend/ir/opcode.h"
13#include "shader_recompiler/frontend/ir/type.h"
14#include "shader_recompiler/frontend/ir/value.h"
15
16namespace Shader::IR {
17
18constexpr size_t MAX_ARG_COUNT = 4;
19
20class Inst : public boost::intrusive::list_base_hook<> {
21public:
22 explicit Inst(Opcode op_) noexcept : op(op_) {}
23
24 /// Get the number of uses this instruction has.
25 [[nodiscard]] int UseCount() const noexcept {
26 return use_count;
27 }
28
29 /// Determines whether this instruction has uses or not.
30 [[nodiscard]] bool HasUses() const noexcept {
31 return use_count > 0;
32 }
33
34 /// Get the opcode this microinstruction represents.
35 [[nodiscard]] IR::Opcode Opcode() const noexcept {
36 return op;
37 }
38
39 /// Determines whether or not this instruction may have side effects.
40 [[nodiscard]] bool MayHaveSideEffects() const noexcept;
41
42 /// Determines whether or not this instruction is a pseudo-instruction.
43 /// Pseudo-instructions depend on their parent instructions for their semantics.
44 [[nodiscard]] bool IsPseudoInstruction() const noexcept;
45
46 /// Determines if there is a pseudo-operation associated with this instruction.
47 [[nodiscard]] bool HasAssociatedPseudoOperation() const noexcept;
48 /// Gets a pseudo-operation associated with this instruction
49 [[nodiscard]] Inst* GetAssociatedPseudoOperation(IR::Opcode opcode);
50
51 /// Get the number of arguments this instruction has.
52 [[nodiscard]] size_t NumArgs() const;
53
54 /// Get the type this instruction returns.
55 [[nodiscard]] IR::Type Type() const;
56
57 /// Get the value of a given argument index.
58 [[nodiscard]] Value Arg(size_t index) const;
59 /// Set the value of a given argument index.
60 void SetArg(size_t index, Value value);
61
62 void Invalidate();
63 void ClearArgs();
64
65 void ReplaceUsesWith(Value replacement);
66
67private:
68 void Use(const Value& value);
69 void UndoUse(const Value& value);
70
71 IR::Opcode op{};
72 int use_count{};
73 std::array<Value, MAX_ARG_COUNT> args{};
74 Inst* zero_inst{};
75 Inst* sign_inst{};
76 Inst* carry_inst{};
77 Inst* overflow_inst{};
78 Inst* zsco_inst{};
79 u64 flags{};
80};
81
82} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <string_view>
8
9#include "shader_recompiler/exception.h"
10#include "shader_recompiler/frontend/ir/opcode.h"
11
12namespace Shader::IR {
13namespace {
14struct OpcodeMeta {
15 std::string_view name;
16 Type type;
17 std::array<Type, 4> arg_types;
18};
19
20using enum Type;
21
22constexpr std::array META_TABLE{
23#define OPCODE(name_token, type_token, ...) \
24 OpcodeMeta{ \
25 .name{#name_token}, \
26 .type{type_token}, \
27 .arg_types{__VA_ARGS__}, \
28 },
29#include "opcode.inc"
30#undef OPCODE
31};
32
33void ValidateOpcode(Opcode op) {
34 const size_t raw{static_cast<size_t>(op)};
35 if (raw >= META_TABLE.size()) {
36 throw InvalidArgument("Invalid opcode with raw value {}", raw);
37 }
38}
39} // Anonymous namespace
40
41Type TypeOf(Opcode op) {
42 ValidateOpcode(op);
43 return META_TABLE[static_cast<size_t>(op)].type;
44}
45
46size_t NumArgsOf(Opcode op) {
47 ValidateOpcode(op);
48 const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
49 const auto distance{std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void))};
50 return static_cast<size_t>(distance);
51}
52
53Type ArgTypeOf(Opcode op, size_t arg_index) {
54 ValidateOpcode(op);
55 const auto& arg_types{META_TABLE[static_cast<size_t>(op)].arg_types};
56 if (arg_index >= arg_types.size() || arg_types[arg_index] == Type::Void) {
57 throw InvalidArgument("Out of bounds argument");
58 }
59 return arg_types[arg_index];
60}
61
62std::string_view NameOf(Opcode op) {
63 ValidateOpcode(op);
64 return META_TABLE[static_cast<size_t>(op)].name;
65}
66
67} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string_view>
8
9#include <fmt/format.h>
10
11#include "shader_recompiler/frontend/ir/type.h"
12
13namespace Shader::IR {
14
15enum class Opcode {
16#define OPCODE(name, ...) name,
17#include "opcode.inc"
18#undef OPCODE
19};
20
21/// Get return type of an opcode
22[[nodiscard]] Type TypeOf(Opcode op);
23
24/// Get the number of arguments an opcode accepts
25[[nodiscard]] size_t NumArgsOf(Opcode op);
26
27/// Get the required type of an argument of an opcode
28[[nodiscard]] Type ArgTypeOf(Opcode op, size_t arg_index);
29
30/// Get the name of an opcode
31[[nodiscard]] std::string_view NameOf(Opcode op);
32
33} // namespace Shader::IR
34
35template <>
36struct fmt::formatter<Shader::IR::Opcode> {
37 constexpr auto parse(format_parse_context& ctx) {
38 return ctx.begin();
39 }
40 template <typename FormatContext>
41 auto format(const Shader::IR::Opcode& op, FormatContext& ctx) {
42 return format_to(ctx.out(), "{}", Shader::IR::NameOf(op));
43 }
44};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5// opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ...
6OPCODE(Void, Void, )
7OPCODE(Identity, Opaque, Opaque, )
8
9// Control flow
10OPCODE(Branch, Void, Label, )
11OPCODE(BranchConditional, Void, U1, Label, Label, )
12OPCODE(Exit, Void, )
13OPCODE(Return, Void, )
14OPCODE(Unreachable, Void, )
15
16// Context getters/setters
17OPCODE(GetRegister, U32, Reg, )
18OPCODE(SetRegister, Void, Reg, U32, )
19OPCODE(GetPred, U1, Pred, )
20OPCODE(SetPred, Void, Pred, U1, )
21OPCODE(GetCbuf, U32, U32, U32, )
22OPCODE(GetAttribute, U32, Attribute, )
23OPCODE(SetAttribute, U32, Attribute, )
24OPCODE(GetAttributeIndexed, U32, U32, )
25OPCODE(SetAttributeIndexed, U32, U32, )
26OPCODE(GetZSCORaw, U32, )
27OPCODE(SetZSCORaw, Void, U32, )
28OPCODE(SetZSCO, Void, ZSCO, )
29OPCODE(GetZFlag, U1, Void, )
30OPCODE(GetSFlag, U1, Void, )
31OPCODE(GetCFlag, U1, Void, )
32OPCODE(GetOFlag, U1, Void, )
33OPCODE(SetZFlag, Void, U1, )
34OPCODE(SetSFlag, Void, U1, )
35OPCODE(SetCFlag, Void, U1, )
36OPCODE(SetOFlag, Void, U1, )
37
38// Memory operations
39OPCODE(WriteGlobalU8, Void, U64, U32, )
40OPCODE(WriteGlobalS8, Void, U64, U32, )
41OPCODE(WriteGlobalU16, Void, U64, U32, )
42OPCODE(WriteGlobalS16, Void, U64, U32, )
43OPCODE(WriteGlobal32, Void, U64, U32, )
44OPCODE(WriteGlobal64, Void, U64, Opaque, )
45OPCODE(WriteGlobal128, Void, U64, Opaque, )
46
47// Vector utility
48OPCODE(CompositeConstruct2, Opaque, Opaque, Opaque, )
49OPCODE(CompositeConstruct3, Opaque, Opaque, Opaque, Opaque, )
50OPCODE(CompositeConstruct4, Opaque, Opaque, Opaque, Opaque, Opaque, )
51OPCODE(CompositeExtract, Opaque, Opaque, U32, )
52
53// Bitwise conversions
54OPCODE(PackUint2x32, U64, Opaque, )
55OPCODE(UnpackUint2x32, Opaque, U64, )
56OPCODE(PackFloat2x16, U32, Opaque, )
57OPCODE(UnpackFloat2x16, Opaque, U32, )
58OPCODE(PackDouble2x32, U64, Opaque, )
59OPCODE(UnpackDouble2x32, Opaque, U64, )
60
61// Pseudo-operation, handled specially at final emit
62OPCODE(GetZeroFromOp, U1, Opaque, )
63OPCODE(GetSignFromOp, U1, Opaque, )
64OPCODE(GetCarryFromOp, U1, Opaque, )
65OPCODE(GetOverflowFromOp, U1, Opaque, )
66OPCODE(GetZSCOFromOp, ZSCO, Opaque, )
67
68// Floating-point operations
69OPCODE(FPAbs16, U16, U16 )
70OPCODE(FPAbs32, U32, U32 )
71OPCODE(FPAbs64, U64, U64 )
72OPCODE(FPAdd16, U16, U16, U16 )
73OPCODE(FPAdd32, U32, U32, U32 )
74OPCODE(FPAdd64, U64, U64, U64 )
75OPCODE(FPFma16, U16, U16, U16 )
76OPCODE(FPFma32, U32, U32, U32 )
77OPCODE(FPFma64, U64, U64, U64 )
78OPCODE(FPMax32, U32, U32, U32 )
79OPCODE(FPMax64, U64, U64, U64 )
80OPCODE(FPMin32, U32, U32, U32 )
81OPCODE(FPMin64, U64, U64, U64 )
82OPCODE(FPMul16, U16, U16, U16 )
83OPCODE(FPMul32, U32, U32, U32 )
84OPCODE(FPMul64, U64, U64, U64 )
85OPCODE(FPNeg16, U16, U16 )
86OPCODE(FPNeg32, U32, U32 )
87OPCODE(FPNeg64, U64, U64 )
88OPCODE(FPRecip32, U32, U32 )
89OPCODE(FPRecip64, U64, U64 )
90OPCODE(FPRecipSqrt32, U32, U32 )
91OPCODE(FPRecipSqrt64, U64, U64 )
92OPCODE(FPSqrt, U32, U32 )
93OPCODE(FPSin, U32, U32 )
94OPCODE(FPSinNotReduced, U32, U32 )
95OPCODE(FPExp2, U32, U32 )
96OPCODE(FPExp2NotReduced, U32, U32 )
97OPCODE(FPCos, U32, U32 )
98OPCODE(FPCosNotReduced, U32, U32 )
99OPCODE(FPLog2, U32, U32 )
100OPCODE(FPSaturate16, U16, U16 )
101OPCODE(FPSaturate32, U32, U32 )
102OPCODE(FPSaturate64, U64, U64 )
103OPCODE(FPRoundEven16, U16, U16 )
104OPCODE(FPRoundEven32, U32, U32 )
105OPCODE(FPRoundEven64, U64, U64 )
106OPCODE(FPFloor16, U16, U16 )
107OPCODE(FPFloor32, U32, U32 )
108OPCODE(FPFloor64, U64, U64 )
109OPCODE(FPCeil16, U16, U16 )
110OPCODE(FPCeil32, U32, U32 )
111OPCODE(FPCeil64, U64, U64 )
112OPCODE(FPTrunc16, U16, U16 )
113OPCODE(FPTrunc32, U32, U32 )
114OPCODE(FPTrunc64, U64, U64 )
115
116// Logical operations
117OPCODE(LogicalOr, U1, U1, U1, )
118OPCODE(LogicalAnd, U1, U1, U1, )
119OPCODE(LogicalNot, U1, U1, )
120
121// Conversion operations
122OPCODE(ConvertS16F16, U32, U16, )
123OPCODE(ConvertS16F32, U32, U32, )
124OPCODE(ConvertS16F64, U32, U64, )
125OPCODE(ConvertS32F16, U32, U16, )
126OPCODE(ConvertS32F32, U32, U32, )
127OPCODE(ConvertS32F64, U32, U64, )
128OPCODE(ConvertS64F16, U64, U16, )
129OPCODE(ConvertS64F32, U64, U32, )
130OPCODE(ConvertS64F64, U64, U64, )
131OPCODE(ConvertU16F16, U32, U16, )
132OPCODE(ConvertU16F32, U32, U32, )
133OPCODE(ConvertU16F64, U32, U64, )
134OPCODE(ConvertU32F16, U32, U16, )
135OPCODE(ConvertU32F32, U32, U32, )
136OPCODE(ConvertU32F64, U32, U64, )
137OPCODE(ConvertU64F16, U64, U16, )
138OPCODE(ConvertU64F32, U64, U32, )
139OPCODE(ConvertU64F64, U64, U64, )
140
141OPCODE(ConvertU64U32, U64, U32, )
142OPCODE(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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <fmt/format.h>
8
9namespace Shader::IR {
10
11enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT };
12
13} // namespace Shader::IR
14
15template <>
16struct fmt::formatter<Shader::IR::Pred> {
17 constexpr auto parse(format_parse_context& ctx) {
18 return ctx.begin();
19 }
20 template <typename FormatContext>
21 auto format(const Shader::IR::Pred& pred, FormatContext& ctx) {
22 if (pred == Shader::IR::Pred::PT) {
23 return fmt::format_to(ctx.out(), "PT");
24 } else {
25 return fmt::format_to(ctx.out(), "P{}", static_cast<int>(pred));
26 }
27 }
28};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <fmt/format.h>
8
9#include "common/common_types.h"
10#include "shader_recompiler/exception.h"
11
12namespace Shader::IR {
13
14enum class Reg : u64 {
15 R0,
16 R1,
17 R2,
18 R3,
19 R4,
20 R5,
21 R6,
22 R7,
23 R8,
24 R9,
25 R10,
26 R11,
27 R12,
28 R13,
29 R14,
30 R15,
31 R16,
32 R17,
33 R18,
34 R19,
35 R20,
36 R21,
37 R22,
38 R23,
39 R24,
40 R25,
41 R26,
42 R27,
43 R28,
44 R29,
45 R30,
46 R31,
47 R32,
48 R33,
49 R34,
50 R35,
51 R36,
52 R37,
53 R38,
54 R39,
55 R40,
56 R41,
57 R42,
58 R43,
59 R44,
60 R45,
61 R46,
62 R47,
63 R48,
64 R49,
65 R50,
66 R51,
67 R52,
68 R53,
69 R54,
70 R55,
71 R56,
72 R57,
73 R58,
74 R59,
75 R60,
76 R61,
77 R62,
78 R63,
79 R64,
80 R65,
81 R66,
82 R67,
83 R68,
84 R69,
85 R70,
86 R71,
87 R72,
88 R73,
89 R74,
90 R75,
91 R76,
92 R77,
93 R78,
94 R79,
95 R80,
96 R81,
97 R82,
98 R83,
99 R84,
100 R85,
101 R86,
102 R87,
103 R88,
104 R89,
105 R90,
106 R91,
107 R92,
108 R93,
109 R94,
110 R95,
111 R96,
112 R97,
113 R98,
114 R99,
115 R100,
116 R101,
117 R102,
118 R103,
119 R104,
120 R105,
121 R106,
122 R107,
123 R108,
124 R109,
125 R110,
126 R111,
127 R112,
128 R113,
129 R114,
130 R115,
131 R116,
132 R117,
133 R118,
134 R119,
135 R120,
136 R121,
137 R122,
138 R123,
139 R124,
140 R125,
141 R126,
142 R127,
143 R128,
144 R129,
145 R130,
146 R131,
147 R132,
148 R133,
149 R134,
150 R135,
151 R136,
152 R137,
153 R138,
154 R139,
155 R140,
156 R141,
157 R142,
158 R143,
159 R144,
160 R145,
161 R146,
162 R147,
163 R148,
164 R149,
165 R150,
166 R151,
167 R152,
168 R153,
169 R154,
170 R155,
171 R156,
172 R157,
173 R158,
174 R159,
175 R160,
176 R161,
177 R162,
178 R163,
179 R164,
180 R165,
181 R166,
182 R167,
183 R168,
184 R169,
185 R170,
186 R171,
187 R172,
188 R173,
189 R174,
190 R175,
191 R176,
192 R177,
193 R178,
194 R179,
195 R180,
196 R181,
197 R182,
198 R183,
199 R184,
200 R185,
201 R186,
202 R187,
203 R188,
204 R189,
205 R190,
206 R191,
207 R192,
208 R193,
209 R194,
210 R195,
211 R196,
212 R197,
213 R198,
214 R199,
215 R200,
216 R201,
217 R202,
218 R203,
219 R204,
220 R205,
221 R206,
222 R207,
223 R208,
224 R209,
225 R210,
226 R211,
227 R212,
228 R213,
229 R214,
230 R215,
231 R216,
232 R217,
233 R218,
234 R219,
235 R220,
236 R221,
237 R222,
238 R223,
239 R224,
240 R225,
241 R226,
242 R227,
243 R228,
244 R229,
245 R230,
246 R231,
247 R232,
248 R233,
249 R234,
250 R235,
251 R236,
252 R237,
253 R238,
254 R239,
255 R240,
256 R241,
257 R242,
258 R243,
259 R244,
260 R245,
261 R246,
262 R247,
263 R248,
264 R249,
265 R250,
266 R251,
267 R252,
268 R253,
269 R254,
270 RZ,
271};
272static_assert(static_cast<int>(Reg::RZ) == 255);
273
274[[nodiscard]] constexpr Reg operator+(Reg reg, int num) {
275 if (reg == Reg::RZ) {
276 // Adding or subtracting registers from RZ yields RZ
277 return Reg::RZ;
278 }
279 const int result{static_cast<int>(reg) + num};
280 if (result >= static_cast<int>(Reg::RZ)) {
281 throw LogicError("Overflow on register arithmetic");
282 }
283 if (result < 0) {
284 throw LogicError("Underflow on register arithmetic");
285 }
286 return static_cast<Reg>(result);
287}
288
289[[nodiscard]] constexpr Reg operator-(Reg reg, int num) {
290 return reg + (-num);
291}
292
293[[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) {
294 return (static_cast<size_t>(reg) / align) * align == static_cast<size_t>(reg);
295}
296
297} // namespace Shader::IR
298
299template <>
300struct fmt::formatter<Shader::IR::Reg> {
301 constexpr auto parse(format_parse_context& ctx) {
302 return ctx.begin();
303 }
304 template <typename FormatContext>
305 auto format(const Shader::IR::Reg& reg, FormatContext& ctx) {
306 if (reg == Shader::IR::Reg::RZ) {
307 return fmt::format_to(ctx.out(), "RZ");
308 } else if (static_cast<int>(reg) >= 0 && static_cast<int>(reg) < 255) {
309 return fmt::format_to(ctx.out(), "R{}", static_cast<int>(reg));
310 } else {
311 throw Shader::LogicError("Invalid register with raw value {}", static_cast<int>(reg));
312 }
313 }
314};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <array>
6#include <string>
7
8#include "shader_recompiler/frontend/ir/type.h"
9
10namespace Shader::IR {
11
12std::string NameOf(Type type) {
13 static constexpr std::array names{
14 "Opaque", "Label", "Reg", "Pred", "Attribute", "U1", "U8", "U16", "U32", "U64", "ZSCO",
15 };
16 const size_t bits{static_cast<size_t>(type)};
17 if (bits == 0) {
18 return "Void";
19 }
20 std::string result;
21 for (size_t i = 0; i < names.size(); i++) {
22 if ((bits & (size_t{1} << i)) != 0) {
23 if (!result.empty()) {
24 result += '|';
25 }
26 result += names[i];
27 }
28 }
29 return result;
30}
31
32bool AreTypesCompatible(Type lhs, Type rhs) noexcept {
33 return lhs == rhs || lhs == Type::Opaque || rhs == Type::Opaque;
34}
35
36} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8
9#include <fmt/format.h>
10
11#include "common/common_funcs.h"
12#include "shader_recompiler/exception.h"
13
14namespace Shader::IR {
15
16enum class Type {
17 Void = 0,
18 Opaque = 1 << 0,
19 Label = 1 << 1,
20 Reg = 1 << 2,
21 Pred = 1 << 3,
22 Attribute = 1 << 4,
23 U1 = 1 << 5,
24 U8 = 1 << 6,
25 U16 = 1 << 7,
26 U32 = 1 << 8,
27 U64 = 1 << 9,
28 ZSCO = 1 << 10,
29};
30DECLARE_ENUM_FLAG_OPERATORS(Type)
31
32[[nodiscard]] std::string NameOf(Type type);
33
34[[nodiscard]] bool AreTypesCompatible(Type lhs, Type rhs) noexcept;
35
36} // namespace Shader::IR
37
38template <>
39struct fmt::formatter<Shader::IR::Type> {
40 constexpr auto parse(format_parse_context& ctx) {
41 return ctx.begin();
42 }
43 template <typename FormatContext>
44 auto format(const Shader::IR::Type& type, FormatContext& ctx) {
45 return fmt::format_to(ctx.out(), "{}", NameOf(type));
46 }
47};
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "shader_recompiler/frontend/ir/microinstruction.h"
6#include "shader_recompiler/frontend/ir/opcode.h"
7#include "shader_recompiler/frontend/ir/value.h"
8
9namespace Shader::IR {
10
11Value::Value(IR::Inst* value) noexcept : type{Type::Opaque}, inst{value} {}
12
13Value::Value(IR::Block* value) noexcept : type{Type::Label}, label{value} {}
14
15Value::Value(IR::Reg value) noexcept : type{Type::Reg}, reg{value} {}
16
17Value::Value(IR::Pred value) noexcept : type{Type::Pred}, pred{value} {}
18
19Value::Value(IR::Attribute value) noexcept : type{Type::Attribute}, attribute{value} {}
20
21Value::Value(bool value) noexcept : type{Type::U1}, imm_u1{value} {}
22
23Value::Value(u8 value) noexcept : type{Type::U8}, imm_u8{value} {}
24
25Value::Value(u16 value) noexcept : type{Type::U16}, imm_u16{value} {}
26
27Value::Value(u32 value) noexcept : type{Type::U32}, imm_u32{value} {}
28
29Value::Value(u64 value) noexcept : type{Type::U64}, imm_u64{value} {}
30
31bool Value::IsIdentity() const noexcept {
32 return type == Type::Opaque && inst->Opcode() == Opcode::Identity;
33}
34
35bool Value::IsEmpty() const noexcept {
36 return type == Type::Void;
37}
38
39bool Value::IsImmediate() const noexcept {
40 if (IsIdentity()) {
41 return inst->Arg(0).IsImmediate();
42 }
43 return type != Type::Opaque;
44}
45
46bool Value::IsLabel() const noexcept {
47 return type == Type::Label;
48}
49
50IR::Type Value::Type() const noexcept {
51 if (IsIdentity()) {
52 return inst->Arg(0).Type();
53 }
54 if (type == Type::Opaque) {
55 return inst->Type();
56 }
57 return type;
58}
59
60IR::Inst* Value::Inst() const {
61 ValidateAccess(Type::Opaque);
62 return inst;
63}
64
65IR::Block* Value::Label() const {
66 ValidateAccess(Type::Label);
67 return label;
68}
69
70IR::Inst* Value::InstRecursive() const {
71 ValidateAccess(Type::Opaque);
72 if (IsIdentity()) {
73 return inst->Arg(0).InstRecursive();
74 }
75 return inst;
76}
77
78IR::Reg Value::Reg() const {
79 ValidateAccess(Type::Reg);
80 return reg;
81}
82
83IR::Pred Value::Pred() const {
84 ValidateAccess(Type::Pred);
85 return pred;
86}
87
88IR::Attribute Value::Attribute() const {
89 ValidateAccess(Type::Attribute);
90 return attribute;
91}
92
93bool Value::U1() const {
94 ValidateAccess(Type::U1);
95 return imm_u1;
96}
97
98u8 Value::U8() const {
99 ValidateAccess(Type::U8);
100 return imm_u8;
101}
102
103u16 Value::U16() const {
104 ValidateAccess(Type::U16);
105 return imm_u16;
106}
107
108u32 Value::U32() const {
109 ValidateAccess(Type::U32);
110 return imm_u32;
111}
112
113u64 Value::U64() const {
114 ValidateAccess(Type::U64);
115 return imm_u64;
116}
117
118void Value::ValidateAccess(IR::Type expected) const {
119 if (type != expected) {
120 throw LogicError("Reading {} out of {}", expected, type);
121 }
122}
123
124} // 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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "shader_recompiler/exception.h"
9#include "shader_recompiler/frontend/ir/attribute.h"
10#include "shader_recompiler/frontend/ir/pred.h"
11#include "shader_recompiler/frontend/ir/reg.h"
12#include "shader_recompiler/frontend/ir/type.h"
13
14namespace Shader::IR {
15
16class Block;
17class Inst;
18
19class Value {
20public:
21 Value() noexcept : type{IR::Type::Void}, inst{nullptr} {}
22 explicit Value(IR::Inst* value) noexcept;
23 explicit Value(IR::Block* value) noexcept;
24 explicit Value(IR::Reg value) noexcept;
25 explicit Value(IR::Pred value) noexcept;
26 explicit Value(IR::Attribute value) noexcept;
27 explicit Value(bool value) noexcept;
28 explicit Value(u8 value) noexcept;
29 explicit Value(u16 value) noexcept;
30 explicit Value(u32 value) noexcept;
31 explicit Value(u64 value) noexcept;
32
33 [[nodiscard]] bool IsIdentity() const noexcept;
34 [[nodiscard]] bool IsEmpty() const noexcept;
35 [[nodiscard]] bool IsImmediate() const noexcept;
36 [[nodiscard]] bool IsLabel() const noexcept;
37 [[nodiscard]] IR::Type Type() const noexcept;
38
39 [[nodiscard]] IR::Inst* Inst() const;
40 [[nodiscard]] IR::Block* Label() const;
41 [[nodiscard]] IR::Inst* InstRecursive() const;
42 [[nodiscard]] IR::Reg Reg() const;
43 [[nodiscard]] IR::Pred Pred() const;
44 [[nodiscard]] IR::Attribute Attribute() const;
45 [[nodiscard]] bool U1() const;
46 [[nodiscard]] u8 U8() const;
47 [[nodiscard]] u16 U16() const;
48 [[nodiscard]] u32 U32() const;
49 [[nodiscard]] u64 U64() const;
50
51private:
52 void ValidateAccess(IR::Type expected) const;
53
54 IR::Type type;
55 union {
56 IR::Inst* inst;
57 IR::Block* label;
58 IR::Reg reg;
59 IR::Pred pred;
60 IR::Attribute attribute;
61 bool imm_u1;
62 u8 imm_u8;
63 u16 imm_u16;
64 u32 imm_u32;
65 u64 imm_u64;
66 };
67};
68
69template <IR::Type type_>
70class TypedValue : public Value {
71public:
72 TypedValue() = default;
73
74 template <IR::Type other_type>
75 requires((other_type & type_) != IR::Type::Void) explicit(false)
76 TypedValue(const TypedValue<other_type>& value)
77 : Value(value) {}
78
79 explicit TypedValue(const Value& value) : Value(value) {
80 if ((value.Type() & type_) == IR::Type::Void) {
81 throw InvalidArgument("Incompatible types {} and {}", type_, value.Type());
82 }
83 }
84
85 explicit TypedValue(IR::Inst* inst) : TypedValue(Value(inst)) {}
86};
87
88using U1 = TypedValue<Type::U1>;
89using U8 = TypedValue<Type::U8>;
90using U16 = TypedValue<Type::U16>;
91using U32 = TypedValue<Type::U32>;
92using U64 = TypedValue<Type::U64>;
93using U32U64 = TypedValue<Type::U32 | Type::U64>;
94using U16U32U64 = TypedValue<Type::U16 | Type::U32 | Type::U64>;
95using UAny = TypedValue<Type::U8 | Type::U16 | Type::U32 | Type::U64>;
96using ZSCO = TypedValue<Type::ZSCO>;
97
98} // namespace Shader::IR