summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/CMakeLists.txt86
-rw-r--r--src/shader_recompiler/environment.h14
-rw-r--r--src/shader_recompiler/exception.h42
-rw-r--r--src/shader_recompiler/file_environment.cpp42
-rw-r--r--src/shader_recompiler/file_environment.h21
-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
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.cpp531
-rw-r--r--src/shader_recompiler/frontend/maxwell/control_flow.h137
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.cpp149
-rw-r--r--src/shader_recompiler/frontend/maxwell/decode.h14
-rw-r--r--src/shader_recompiler/frontend/maxwell/instruction.h62
-rw-r--r--src/shader_recompiler/frontend/maxwell/location.h106
-rw-r--r--src/shader_recompiler/frontend/maxwell/maxwell.inc285
-rw-r--r--src/shader_recompiler/frontend/maxwell/opcode.cpp26
-rw-r--r--src/shader_recompiler/frontend/maxwell/opcode.h30
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.cpp69
-rw-r--r--src/shader_recompiler/frontend/maxwell/program.h39
-rw-r--r--src/shader_recompiler/frontend/maxwell/termination_code.cpp79
-rw-r--r--src/shader_recompiler/frontend/maxwell/termination_code.h16
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp15
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp133
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp71
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp79
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/impl.h316
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp92
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp90
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp1105
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp45
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/translate.cpp50
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate/translate.h16
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp23
-rw-r--r--src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp87
-rw-r--r--src/shader_recompiler/ir_opt/identity_removal_pass.cpp37
-rw-r--r--src/shader_recompiler/ir_opt/passes.h16
-rw-r--r--src/shader_recompiler/ir_opt/verification_pass.cpp50
-rw-r--r--src/shader_recompiler/main.cpp60
57 files changed, 7061 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f8ec8fea8..6e66dc1df 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -142,6 +142,7 @@ add_subdirectory(core)
142add_subdirectory(audio_core) 142add_subdirectory(audio_core)
143add_subdirectory(video_core) 143add_subdirectory(video_core)
144add_subdirectory(input_common) 144add_subdirectory(input_common)
145add_subdirectory(shader_recompiler)
145add_subdirectory(tests) 146add_subdirectory(tests)
146 147
147if (ENABLE_SDL2) 148if (ENABLE_SDL2)
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
new file mode 100644
index 000000000..c65846bc4
--- /dev/null
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -0,0 +1,86 @@
1add_executable(shader_recompiler
2 environment.h
3 exception.h
4 file_environment.cpp
5 file_environment.h
6 frontend/ir/attribute.cpp
7 frontend/ir/attribute.h
8 frontend/ir/basic_block.cpp
9 frontend/ir/basic_block.h
10 frontend/ir/condition.cpp
11 frontend/ir/condition.h
12 frontend/ir/flow_test.cpp
13 frontend/ir/flow_test.h
14 frontend/ir/ir_emitter.cpp
15 frontend/ir/ir_emitter.h
16 frontend/ir/microinstruction.cpp
17 frontend/ir/microinstruction.h
18 frontend/ir/opcode.cpp
19 frontend/ir/opcode.h
20 frontend/ir/opcode.inc
21 frontend/ir/pred.h
22 frontend/ir/reg.h
23 frontend/ir/type.cpp
24 frontend/ir/type.h
25 frontend/ir/value.cpp
26 frontend/ir/value.h
27 frontend/maxwell/control_flow.cpp
28 frontend/maxwell/control_flow.h
29 frontend/maxwell/decode.cpp
30 frontend/maxwell/decode.h
31 frontend/maxwell/instruction.h
32 frontend/maxwell/location.h
33 frontend/maxwell/maxwell.inc
34 frontend/maxwell/opcode.cpp
35 frontend/maxwell/opcode.h
36 frontend/maxwell/program.cpp
37 frontend/maxwell/program.h
38 frontend/maxwell/termination_code.cpp
39 frontend/maxwell/termination_code.h
40 frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
41 frontend/maxwell/translate/impl/floating_point_multi_function.cpp
42 frontend/maxwell/translate/impl/impl.cpp
43 frontend/maxwell/translate/impl/impl.h
44 frontend/maxwell/translate/impl/load_store_attribute.cpp
45 frontend/maxwell/translate/impl/load_store_memory.cpp
46 frontend/maxwell/translate/impl/not_implemented.cpp
47 frontend/maxwell/translate/impl/register_move.cpp
48 frontend/maxwell/translate/translate.cpp
49 frontend/maxwell/translate/translate.h
50 ir_opt/dead_code_elimination_pass.cpp
51 ir_opt/get_set_elimination_pass.cpp
52 ir_opt/identity_removal_pass.cpp
53 ir_opt/passes.h
54 ir_opt/verification_pass.cpp
55 main.cpp
56)
57target_link_libraries(shader_recompiler PRIVATE fmt::fmt)
58
59if (MSVC)
60 target_compile_options(shader_recompiler PRIVATE
61 /W4
62 /WX
63 /we4018 # 'expression' : signed/unsigned mismatch
64 /we4244 # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
65 /we4245 # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
66 /we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
67 /we4267 # 'var' : conversion from 'size_t' to 'type', possible loss of data
68 /we4305 # 'context' : truncation from 'type1' to 'type2'
69 /we4800 # Implicit conversion from 'type' to bool. Possible information loss
70 /we4826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior.
71 )
72else()
73 target_compile_options(shader_recompiler PRIVATE
74 -Werror
75 -Werror=conversion
76 -Werror=ignored-qualifiers
77 -Werror=implicit-fallthrough
78 -Werror=shadow
79 -Werror=sign-compare
80 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
81 $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
82 -Werror=unused-variable
83 )
84endif()
85
86create_target_directory_groups(shader_recompiler)
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
new file mode 100644
index 000000000..f6230e817
--- /dev/null
+++ b/src/shader_recompiler/environment.h
@@ -0,0 +1,14 @@
1#pragma once
2
3#include "common/common_types.h"
4
5namespace Shader {
6
7class Environment {
8public:
9 virtual ~Environment() = default;
10
11 [[nodiscard]] virtual u64 ReadInstruction(u32 address) const = 0;
12};
13
14} // namespace Shader
diff --git a/src/shader_recompiler/exception.h b/src/shader_recompiler/exception.h
new file mode 100644
index 000000000..6fe620801
--- /dev/null
+++ b/src/shader_recompiler/exception.h
@@ -0,0 +1,42 @@
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 <stdexcept>
8#include <utility>
9
10#include <fmt/format.h>
11
12namespace Shader {
13
14class LogicError : public std::logic_error {
15public:
16 template <typename... Args>
17 LogicError(const char* message, Args&&... args)
18 : std::logic_error{fmt::format(message, std::forward<Args>(args)...)} {}
19};
20
21class RuntimeError : public std::runtime_error {
22public:
23 template <typename... Args>
24 RuntimeError(const char* message, Args&&... args)
25 : std::runtime_error{fmt::format(message, std::forward<Args>(args)...)} {}
26};
27
28class NotImplementedException : public std::logic_error {
29public:
30 template <typename... Args>
31 NotImplementedException(const char* message, Args&&... args)
32 : std::logic_error{fmt::format(message, std::forward<Args>(args)...)} {}
33};
34
35class InvalidArgument : public std::invalid_argument {
36public:
37 template <typename... Args>
38 InvalidArgument(const char* message, Args&&... args)
39 : std::invalid_argument{fmt::format(message, std::forward<Args>(args)...)} {}
40};
41
42} // namespace Shader
diff --git a/src/shader_recompiler/file_environment.cpp b/src/shader_recompiler/file_environment.cpp
new file mode 100644
index 000000000..b34bf462b
--- /dev/null
+++ b/src/shader_recompiler/file_environment.cpp
@@ -0,0 +1,42 @@
1#include <cstdio>
2
3#include "exception.h"
4#include "file_environment.h"
5
6namespace Shader {
7
8FileEnvironment::FileEnvironment(const char* path) {
9 std::FILE* const file{std::fopen(path, "rb")};
10 if (!file) {
11 throw RuntimeError("Failed to open file='{}'", path);
12 }
13 std::fseek(file, 0, SEEK_END);
14 const long size{std::ftell(file)};
15 std::rewind(file);
16 if (size % 8 != 0) {
17 std::fclose(file);
18 throw RuntimeError("File size={} is not aligned to 8", size);
19 }
20 // TODO: Use a unique_ptr to avoid zero-initializing this
21 const size_t num_inst{static_cast<size_t>(size) / 8};
22 data.resize(num_inst);
23 if (std::fread(data.data(), 8, num_inst, file) != num_inst) {
24 std::fclose(file);
25 throw RuntimeError("Failed to read instructions={} from file='{}'", num_inst, path);
26 }
27 std::fclose(file);
28}
29
30FileEnvironment::~FileEnvironment() = default;
31
32u64 FileEnvironment::ReadInstruction(u32 offset) const {
33 if (offset % 8 != 0) {
34 throw InvalidArgument("offset={} is not aligned to 8", offset);
35 }
36 if (offset / 8 >= static_cast<u32>(data.size())) {
37 throw InvalidArgument("offset={} is out of bounds", offset);
38 }
39 return data[offset / 8];
40}
41
42} // namespace Shader
diff --git a/src/shader_recompiler/file_environment.h b/src/shader_recompiler/file_environment.h
new file mode 100644
index 000000000..c294bc6fa
--- /dev/null
+++ b/src/shader_recompiler/file_environment.h
@@ -0,0 +1,21 @@
1#pragma once
2
3#include <vector>
4
5#include "common/common_types.h"
6#include "environment.h"
7
8namespace Shader {
9
10class FileEnvironment final : public Environment {
11public:
12 explicit FileEnvironment(const char* path);
13 ~FileEnvironment() override;
14
15 u64 ReadInstruction(u32 offset) const override;
16
17private:
18 std::vector<u64> data;
19};
20
21} // namespace Shader
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
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
new file mode 100644
index 000000000..fc4dba826
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp
@@ -0,0 +1,531 @@
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 <optional>
8#include <ranges>
9#include <string>
10#include <utility>
11
12#include <fmt/format.h>
13
14#include "shader_recompiler/exception.h"
15#include "shader_recompiler/frontend/maxwell/control_flow.h"
16#include "shader_recompiler/frontend/maxwell/decode.h"
17#include "shader_recompiler/frontend/maxwell/location.h"
18
19namespace Shader::Maxwell::Flow {
20
21static u32 BranchOffset(Location pc, Instruction inst) {
22 return pc.Offset() + inst.branch.Offset() + 8;
23}
24
25static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) {
26 if (pc <= block.begin || pc >= block.end) {
27 throw InvalidArgument("Invalid address to split={}", pc);
28 }
29 return {
30 Block{
31 .begin{block.begin},
32 .end{pc},
33 .end_class{EndClass::Branch},
34 .id{block.id},
35 .stack{block.stack},
36 .cond{true},
37 .branch_true{new_id},
38 .branch_false{UNREACHABLE_BLOCK_ID},
39 },
40 Block{
41 .begin{pc},
42 .end{block.end},
43 .end_class{block.end_class},
44 .id{new_id},
45 .stack{std::move(block.stack)},
46 .cond{block.cond},
47 .branch_true{block.branch_true},
48 .branch_false{block.branch_false},
49 },
50 };
51}
52
53static Token OpcodeToken(Opcode opcode) {
54 switch (opcode) {
55 case Opcode::PBK:
56 case Opcode::BRK:
57 return Token::PBK;
58 case Opcode::PCNT:
59 case Opcode::CONT:
60 return Token::PBK;
61 case Opcode::PEXIT:
62 case Opcode::EXIT:
63 return Token::PEXIT;
64 case Opcode::PLONGJMP:
65 case Opcode::LONGJMP:
66 return Token::PLONGJMP;
67 case Opcode::PRET:
68 case Opcode::RET:
69 case Opcode::CAL:
70 return Token::PRET;
71 case Opcode::SSY:
72 case Opcode::SYNC:
73 return Token::SSY;
74 default:
75 throw InvalidArgument("{}", opcode);
76 }
77}
78
79static bool IsAbsoluteJump(Opcode opcode) {
80 switch (opcode) {
81 case Opcode::JCAL:
82 case Opcode::JMP:
83 case Opcode::JMX:
84 return true;
85 default:
86 return false;
87 }
88}
89
90static bool HasFlowTest(Opcode opcode) {
91 switch (opcode) {
92 case Opcode::BRA:
93 case Opcode::BRX:
94 case Opcode::EXIT:
95 case Opcode::JMP:
96 case Opcode::JMX:
97 case Opcode::BRK:
98 case Opcode::CONT:
99 case Opcode::LONGJMP:
100 case Opcode::RET:
101 case Opcode::SYNC:
102 return true;
103 case Opcode::CAL:
104 case Opcode::JCAL:
105 return false;
106 default:
107 throw InvalidArgument("Invalid branch {}", opcode);
108 }
109}
110
111static std::string Name(const Block& block) {
112 if (block.begin.IsVirtual()) {
113 return fmt::format("\"Virtual {}\"", block.id);
114 } else {
115 return fmt::format("\"{}\"", block.begin);
116 }
117}
118
119void Stack::Push(Token token, Location target) {
120 entries.push_back({
121 .token{token},
122 .target{target},
123 });
124}
125
126std::pair<Location, Stack> Stack::Pop(Token token) const {
127 const std::optional<Location> pc{Peek(token)};
128 if (!pc) {
129 throw LogicError("Token could not be found");
130 }
131 return {*pc, Remove(token)};
132}
133
134std::optional<Location> Stack::Peek(Token token) const {
135 const auto reverse_entries{entries | std::views::reverse};
136 const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)};
137 if (it == reverse_entries.end()) {
138 return std::nullopt;
139 }
140 return it->target;
141}
142
143Stack Stack::Remove(Token token) const {
144 const auto reverse_entries{entries | std::views::reverse};
145 const auto it{std::ranges::find(reverse_entries, token, &StackEntry::token)};
146 const auto pos{std::distance(reverse_entries.begin(), it)};
147 Stack result;
148 result.entries.insert(result.entries.end(), entries.begin(), entries.end() - pos - 1);
149 return result;
150}
151
152bool Block::Contains(Location pc) const noexcept {
153 return pc >= begin && pc < end;
154}
155
156Function::Function(Location start_address)
157 : entrypoint{start_address}, labels{Label{
158 .address{start_address},
159 .block_id{0},
160 .stack{},
161 }} {}
162
163CFG::CFG(Environment& env_, Location start_address) : env{env_} {
164 functions.emplace_back(start_address);
165 for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) {
166 while (!functions[function_id].labels.empty()) {
167 Function& function{functions[function_id]};
168 Label label{function.labels.back()};
169 function.labels.pop_back();
170 AnalyzeLabel(function_id, label);
171 }
172 }
173}
174
175void CFG::AnalyzeLabel(FunctionId function_id, Label& label) {
176 if (InspectVisitedBlocks(function_id, label)) {
177 // Label address has been visited
178 return;
179 }
180 // Try to find the next block
181 Function* function{&functions[function_id]};
182 Location pc{label.address};
183 const auto next{std::upper_bound(function->blocks.begin(), function->blocks.end(), pc,
184 [function](Location pc, u32 block_index) {
185 return pc < function->blocks_data[block_index].begin;
186 })};
187 const auto next_index{std::distance(function->blocks.begin(), next)};
188 const bool is_last{next == function->blocks.end()};
189 Location next_pc;
190 BlockId next_id{UNREACHABLE_BLOCK_ID};
191 if (!is_last) {
192 next_pc = function->blocks_data[*next].begin;
193 next_id = function->blocks_data[*next].id;
194 }
195 // Insert before the next block
196 Block block{
197 .begin{pc},
198 .end{pc},
199 .end_class{EndClass::Branch},
200 .id{label.block_id},
201 .stack{std::move(label.stack)},
202 .cond{true},
203 .branch_true{UNREACHABLE_BLOCK_ID},
204 .branch_false{UNREACHABLE_BLOCK_ID},
205 };
206 // Analyze instructions until it reaches an already visited block or there's a branch
207 bool is_branch{false};
208 while (is_last || pc < next_pc) {
209 is_branch = AnalyzeInst(block, function_id, pc) == AnalysisState::Branch;
210 if (is_branch) {
211 break;
212 }
213 ++pc;
214 }
215 if (!is_branch) {
216 // If the block finished without a branch,
217 // it means that the next instruction is already visited, jump to it
218 block.end = pc;
219 block.cond = true;
220 block.branch_true = next_id;
221 block.branch_false = UNREACHABLE_BLOCK_ID;
222 }
223 // Function's pointer might be invalid, resolve it again
224 function = &functions[function_id];
225 const u32 new_block_index = static_cast<u32>(function->blocks_data.size());
226 function->blocks.insert(function->blocks.begin() + next_index, new_block_index);
227 function->blocks_data.push_back(std::move(block));
228}
229
230bool CFG::InspectVisitedBlocks(FunctionId function_id, const Label& label) {
231 const Location pc{label.address};
232 Function& function{functions[function_id]};
233 const auto it{std::ranges::find_if(function.blocks, [&function, pc](u32 block_index) {
234 return function.blocks_data[block_index].Contains(pc);
235 })};
236 if (it == function.blocks.end()) {
237 // Address has not been visited
238 return false;
239 }
240 Block& block{function.blocks_data[*it]};
241 if (block.begin == pc) {
242 throw LogicError("Dangling branch");
243 }
244 const u32 first_index{*it};
245 const u32 second_index{static_cast<u32>(function.blocks_data.size())};
246 const std::array new_indices{first_index, second_index};
247 std::array split_blocks{Split(std::move(block), pc, label.block_id)};
248 function.blocks_data[*it] = std::move(split_blocks[0]);
249 function.blocks_data.push_back(std::move(split_blocks[1]));
250 function.blocks.insert(function.blocks.erase(it), new_indices.begin(), new_indices.end());
251 return true;
252}
253
254CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Location pc) {
255 const Instruction inst{env.ReadInstruction(pc.Offset())};
256 const Opcode opcode{Decode(inst.raw)};
257 switch (opcode) {
258 case Opcode::BRA:
259 case Opcode::BRX:
260 case Opcode::JMP:
261 case Opcode::JMX:
262 case Opcode::RET:
263 if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) {
264 return AnalysisState::Continue;
265 }
266 switch (opcode) {
267 case Opcode::BRA:
268 case Opcode::JMP:
269 AnalyzeBRA(block, function_id, pc, inst, IsAbsoluteJump(opcode));
270 break;
271 case Opcode::BRX:
272 case Opcode::JMX:
273 AnalyzeBRX(block, pc, inst, IsAbsoluteJump(opcode));
274 break;
275 case Opcode::RET:
276 block.end_class = EndClass::Return;
277 break;
278 default:
279 break;
280 }
281 block.end = pc;
282 return AnalysisState::Branch;
283 case Opcode::BRK:
284 case Opcode::CONT:
285 case Opcode::LONGJMP:
286 case Opcode::SYNC: {
287 if (!AnalyzeBranch(block, function_id, pc, inst, opcode)) {
288 return AnalysisState::Continue;
289 }
290 const auto [stack_pc, new_stack]{block.stack.Pop(OpcodeToken(opcode))};
291 block.branch_true = AddLabel(block, new_stack, stack_pc, function_id);
292 block.end = pc;
293 return AnalysisState::Branch;
294 }
295 case Opcode::PBK:
296 case Opcode::PCNT:
297 case Opcode::PEXIT:
298 case Opcode::PLONGJMP:
299 case Opcode::SSY:
300 block.stack.Push(OpcodeToken(opcode), BranchOffset(pc, inst));
301 return AnalysisState::Continue;
302 case Opcode::EXIT:
303 return AnalyzeEXIT(block, function_id, pc, inst);
304 case Opcode::PRET:
305 throw NotImplementedException("PRET flow analysis");
306 case Opcode::CAL:
307 case Opcode::JCAL: {
308 const bool is_absolute{IsAbsoluteJump(opcode)};
309 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
310 // Technically CAL pushes into PRET, but that's implicit in the function call for us
311 // Insert the function into the list if it doesn't exist
312 if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) {
313 functions.push_back(cal_pc);
314 }
315 // Handle CAL like a regular instruction
316 break;
317 }
318 default:
319 break;
320 }
321 const Predicate pred{inst.Pred()};
322 if (pred == Predicate{true} || pred == Predicate{false}) {
323 return AnalysisState::Continue;
324 }
325 const IR::Condition cond{static_cast<IR::Pred>(pred.index), pred.negated};
326 AnalyzeCondInst(block, function_id, pc, EndClass::Branch, cond);
327 return AnalysisState::Branch;
328}
329
330void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc,
331 EndClass insn_end_class, IR::Condition cond) {
332 if (block.begin != pc) {
333 // If the block doesn't start in the conditional instruction
334 // mark it as a label to visit it later
335 block.end = pc;
336 block.cond = true;
337 block.branch_true = AddLabel(block, block.stack, pc, function_id);
338 block.branch_false = UNREACHABLE_BLOCK_ID;
339 return;
340 }
341 // Impersonate the visited block with a virtual block
342 // Jump from this virtual to the real conditional instruction and the next instruction
343 Function& function{functions[function_id]};
344 const BlockId conditional_block_id{++function.current_block_id};
345 function.blocks.push_back(static_cast<u32>(function.blocks_data.size()));
346 Block& virtual_block{function.blocks_data.emplace_back(Block{
347 .begin{}, // Virtual block
348 .end{},
349 .end_class{EndClass::Branch},
350 .id{block.id}, // Impersonating
351 .stack{block.stack},
352 .cond{cond},
353 .branch_true{conditional_block_id},
354 .branch_false{UNREACHABLE_BLOCK_ID},
355 })};
356 // Set the end properties of the conditional instruction and give it a new identity
357 Block& conditional_block{block};
358 conditional_block.end = pc;
359 conditional_block.end_class = insn_end_class;
360 conditional_block.id = conditional_block_id;
361 // Add a label to the instruction after the conditional instruction
362 const BlockId endif_block_id{AddLabel(conditional_block, block.stack, pc + 1, function_id)};
363 // Branch to the next instruction from the virtual block
364 virtual_block.branch_false = endif_block_id;
365 // And branch to it from the conditional instruction if it is a branch
366 if (insn_end_class == EndClass::Branch) {
367 conditional_block.cond = true;
368 conditional_block.branch_true = endif_block_id;
369 conditional_block.branch_false = UNREACHABLE_BLOCK_ID;
370 }
371}
372
373bool CFG::AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst,
374 Opcode opcode) {
375 if (inst.branch.is_cbuf) {
376 throw NotImplementedException("Branch with constant buffer offset");
377 }
378 const Predicate pred{inst.Pred()};
379 if (pred == Predicate{false}) {
380 return false;
381 }
382 const bool has_flow_test{HasFlowTest(opcode)};
383 const IR::FlowTest flow_test{has_flow_test ? inst.branch.flow_test.Value() : IR::FlowTest::T};
384 if (pred != Predicate{true} || flow_test != IR::FlowTest::T) {
385 block.cond = IR::Condition(flow_test, static_cast<IR::Pred>(pred.index), pred.negated);
386 block.branch_false = AddLabel(block, block.stack, pc + 1, function_id);
387 } else {
388 block.cond = true;
389 }
390 return true;
391}
392
393void CFG::AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
394 bool is_absolute) {
395 const Location bra_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
396 block.branch_true = AddLabel(block, block.stack, bra_pc, function_id);
397}
398
399void CFG::AnalyzeBRX(Block&, Location, Instruction, bool is_absolute) {
400 throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX");
401}
402
403void CFG::AnalyzeCAL(Location pc, Instruction inst, bool is_absolute) {
404 const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)};
405 // Technically CAL pushes into PRET, but that's implicit in the function call for us
406 // Insert the function to the function list if it doesn't exist
407 const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)};
408 if (it == functions.end()) {
409 functions.emplace_back(cal_pc);
410 }
411}
412
413CFG::AnalysisState CFG::AnalyzeEXIT(Block& block, FunctionId function_id, Location pc,
414 Instruction inst) {
415 const IR::FlowTest flow_test{inst.branch.flow_test};
416 const Predicate pred{inst.Pred()};
417 if (pred == Predicate{false} || flow_test == IR::FlowTest::F) {
418 // EXIT will never be taken
419 return AnalysisState::Continue;
420 }
421 if (pred != Predicate{true} || flow_test != IR::FlowTest::T) {
422 if (block.stack.Peek(Token::PEXIT).has_value()) {
423 throw NotImplementedException("Conditional EXIT with PEXIT token");
424 }
425 const IR::Condition cond{flow_test, static_cast<IR::Pred>(pred.index), pred.negated};
426 AnalyzeCondInst(block, function_id, pc, EndClass::Exit, cond);
427 return AnalysisState::Branch;
428 }
429 if (const std::optional<Location> exit_pc{block.stack.Peek(Token::PEXIT)}) {
430 const Stack popped_stack{block.stack.Remove(Token::PEXIT)};
431 block.cond = true;
432 block.branch_true = AddLabel(block, popped_stack, *exit_pc, function_id);
433 block.branch_false = UNREACHABLE_BLOCK_ID;
434 return AnalysisState::Branch;
435 }
436 block.end = pc;
437 block.end_class = EndClass::Exit;
438 return AnalysisState::Branch;
439}
440
441BlockId CFG::AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id) {
442 Function& function{functions[function_id]};
443 if (block.begin == pc) {
444 return block.id;
445 }
446 const auto target{std::ranges::find(function.blocks_data, pc, &Block::begin)};
447 if (target != function.blocks_data.end()) {
448 return target->id;
449 }
450 const BlockId block_id{++function.current_block_id};
451 function.labels.push_back(Label{
452 .address{pc},
453 .block_id{block_id},
454 .stack{std::move(stack)},
455 });
456 return block_id;
457}
458
459std::string CFG::Dot() const {
460 int node_uid{0};
461
462 std::string dot{"digraph shader {\n"};
463 for (const Function& function : functions) {
464 dot += fmt::format("\tsubgraph cluster_{} {{\n", function.entrypoint);
465 dot += fmt::format("\t\tnode [style=filled];\n");
466 for (const u32 block_index : function.blocks) {
467 const Block& block{function.blocks_data[block_index]};
468 const std::string name{Name(block)};
469 const auto add_branch = [&](BlockId branch_id, bool add_label) {
470 const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)};
471 dot += fmt::format("\t\t{}->", name);
472 if (it == function.blocks_data.end()) {
473 dot += fmt::format("\"Unknown label {}\"", branch_id);
474 } else {
475 dot += Name(*it);
476 };
477 if (add_label && block.cond != true && block.cond != false) {
478 dot += fmt::format(" [label=\"{}\"]", block.cond);
479 }
480 dot += '\n';
481 };
482 dot += fmt::format("\t\t{};\n", name);
483 switch (block.end_class) {
484 case EndClass::Branch:
485 if (block.cond != false) {
486 add_branch(block.branch_true, true);
487 }
488 if (block.cond != true) {
489 add_branch(block.branch_false, false);
490 }
491 break;
492 case EndClass::Exit:
493 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
494 dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n",
495 node_uid);
496 ++node_uid;
497 break;
498 case EndClass::Return:
499 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
500 dot += fmt::format("\t\tN{} [label=\"Return\"][shape=square][style=stripped];\n",
501 node_uid);
502 ++node_uid;
503 break;
504 case EndClass::Unreachable:
505 dot += fmt::format("\t\t{}->N{};\n", name, node_uid);
506 dot += fmt::format(
507 "\t\tN{} [label=\"Unreachable\"][shape=square][style=stripped];\n", node_uid);
508 ++node_uid;
509 break;
510 }
511 }
512 if (function.entrypoint == 8) {
513 dot += fmt::format("\t\tlabel = \"main\";\n");
514 } else {
515 dot += fmt::format("\t\tlabel = \"Function {}\";\n", function.entrypoint);
516 }
517 dot += "\t}\n";
518 }
519 if (!functions.empty()) {
520 if (functions.front().blocks.empty()) {
521 dot += "Start;\n";
522 } else {
523 dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front()));
524 }
525 dot += fmt::format("\tStart [shape=diamond];\n");
526 }
527 dot += "}\n";
528 return dot;
529}
530
531} // namespace Shader::Maxwell::Flow
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h
new file mode 100644
index 000000000..b2ab0cdc3
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/control_flow.h
@@ -0,0 +1,137 @@
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 <compare>
8#include <optional>
9#include <span>
10#include <string>
11#include <vector>
12
13#include <boost/container/small_vector.hpp>
14
15#include "shader_recompiler/environment.h"
16#include "shader_recompiler/frontend/ir/condition.h"
17#include "shader_recompiler/frontend/maxwell/instruction.h"
18#include "shader_recompiler/frontend/maxwell/location.h"
19#include "shader_recompiler/frontend/maxwell/opcode.h"
20
21namespace Shader::Maxwell::Flow {
22
23using BlockId = u32;
24using FunctionId = size_t;
25
26constexpr BlockId UNREACHABLE_BLOCK_ID{static_cast<u32>(-1)};
27
28enum class EndClass {
29 Branch,
30 Exit,
31 Return,
32 Unreachable,
33};
34
35enum class Token {
36 SSY,
37 PBK,
38 PEXIT,
39 PRET,
40 PCNT,
41 PLONGJMP,
42};
43
44struct StackEntry {
45 auto operator<=>(const StackEntry&) const noexcept = default;
46
47 Token token;
48 Location target;
49};
50
51class Stack {
52public:
53 void Push(Token token, Location target);
54 [[nodiscard]] std::pair<Location, Stack> Pop(Token token) const;
55 [[nodiscard]] std::optional<Location> Peek(Token token) const;
56 [[nodiscard]] Stack Remove(Token token) const;
57
58private:
59 boost::container::small_vector<StackEntry, 3> entries;
60};
61
62struct Block {
63 [[nodiscard]] bool Contains(Location pc) const noexcept;
64
65 Location begin;
66 Location end;
67 EndClass end_class;
68 BlockId id;
69 Stack stack;
70 IR::Condition cond;
71 BlockId branch_true;
72 BlockId branch_false;
73};
74
75struct Label {
76 Location address;
77 BlockId block_id;
78 Stack stack;
79};
80
81struct Function {
82 Function(Location start_address);
83
84 Location entrypoint;
85 BlockId current_block_id{0};
86 boost::container::small_vector<Label, 16> labels;
87 boost::container::small_vector<u32, 0x130> blocks;
88 boost::container::small_vector<Block, 0x130> blocks_data;
89};
90
91class CFG {
92 enum class AnalysisState {
93 Branch,
94 Continue,
95 };
96
97public:
98 explicit CFG(Environment& env, Location start_address);
99
100 [[nodiscard]] std::string Dot() const;
101
102 [[nodiscard]] std::span<const Function> Functions() const noexcept {
103 return std::span(functions.data(), functions.size());
104 }
105
106private:
107 void AnalyzeLabel(FunctionId function_id, Label& label);
108
109 /// Inspect already visited blocks.
110 /// Return true when the block has already been visited
111 [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label);
112
113 [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc);
114
115 void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class,
116 IR::Condition cond);
117
118 /// Return true when the branch instruction is confirmed to be a branch
119 [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc,
120 Instruction inst, Opcode opcode);
121
122 void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst,
123 bool is_absolute);
124 void AnalyzeBRX(Block& block, Location pc, Instruction inst, bool is_absolute);
125 void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute);
126 AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst);
127
128 /// Return the branch target block id
129 [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc,
130 FunctionId function_id);
131
132 Environment& env;
133 boost::container::small_vector<Function, 1> functions;
134 FunctionId current_function_id{0};
135};
136
137} // namespace Shader::Maxwell::Flow
diff --git a/src/shader_recompiler/frontend/maxwell/decode.cpp b/src/shader_recompiler/frontend/maxwell/decode.cpp
new file mode 100644
index 000000000..ab1cc6c8d
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/decode.cpp
@@ -0,0 +1,149 @@
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 <bit>
8#include <memory>
9#include <string_view>
10
11#include "common/common_types.h"
12#include "shader_recompiler/exception.h"
13#include "shader_recompiler/frontend/maxwell/decode.h"
14#include "shader_recompiler/frontend/maxwell/opcode.h"
15
16namespace Shader::Maxwell {
17namespace {
18struct MaskValue {
19 u64 mask;
20 u64 value;
21};
22
23constexpr MaskValue MaskValueFromEncoding(const char* encoding) {
24 u64 mask{};
25 u64 value{};
26 u64 bit{u64(1) << 63};
27 while (*encoding) {
28 switch (*encoding) {
29 case '0':
30 mask |= bit;
31 break;
32 case '1':
33 mask |= bit;
34 value |= bit;
35 break;
36 case '-':
37 break;
38 case ' ':
39 break;
40 default:
41 throw LogicError("Invalid encoding character '{}'", *encoding);
42 }
43 ++encoding;
44 if (*encoding != ' ') {
45 bit >>= 1;
46 }
47 }
48 return MaskValue{.mask{mask}, .value{value}};
49}
50
51struct InstEncoding {
52 MaskValue mask_value;
53 Opcode opcode;
54};
55constexpr std::array UNORDERED_ENCODINGS{
56#define INST(name, cute, encode) \
57 InstEncoding{ \
58 .mask_value{MaskValueFromEncoding(encode)}, \
59 .opcode{Opcode::name}, \
60 },
61#include "maxwell.inc"
62#undef INST
63};
64
65constexpr auto SortedEncodings() {
66 std::array encodings{UNORDERED_ENCODINGS};
67 std::ranges::sort(encodings, [](const InstEncoding& lhs, const InstEncoding& rhs) {
68 return std::popcount(lhs.mask_value.mask) > std::popcount(rhs.mask_value.mask);
69 });
70 return encodings;
71}
72constexpr auto ENCODINGS{SortedEncodings()};
73
74constexpr int WidestLeftBits() {
75 int bits{64};
76 for (const InstEncoding& encoding : ENCODINGS) {
77 bits = std::min(bits, std::countr_zero(encoding.mask_value.mask));
78 }
79 return 64 - bits;
80}
81constexpr int WIDEST_LEFT_BITS{WidestLeftBits()};
82constexpr int MASK_SHIFT{64 - WIDEST_LEFT_BITS};
83
84constexpr size_t ToFastLookupIndex(u64 value) {
85 return static_cast<size_t>(value >> MASK_SHIFT);
86}
87
88constexpr size_t FastLookupSize() {
89 size_t max_width{};
90 for (const InstEncoding& encoding : ENCODINGS) {
91 max_width = std::max(max_width, ToFastLookupIndex(encoding.mask_value.mask));
92 }
93 return max_width + 1;
94}
95constexpr size_t FAST_LOOKUP_SIZE{FastLookupSize()};
96
97struct InstInfo {
98 [[nodiscard]] u64 Mask() const noexcept {
99 return static_cast<u64>(high_mask) << MASK_SHIFT;
100 }
101
102 [[nodiscard]] u64 Value() const noexcept {
103 return static_cast<u64>(high_value) << MASK_SHIFT;
104 }
105
106 u16 high_mask;
107 u16 high_value;
108 Opcode opcode;
109};
110
111constexpr auto MakeFastLookupTableIndex(size_t index) {
112 std::array<InstInfo, 2> encodings{};
113 size_t element{};
114 for (const auto& encoding : ENCODINGS) {
115 const size_t mask{ToFastLookupIndex(encoding.mask_value.mask)};
116 const size_t value{ToFastLookupIndex(encoding.mask_value.value)};
117 if ((index & mask) == value) {
118 encodings.at(element) = InstInfo{
119 .high_mask{static_cast<u16>(encoding.mask_value.mask >> MASK_SHIFT)},
120 .high_value{static_cast<u16>(encoding.mask_value.value >> MASK_SHIFT)},
121 .opcode{encoding.opcode},
122 };
123 ++element;
124 }
125 }
126 return encodings;
127}
128
129/*constexpr*/ auto MakeFastLookupTable() {
130 auto encodings{std::make_unique<std::array<std::array<InstInfo, 2>, FAST_LOOKUP_SIZE>>()};
131 for (size_t index = 0; index < FAST_LOOKUP_SIZE; ++index) {
132 (*encodings)[index] = MakeFastLookupTableIndex(index);
133 }
134 return encodings;
135}
136const auto FAST_LOOKUP_TABLE{MakeFastLookupTable()};
137} // Anonymous namespace
138
139Opcode Decode(u64 insn) {
140 const auto& table{(*FAST_LOOKUP_TABLE)[ToFastLookupIndex(insn)]};
141 const auto it{std::ranges::find_if(
142 table, [insn](const InstInfo& info) { return (insn & info.Mask()) == info.Value(); })};
143 if (it == table.end()) {
144 throw NotImplementedException("Instruction 0x{:016x} is unknown / unimplemented", insn);
145 }
146 return it->opcode;
147}
148
149} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/decode.h b/src/shader_recompiler/frontend/maxwell/decode.h
new file mode 100644
index 000000000..2a3dd28e8
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/decode.h
@@ -0,0 +1,14 @@
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/frontend/maxwell/opcode.h"
9
10namespace Shader::Maxwell {
11
12[[nodiscard]] Opcode Decode(u64 insn);
13
14} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/instruction.h b/src/shader_recompiler/frontend/maxwell/instruction.h
new file mode 100644
index 000000000..57fd531f2
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/instruction.h
@@ -0,0 +1,62 @@
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/bit_field.h"
8#include "common/common_types.h"
9#include "shader_recompiler/frontend/ir/flow_test.h"
10
11namespace Shader::Maxwell {
12
13struct Predicate {
14 Predicate() = default;
15 Predicate(unsigned index_, bool negated_ = false) : index{index_}, negated{negated_} {}
16 Predicate(bool value) : index{7}, negated{!value} {}
17 Predicate(u64 raw) : index{static_cast<unsigned>(raw & 7)}, negated{(raw & 8) != 0} {}
18
19 unsigned index;
20 bool negated;
21};
22
23inline bool operator==(const Predicate& lhs, const Predicate& rhs) noexcept {
24 return lhs.index == rhs.index && lhs.negated == rhs.negated;
25}
26
27inline bool operator!=(const Predicate& lhs, const Predicate& rhs) noexcept {
28 return !(lhs == rhs);
29}
30
31union Instruction {
32 Instruction(u64 raw_) : raw{raw_} {}
33
34 u64 raw;
35
36 union {
37 BitField<5, 1, u64> is_cbuf;
38 BitField<0, 5, IR::FlowTest> flow_test;
39
40 [[nodiscard]] u32 Absolute() const noexcept {
41 return static_cast<u32>(absolute);
42 }
43
44 [[nodiscard]] s32 Offset() const noexcept {
45 return static_cast<s32>(offset);
46 }
47
48 private:
49 BitField<20, 24, s64> offset;
50 BitField<20, 32, u64> absolute;
51 } branch;
52
53 [[nodiscard]] Predicate Pred() const noexcept {
54 return Predicate{pred};
55 }
56
57private:
58 BitField<16, 4, u64> pred;
59};
60static_assert(std::is_trivially_copyable_v<Instruction>);
61
62} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/location.h b/src/shader_recompiler/frontend/maxwell/location.h
new file mode 100644
index 000000000..66b51a19e
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/location.h
@@ -0,0 +1,106 @@
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 <compare>
8#include <iterator>
9
10#include <fmt/format.h>
11
12#include "common/common_types.h"
13#include "shader_recompiler/exception.h"
14
15namespace Shader::Maxwell {
16
17class Location {
18 static constexpr u32 VIRTUAL_OFFSET{std::numeric_limits<u32>::max()};
19
20public:
21 constexpr Location() = default;
22
23 constexpr Location(u32 initial_offset) : offset{initial_offset} {
24 if (initial_offset % 8 != 0) {
25 throw InvalidArgument("initial_offset={} is not a multiple of 8", initial_offset);
26 }
27 Align();
28 }
29
30 [[nodiscard]] constexpr u32 Offset() const noexcept {
31 return offset;
32 }
33
34 [[nodiscard]] constexpr bool IsVirtual() const {
35 return offset == VIRTUAL_OFFSET;
36 }
37
38 constexpr auto operator<=>(const Location&) const noexcept = default;
39
40 constexpr Location operator++() noexcept {
41 const Location copy{*this};
42 Step();
43 return copy;
44 }
45
46 constexpr Location operator++(int) noexcept {
47 Step();
48 return *this;
49 }
50
51 constexpr Location operator--() noexcept {
52 const Location copy{*this};
53 Back();
54 return copy;
55 }
56
57 constexpr Location operator--(int) noexcept {
58 Back();
59 return *this;
60 }
61
62 constexpr Location operator+(int number) const {
63 Location new_pc{*this};
64 while (number > 0) {
65 --number;
66 ++new_pc;
67 }
68 while (number < 0) {
69 ++number;
70 --new_pc;
71 }
72 return new_pc;
73 }
74
75 constexpr Location operator-(int number) const {
76 return operator+(-number);
77 }
78
79private:
80 constexpr void Align() {
81 offset += offset % 32 == 0 ? 8 : 0;
82 }
83
84 constexpr void Step() {
85 offset += 8 + (offset % 32 == 24 ? 8 : 0);
86 }
87
88 constexpr void Back() {
89 offset -= 8 + (offset % 32 == 8 ? 8 : 0);
90 }
91
92 u32 offset{VIRTUAL_OFFSET};
93};
94
95} // namespace Shader::Maxwell
96
97template <>
98struct fmt::formatter<Shader::Maxwell::Location> {
99 constexpr auto parse(format_parse_context& ctx) {
100 return ctx.begin();
101 }
102 template <typename FormatContext>
103 auto format(const Shader::Maxwell::Location& location, FormatContext& ctx) {
104 return fmt::format_to(ctx.out(), "{:04x}", location.Offset());
105 }
106};
diff --git a/src/shader_recompiler/frontend/maxwell/maxwell.inc b/src/shader_recompiler/frontend/maxwell/maxwell.inc
new file mode 100644
index 000000000..1515285bf
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/maxwell.inc
@@ -0,0 +1,285 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5INST(AL2P, "AL2P", "1110 1111 1010 0---")
6INST(ALD, "ALD", "1110 1111 1101 1---")
7INST(AST, "AST", "1110 1111 1111 0---")
8INST(ATOM_cas, "ATOM (cas)", "1110 1110 1111 ----")
9INST(ATOM, "ATOM", "1110 1101 ---- ----")
10INST(ATOMS_cas, "ATOMS (cas)", "1110 1110 ---- ----")
11INST(ATOMS, "ATOMS", "1110 1100 ---- ----")
12INST(B2R, "B2R", "1111 0000 1011 1---")
13INST(BAR, "BAR", "1111 0000 1010 1---")
14INST(BFE_reg, "BFE (reg)", "0101 1100 0000 0---")
15INST(BFE_cbuf, "BFE (cbuf)", "0100 1100 0000 0---")
16INST(BFE_imm, "BFE (imm)", "0011 100- 0000 0---")
17INST(BFI_reg, "BFI (reg)", "0101 1011 1111 0---")
18INST(BFI_rc, "BFI (rc)", "0101 0011 1111 0---")
19INST(BFI_cr, "BFI (cr)", "0100 1011 1111 0---")
20INST(BFI_imm, "BFI (imm)", "0011 011- 1111 0---")
21INST(BPT, "BPT", "1110 0011 1010 ----")
22INST(BRA, "BRA", "1110 0010 0100 ----")
23INST(BRK, "BRK", "1110 0011 0100 ----")
24INST(BRX, "BRX", "1110 0010 0101 ----")
25INST(CAL, "CAL", "1110 0010 0110 ----")
26INST(CCTL, "CCTL", "1110 1111 011- ----")
27INST(CCTLL, "CCTLL", "1110 1111 100- ----")
28INST(CONT, "CONT", "1110 0011 0101 ----")
29INST(CS2R, "CS2R", "0101 0000 1100 1---")
30INST(CSET, "CSET", "0101 0000 1001 1---")
31INST(CSETP, "CSETP", "0101 0000 1010 0---")
32INST(DADD_reg, "DADD (reg)", "0101 1100 0111 0---")
33INST(DADD_cbuf, "DADD (cbuf)", "0100 1100 0111 0---")
34INST(DADD_imm, "DADD (imm)", "0011 100- 0111 0---")
35INST(DEPBAR, "DEPBAR", "1111 0000 1111 0---")
36INST(DFMA_reg, "DFMA (reg)", "0101 1011 0111 ----")
37INST(DFMA_rc, "DFMA (rc)", "0101 0011 0111 ----")
38INST(DFMA_cr, "DFMA (cr)", "0010 1011 0111 ----")
39INST(DFMA_imm, "DFMA (imm)", "0011 011- 0111 ----")
40INST(DMNMX_reg, "DMNMX (reg)", "0100 1100 0101 0---")
41INST(DMNMX_cbuf, "DMNMX (cbuf)", "0101 1100 0101 0---")
42INST(DMNMX_imm, "DMNMX (imm)", "0011 100- 0101 0---")
43INST(DMUL_reg, "DMUL (reg)", "0101 1100 1000 0---")
44INST(DMUL_cbuf, "DMUL (cbuf)", "0100 1100 1000 0---")
45INST(DMUL_imm, "DMUL (imm)", "0011 100- 1000 0---")
46INST(DSET_reg, "DSET (reg)", "0101 1001 0--- ----")
47INST(DSET_cbuf, "DSET (cbuf)", "0100 1001 0--- ----")
48INST(DSET_imm, "DSET (imm)", "0011 001- 0--- ----")
49INST(DSETP_reg, "DSETP (reg)", "0101 1011 1000 ----")
50INST(DSETP_cbuf, "DSETP (cbuf)", "0100 1011 1000 ----")
51INST(DSETP_imm, "DSETP (imm)", "0011 011- 1000 ----")
52INST(EXIT, "EXIT", "1110 0011 0000 ----")
53INST(F2F_reg, "F2F (reg)", "0101 1100 1010 1---")
54INST(F2F_cbuf, "F2F (cbuf)", "0100 1100 1010 1---")
55INST(F2F_imm, "F2F (imm)", "0011 100- 1010 1---")
56INST(F2I_reg, "F2I (reg)", "0101 1100 1011 0---")
57INST(F2I_cbuf, "F2I (cbuf)", "0100 1100 1011 0---")
58INST(F2I_imm, "F2I (imm)", "0011 100- 1011 0---")
59INST(FADD_reg, "FADD (reg)", "0101 1100 0101 1---")
60INST(FADD_cbuf, "FADD (cbuf)", "0100 1100 0101 1---")
61INST(FADD_imm, "FADD (imm)", "0011 100- 0101 1---")
62INST(FADD32I, "FADD32I", "0000 10-- ---- ----")
63INST(FCHK_reg, "FCHK (reg)", "0101 1100 1000 1---")
64INST(FCHK_cbuf, "FCHK (cbuf)", "0100 1100 1000 1---")
65INST(FCHK_imm, "FCHK (imm)", "0011 100- 1000 1---")
66INST(FCMP_reg, "FCMP (reg)", "0101 1011 1010 ----")
67INST(FCMP_rc, "FCMP (rc)", "0101 0011 1010 ----")
68INST(FCMP_cr, "FCMP (cr)", "0100 1011 1010 ----")
69INST(FCMP_imm, "FCMP (imm)", "0011 011- 1010 ----")
70INST(FFMA_reg, "FFMA (reg)", "0101 1001 1--- ----")
71INST(FFMA_rc, "FFMA (rc)", "0101 0001 1--- ----")
72INST(FFMA_cr, "FFMA (cr)", "0100 1001 1--- ----")
73INST(FFMA_imm, "FFMA (imm)", "0011 001- 1--- ----")
74INST(FFMA32I, "FFMA32I", "0000 11-- ---- ----")
75INST(FLO_reg, "FLO (reg)", "0101 1100 0011 0---")
76INST(FLO_cbuf, "FLO (cbuf)", "0100 1100 0011 0---")
77INST(FLO_imm, "FLO (imm)", "0011 100- 0011 0---")
78INST(FMNMX_reg, "FMNMX (reg)", "0101 1100 0110 0---")
79INST(FMNMX_cbuf, "FMNMX (cbuf)", "0100 1100 0110 0---")
80INST(FMNMX_imm, "FMNMX (imm)", "0011 100- 0110 0---")
81INST(FMUL_reg, "FMUL (reg)", "0101 1100 0110 1---")
82INST(FMUL_cbuf, "FMUL (cbuf)", "0100 1100 0110 1---")
83INST(FMUL_imm, "FMUL (imm)", "0011 100- 0110 1---")
84INST(FMUL32I, "FMUL32I", "0001 1110 ---- ----")
85INST(FSET_reg, "FSET (reg)", "0101 1000 ---- ----")
86INST(FSET_cbuf, "FSET (cbuf)", "0100 1000 ---- ----")
87INST(FSET_imm, "FSET (imm)", "0011 000- ---- ----")
88INST(FSETP_reg, "FSETP (reg)", "0101 1011 1011 ----")
89INST(FSETP_cbuf, "FSETP (cbuf)", "0100 1011 1011 ----")
90INST(FSETP_imm, "FSETP (imm)", "0011 011- 1011 ----")
91INST(FSWZADD, "FSWZADD", "0101 0000 1111 1---")
92INST(GETCRSPTR, "GETCRSPTR", "1110 0010 1100 ----")
93INST(GETLMEMBASE, "GETLMEMBASE", "1110 0010 1101 ----")
94INST(HADD2_reg, "HADD2 (reg)", "0101 1101 0001 0---")
95INST(HADD2_cbuf, "HADD2 (cbuf)", "0111 101- 1--- ----")
96INST(HADD2_imm, "HADD2 (imm)", "0111 101- 0--- ----")
97INST(HADD2_32I, "HADD2_32I", "0010 110- ---- ----")
98INST(HFMA2_reg, "HFMA2 (reg)", "0101 1101 0000 0---")
99INST(HFMA2_rc, "HFMA2 (rc)", "0110 0--- 1--- ----")
100INST(HFMA2_cr, "HFMA2 (cr)", "0111 0--- 1--- ----")
101INST(HFMA2_imm, "HFMA2 (imm)", "0111 0--- 0--- ----")
102INST(HFMA2_32I, "HFMA2_32I", "0010 100- ---- ----")
103INST(HMUL2_reg, "HMUL2 (reg)", "0101 1101 0000 1---")
104INST(HMUL2_cbuf, "HMUL2 (cbuf)", "0111 100- 1--- ----")
105INST(HMUL2_imm, "HMUL2 (imm)", "0111 100- 0--- ----")
106INST(HMUL2_32I, "HMUL2_32I", "0010 101- ---- ----")
107INST(HSET2_reg, "HSET2 (reg)", "0101 1101 0001 1---")
108INST(HSET2_cbuf, "HSET2 (cbuf)", "0111 1100 1--- ----")
109INST(HSET2_imm, "HSET2 (imm)", "0111 1100 0--- ----")
110INST(HSETP2_reg, "HSETP2 (reg)", "0101 1101 0010 0---")
111INST(HSETP2_cbuf, "HSETP2 (cbuf)", "0111 111- 1--- ----")
112INST(HSETP2_imm, "HSETP2 (imm)", "0111 111- 0--- ----")
113INST(I2F_reg, "I2F (reg)", "0101 1100 1011 1---")
114INST(I2F_cbuf, "I2F (cbuf)", "0100 1100 1011 1---")
115INST(I2F_imm, "I2F (imm)", "0011 100- 1011 1---")
116INST(I2I_reg, "I2I (reg)", "0101 1100 1110 0---")
117INST(I2I_cbuf, "I2I (cbuf)", "0100 1100 1110 0---")
118INST(I2I_imm, "I2I (imm)", "0011 100- 1110 0---")
119INST(IADD_reg, "IADD (reg)", "0101 1100 0001 0---")
120INST(IADD_cbuf, "IADD (cbuf)", "0100 1100 0001 0---")
121INST(IADD_imm, "IADD (imm)", "0011 100- 0001 0---")
122INST(IADD3_reg, "IADD3 (reg)", "0101 1100 1100 ----")
123INST(IADD3_cbuf, "IADD3 (cbuf)", "0100 1100 1100 ----")
124INST(IADD3_imm, "IADD3 (imm)", "0011 100- 1100 ----")
125INST(IADD32I, "IADD32I", "0001 110- ---- ----")
126INST(ICMP_reg, "ICMP (reg)", "0101 1011 0100 ----")
127INST(ICMP_rc, "ICMP (rc)", "0101 0011 0100 ----")
128INST(ICMP_cr, "ICMP (cr)", "0100 1011 0100 ----")
129INST(ICMP_imm, "ICMP (imm)", "0011 011- 0100 ----")
130INST(IDE, "IDE", "1110 0011 1001 ----")
131INST(IDP_reg, "IDP (reg)", "0101 0011 1111 1---")
132INST(IDP_imm, "IDP (imm)", "0101 0011 1101 1---")
133INST(IMAD_reg, "IMAD (reg)", "0101 1010 0--- ----")
134INST(IMAD_rc, "IMAD (rc)", "0101 0010 0--- ----")
135INST(IMAD_cr, "IMAD (cr)", "0100 1010 0--- ----")
136INST(IMAD_imm, "IMAD (imm)", "0011 010- 0--- ----")
137INST(IMAD32I, "IMAD32I", "1000 00-- ---- ----")
138INST(IMADSP_reg, "IMADSP (reg)", "0101 1010 1--- ----")
139INST(IMADSP_rc, "IMADSP (rc)", "0101 0010 1--- ----")
140INST(IMADSP_cr, "IMADSP (cr)", "0100 1010 1--- ----")
141INST(IMADSP_imm, "IMADSP (imm)", "0011 010- 1--- ----")
142INST(IMNMX_reg, "IMNMX (reg)", "0101 1100 0010 0---")
143INST(IMNMX_cbuf, "IMNMX (cbuf)", "0100 1100 0010 0---")
144INST(IMNMX_imm, "IMNMX (imm)", "0011 100- 0010 0---")
145INST(IMUL_reg, "IMUL (reg)", "0101 1100 0011 1---")
146INST(IMUL_cbuf, "IMUL (cbuf)", "0100 1100 0011 1---")
147INST(IMUL_imm, "IMUL (imm)", "0011 100- 0011 1---")
148INST(IMUL32I, "IMUL32I", "0001 1111 ---- ----")
149INST(IPA, "IPA", "1110 0000 ---- ----")
150INST(ISBERD, "ISBERD", "1110 1111 1101 0---")
151INST(ISCADD_reg, "ISCADD (reg)", "0101 1100 0001 1---")
152INST(ISCADD_cbuf, "ISCADD (cbuf)", "0100 1100 0001 1---")
153INST(ISCADD_imm, "ISCADD (imm)", "0011 100- 0001 1---")
154INST(ISCADD32I, "ISCADD32I", "0001 01-- ---- ----")
155INST(ISET_reg, "ISET (reg)", "0101 1011 0101 ----")
156INST(ISET_cbuf, "ISET (cbuf)", "0100 1011 0101 ----")
157INST(ISET_imm, "ISET (imm)", "0011 011- 0101 ----")
158INST(ISETP_reg, "ISETP (reg)", "0101 1011 0110 ----")
159INST(ISETP_cbuf, "ISETP (cbuf)", "0100 1011 0110 ----")
160INST(ISETP_imm, "ISETP (imm)", "0011 011- 0110 ----")
161INST(JCAL, "JCAL", "1110 0010 0010 ----")
162INST(JMP, "JMP", "1110 0010 0001 ----")
163INST(JMX, "JMX", "1110 0010 0000 ----")
164INST(KIL, "KIL", "1110 0011 0011 ----")
165INST(LD, "LD", "100- ---- ---- ----")
166INST(LDC, "LDC", "1110 1111 1001 0---")
167INST(LDG, "LDG", "1110 1110 1101 0---")
168INST(LDL, "LDL", "1110 1111 0100 0---")
169INST(LDS, "LDS", "1110 1111 0100 1---")
170INST(LEA_hi_reg, "LEA (hi reg)", "0101 1011 1101 1---")
171INST(LEA_hi_cbuf, "LEA (hi cbuf)", "0001 10-- ---- ----")
172INST(LEA_lo_reg, "LEA (lo reg)", "0101 1011 1101 0---")
173INST(LEA_lo_cbuf, "LEA (lo cbuf)", "0100 1011 1101 ----")
174INST(LEA_lo_imm, "LEA (lo imm)", "0011 011- 1101 0---")
175INST(LEPC, "LEPC", "0101 0000 1101 0---")
176INST(LONGJMP, "LONGJMP", "1110 0011 0001 ----")
177INST(LOP_reg, "LOP (reg)", "0101 1100 0100 0---")
178INST(LOP_cbuf, "LOP (cbuf)", "0100 1100 0100 0---")
179INST(LOP_imm, "LOP (imm)", "0011 100- 0100 0---")
180INST(LOP3_reg, "LOP3 (reg)", "0101 1011 1110 0---")
181INST(LOP3_cbuf, "LOP3 (cbuf)", "0011 11-- ---- ----")
182INST(LOP3_imm, "LOP3 (imm)", "0000 001- ---- ----")
183INST(LOP32I, "LOP32I", "0000 01-- ---- ----")
184INST(MEMBAR, "MEMBAR", "1110 1111 1001 1---")
185INST(MOV_reg, "MOV (reg)", "0101 1100 1001 1---")
186INST(MOV_cbuf, "MOV (cbuf)", "0100 1100 1001 1---")
187INST(MOV_imm, "MOV (imm)", "0011 100- 1001 1---")
188INST(MOV32I, "MOV32I", "0000 0001 0000 ----")
189INST(MUFU, "MUFU", "0101 0000 1000 0---")
190INST(NOP, "NOP", "0101 0000 1011 0---")
191INST(OUT_reg, "OUT (reg)", "1111 1011 1110 0---")
192INST(OUT_cbuf, "OUT (cbuf)", "1110 1011 1110 0---")
193INST(OUT_imm, "OUT (imm)", "1111 011- 1110 0---")
194INST(P2R_reg, "P2R (reg)", "0101 1100 1110 1---")
195INST(P2R_cbuf, "P2R (cbuf)", "0100 1100 1110 1---")
196INST(P2R_imm, "P2R (imm)", "0011 1000 1110 1---")
197INST(PBK, "PBK", "1110 0010 1010 ----")
198INST(PCNT, "PCNT", "1110 0010 1011 ----")
199INST(PEXIT, "PEXIT", "1110 0010 0011 ----")
200INST(PIXLD, "PIXLD", "1110 1111 1110 1---")
201INST(PLONGJMP, "PLONGJMP", "1110 0010 1000 ----")
202INST(POPC_reg, "POPC (reg)", "0101 1100 0000 1---")
203INST(POPC_cbuf, "POPC (cbuf)", "0100 1100 0000 1---")
204INST(POPC_imm, "POPC (imm)", "0011 100- 0000 1---")
205INST(PRET, "PRET", "1110 0010 0111 ----")
206INST(PRMT_reg, "PRMT (reg)", "0101 1011 1100 ----")
207INST(PRMT_rc, "PRMT (rc)", "0101 0011 1100 ----")
208INST(PRMT_cr, "PRMT (cr)", "0100 1011 1100 ----")
209INST(PRMT_imm, "PRMT (imm)", "0011 011- 1100 ----")
210INST(PSET, "PSET", "0101 0000 1000 1---")
211INST(PSETP, "PSETP", "0101 0000 1001 0---")
212INST(R2B, "R2B", "1111 0000 1100 0---")
213INST(R2P_reg, "R2P (reg)", "0101 1100 1111 0---")
214INST(R2P_cbuf, "R2P (cbuf)", "0100 1100 1111 0---")
215INST(R2P_imm, "R2P (imm)", "0011 100- 1111 0---")
216INST(RAM, "RAM", "1110 0011 1000 ----")
217INST(RED, "RED", "1110 1011 1111 1---")
218INST(RET, "RET", "1110 0011 0010 ----")
219INST(RRO_reg, "RRO (reg)", "0101 1100 1001 0---")
220INST(RRO_cbuf, "RRO (cbuf)", "0100 1100 1001 0---")
221INST(RRO_imm, "RRO (imm)", "0011 100- 1001 0---")
222INST(RTT, "RTT", "1110 0011 0110 ----")
223INST(S2R, "S2R", "1111 0000 1100 1---")
224INST(SAM, "SAM", "1110 0011 0111 ----")
225INST(SEL_reg, "SEL (reg)", "0101 1100 1010 0---")
226INST(SEL_cbuf, "SEL (cbuf)", "0100 1100 1010 0---")
227INST(SEL_imm, "SEL (imm)", "0011 100- 1010 0---")
228INST(SETCRSPTR, "SETCRSPTR", "1110 0010 1110 ----")
229INST(SETLMEMBASE, "SETLMEMBASE", "1110 0010 1111 ----")
230INST(SHF_l_reg, "SHF (l reg)", "0101 1011 1111 1---")
231INST(SHF_l_imm, "SHF (l imm)", "0011 011- 1111 1---")
232INST(SHF_r_reg, "SHF (r reg)", "0101 1100 1111 1---")
233INST(SHF_r_imm, "SHF (r imm)", "0011 100- 1111 1---")
234INST(SHFL, "SHFL", "1110 1111 0001 0---")
235INST(SHL_reg, "SHL (reg)", "0101 1100 0100 1---")
236INST(SHL_cbuf, "SHL (cbuf)", "0100 1100 0100 1---")
237INST(SHL_imm, "SHL (imm)", "0011 100- 0100 1---")
238INST(SHR_reg, "SHR (reg)", "0101 1100 0010 1---")
239INST(SHR_cbuf, "SHR (cbuf)", "0100 1100 0010 1---")
240INST(SHR_imm, "SHR (imm)", "0011 100- 0010 1---")
241INST(SSY, "SSY", "1110 0010 1001 ----")
242INST(ST, "ST", "101- ---- ---- ----")
243INST(STG, "STG", "1110 1110 1101 1---")
244INST(STL, "STL", "1110 1111 0101 0---")
245INST(STP, "STP", "1110 1110 1010 0---")
246INST(STS, "STS", "1110 1111 0101 1---")
247INST(SUATOM_cas, "SUATOM", "1110 1010 ---- ----")
248INST(SULD, "SULD", "1110 1011 000- ----")
249INST(SURED, "SURED", "1110 1011 010- ----")
250INST(SUST, "SUST", "1110 1011 001- ----")
251INST(SYNC, "SYNC", "1111 0000 1111 1---")
252INST(TEX, "TEX", "1100 00-- --11 1---")
253INST(TEX_b, "TEX (b)", "1101 1110 1011 1---")
254INST(TEXS, "TEXS", "1101 -00- ---- ----")
255INST(TLD, "TLD", "1101 1100 --11 1---")
256INST(TLD_b, "TLD (b)", "1101 1101 --11 1---")
257INST(TLD4, "TLD4", "1100 10-- --11 1---")
258INST(TLD4_b, "TLD4 (b)", "1101 1110 1111 1---")
259INST(TLD4S, "TLD4S", "1101 1111 -0-- ----")
260INST(TLDS, "TLDS", "1101 -01- ---- ----")
261INST(TMML, "TMML", "1101 1111 0101 1---")
262INST(TMML_b, "TMML (b)", "1101 1111 0110 0---")
263INST(TXA, "TXA", "1101 1111 0100 0---")
264INST(TXD, "TXD", "1101 1110 0011 10--")
265INST(TXD_b, "TXD (b)", "1101 1110 0111 10--")
266INST(TXQ, "TXQ", "1101 1111 0100 1---")
267INST(TXQ_b, "TXQ (b)", "1101 1111 0101 0---")
268INST(VABSDIFF, "VABSDIFF", "0101 0100 ---- ----")
269INST(VABSDIFF4, "VABSDIFF4", "0101 0000 0--- ----")
270INST(VADD, "VADD", "0010 00-- ---- ----")
271INST(VMAD, "VMAD", "0101 1111 ---- ----")
272INST(VMNMX, "VMNMX", "0011 101- ---- ----")
273INST(VOTE, "VOTE", "0101 0000 1101 1---")
274INST(VOTE_vtg, "VOTE (vtg)", "0101 0000 1110 0---")
275INST(VSET, "VSET", "0100 000- ---- ----")
276INST(VSETP, "VSETP", "0101 0000 1111 0---")
277INST(VSHL, "VSHL", "0101 0111 ---- ----")
278INST(VSHR, "VSHR", "0101 0110 ---- ----")
279INST(XMAD_reg, "XMAD (reg)", "0101 1011 00-- ----")
280INST(XMAD_rc, "XMAD (rc)", "0101 0001 0--- ----")
281INST(XMAD_cr, "XMAD (cr)", "0100 111- ---- ----")
282INST(XMAD_imm, "XMAD (imm)", "0011 011- 00-- ----")
283
284// Removed due to its weird formatting making fast tables larger
285// INST(CCTLT, "CCTLT", "1110 1011 1111 0--0")
diff --git a/src/shader_recompiler/frontend/maxwell/opcode.cpp b/src/shader_recompiler/frontend/maxwell/opcode.cpp
new file mode 100644
index 000000000..8a7bdb611
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/opcode.cpp
@@ -0,0 +1,26 @@
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
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9
10namespace Shader::Maxwell {
11namespace {
12constexpr std::array NAME_TABLE{
13#define INST(name, cute, encode) #cute,
14#include "maxwell.inc"
15#undef INST
16};
17} // Anonymous namespace
18
19const char* NameOf(Opcode opcode) {
20 if (static_cast<size_t>(opcode) >= NAME_TABLE.size()) {
21 throw InvalidArgument("Invalid opcode with raw value {}", static_cast<int>(opcode));
22 }
23 return NAME_TABLE[static_cast<size_t>(opcode)];
24}
25
26} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/opcode.h b/src/shader_recompiler/frontend/maxwell/opcode.h
new file mode 100644
index 000000000..cd574f29d
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/opcode.h
@@ -0,0 +1,30 @@
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::Maxwell {
10
11enum class Opcode {
12#define INST(name, cute, encode) name,
13#include "maxwell.inc"
14#undef INST
15};
16
17const char* NameOf(Opcode opcode);
18
19} // namespace Shader::Maxwell
20
21template <>
22struct fmt::formatter<Shader::Maxwell::Opcode> {
23 constexpr auto parse(format_parse_context& ctx) {
24 return ctx.begin();
25 }
26 template <typename FormatContext>
27 auto format(const Shader::Maxwell::Opcode& opcode, FormatContext& ctx) {
28 return format_to(ctx.out(), "{}", NameOf(opcode));
29 }
30};
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp
new file mode 100644
index 000000000..67a98ba57
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/program.cpp
@@ -0,0 +1,69 @@
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 <memory>
7
8#include "shader_recompiler/frontend/maxwell/program.h"
9#include "shader_recompiler/frontend/maxwell/termination_code.h"
10#include "shader_recompiler/frontend/maxwell/translate/translate.h"
11
12namespace Shader::Maxwell {
13
14Program::Function::~Function() {
15 std::ranges::for_each(blocks, &std::destroy_at<IR::Block>);
16}
17
18Program::Program(Environment& env, const Flow::CFG& cfg) {
19 std::vector<IR::Block*> block_map;
20 functions.reserve(cfg.Functions().size());
21
22 for (const Flow::Function& cfg_function : cfg.Functions()) {
23 Function& function{functions.emplace_back()};
24
25 const size_t num_blocks{cfg_function.blocks.size()};
26 IR::Block* block_memory{block_alloc_pool.allocate(num_blocks)};
27 function.blocks.reserve(num_blocks);
28
29 block_map.resize(cfg_function.blocks_data.size());
30
31 // Visit the instructions of all blocks
32 for (const Flow::BlockId block_id : cfg_function.blocks) {
33 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
34
35 IR::Block* const block{std::construct_at(block_memory, Translate(env, flow_block))};
36 ++block_memory;
37 function.blocks.push_back(block);
38 block_map[flow_block.id] = block;
39 }
40 // Now that all blocks are defined, emit the termination instructions
41 for (const Flow::BlockId block_id : cfg_function.blocks) {
42 const Flow::Block& flow_block{cfg_function.blocks_data[block_id]};
43 EmitTerminationCode(flow_block, block_map);
44 }
45 }
46}
47
48std::string DumpProgram(const Program& program) {
49 size_t index{0};
50 std::map<const IR::Inst*, size_t> inst_to_index;
51 std::map<const IR::Block*, size_t> block_to_index;
52
53 for (const Program::Function& function : program.functions) {
54 for (const IR::Block* const block : function.blocks) {
55 block_to_index.emplace(block, index);
56 ++index;
57 }
58 }
59 std::string ret;
60 for (const Program::Function& function : program.functions) {
61 ret += fmt::format("Function\n");
62 for (const IR::Block* const block : function.blocks) {
63 ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n';
64 }
65 }
66 return ret;
67}
68
69} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/program.h b/src/shader_recompiler/frontend/maxwell/program.h
new file mode 100644
index 000000000..7814b2c01
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/program.h
@@ -0,0 +1,39 @@
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 <vector>
9
10#include <boost/pool/pool_alloc.hpp>
11
12#include "shader_recompiler/environment.h"
13#include "shader_recompiler/frontend/ir/basic_block.h"
14#include "shader_recompiler/frontend/maxwell/control_flow.h"
15
16namespace Shader::Maxwell {
17
18class Program {
19 friend std::string DumpProgram(const Program& program);
20
21public:
22 explicit Program(Environment& env, const Flow::CFG& cfg);
23
24private:
25 struct Function {
26 ~Function();
27
28 std::vector<IR::Block*> blocks;
29 };
30
31 boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete,
32 boost::details::pool::null_mutex>
33 block_alloc_pool;
34 std::vector<Function> functions;
35};
36
37[[nodiscard]] std::string DumpProgram(const Program& program);
38
39} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.cpp b/src/shader_recompiler/frontend/maxwell/termination_code.cpp
new file mode 100644
index 000000000..a4ea5c5e3
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/termination_code.cpp
@@ -0,0 +1,79 @@
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 <span>
6
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/ir_emitter.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11#include "shader_recompiler/frontend/maxwell/termination_code.h"
12
13namespace Shader::Maxwell {
14
15static void EmitExit(IR::IREmitter& ir) {
16 ir.Exit();
17}
18
19static IR::U1 GetFlowTest(IR::FlowTest flow_test, IR::IREmitter& ir) {
20 switch (flow_test) {
21 case IR::FlowTest::T:
22 return ir.Imm1(true);
23 case IR::FlowTest::F:
24 return ir.Imm1(false);
25 case IR::FlowTest::NE:
26 // FIXME: Verify this
27 return ir.LogicalNot(ir.GetZFlag());
28 case IR::FlowTest::NaN:
29 // FIXME: Verify this
30 return ir.LogicalAnd(ir.GetSFlag(), ir.GetZFlag());
31 default:
32 throw NotImplementedException("Flow test {}", flow_test);
33 }
34}
35
36static IR::U1 GetCond(IR::Condition cond, IR::IREmitter& ir) {
37 const IR::FlowTest flow_test{cond.FlowTest()};
38 const auto [pred, pred_negated]{cond.Pred()};
39 if (pred == IR::Pred::PT && !pred_negated) {
40 return GetFlowTest(flow_test, ir);
41 }
42 if (flow_test == IR::FlowTest::T) {
43 return ir.GetPred(pred, pred_negated);
44 }
45 return ir.LogicalAnd(ir.GetPred(pred, pred_negated), GetFlowTest(flow_test, ir));
46}
47
48static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map,
49 IR::IREmitter& ir) {
50 if (flow_block.cond == true) {
51 return ir.Branch(block_map[flow_block.branch_true]);
52 }
53 if (flow_block.cond == false) {
54 return ir.Branch(block_map[flow_block.branch_false]);
55 }
56 return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true],
57 block_map[flow_block.branch_false]);
58}
59
60void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map) {
61 IR::Block* const block{block_map[flow_block.id]};
62 IR::IREmitter ir(*block);
63 switch (flow_block.end_class) {
64 case Flow::EndClass::Branch:
65 EmitBranch(flow_block, block_map, ir);
66 break;
67 case Flow::EndClass::Exit:
68 EmitExit(ir);
69 break;
70 case Flow::EndClass::Return:
71 ir.Return();
72 break;
73 case Flow::EndClass::Unreachable:
74 ir.Unreachable();
75 break;
76 }
77}
78
79} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.h b/src/shader_recompiler/frontend/maxwell/termination_code.h
new file mode 100644
index 000000000..b0d667942
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/termination_code.h
@@ -0,0 +1,16 @@
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 <span>
8
9#include "shader_recompiler/frontend/ir/basic_block.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11
12namespace Shader::Maxwell {
13
14void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map);
15
16} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp
new file mode 100644
index 000000000..e98bbd0d1
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/exit.cpp
@@ -0,0 +1,15 @@
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/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
8
9namespace Shader::Maxwell {
10
11void TranslatorVisitor::EXIT(u64) {
12 ir.Exit();
13}
14
15} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
new file mode 100644
index 000000000..c4288d9a8
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_conversion_integer.cpp
@@ -0,0 +1,133 @@
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/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/opcode.h"
8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
9
10namespace Shader::Maxwell {
11namespace {
12enum class DestFormat : u64 {
13 Invalid,
14 I16,
15 I32,
16 I64,
17};
18enum class SrcFormat : u64 {
19 Invalid,
20 F16,
21 F32,
22 F64,
23};
24enum class Rounding : u64 {
25 Round,
26 Floor,
27 Ceil,
28 Trunc,
29};
30
31union F2I {
32 u64 raw;
33 BitField<0, 8, IR::Reg> dest_reg;
34 BitField<8, 2, DestFormat> dest_format;
35 BitField<10, 2, SrcFormat> src_format;
36 BitField<12, 1, u64> is_signed;
37 BitField<39, 1, Rounding> rounding;
38 BitField<49, 1, u64> half;
39 BitField<44, 1, u64> ftz;
40 BitField<45, 1, u64> abs;
41 BitField<47, 1, u64> cc;
42 BitField<49, 1, u64> neg;
43};
44
45size_t BitSize(DestFormat dest_format) {
46 switch (dest_format) {
47 case DestFormat::I16:
48 return 16;
49 case DestFormat::I32:
50 return 32;
51 case DestFormat::I64:
52 return 64;
53 default:
54 throw NotImplementedException("Invalid destination format {}", dest_format);
55 }
56}
57
58void TranslateF2I(TranslatorVisitor& v, u64 insn, const IR::U16U32U64& op_a) {
59 // F2I is used to convert from a floating point value to an integer
60 const F2I f2i{insn};
61
62 const IR::U16U32U64 float_value{v.ir.FPAbsNeg(op_a, f2i.abs != 0, f2i.neg != 0)};
63 const IR::U16U32U64 rounded_value{[&] {
64 switch (f2i.rounding) {
65 case Rounding::Round:
66 return v.ir.FPRoundEven(float_value);
67 case Rounding::Floor:
68 return v.ir.FPFloor(float_value);
69 case Rounding::Ceil:
70 return v.ir.FPCeil(float_value);
71 case Rounding::Trunc:
72 return v.ir.FPTrunc(float_value);
73 default:
74 throw NotImplementedException("Invalid F2I rounding {}", f2i.rounding.Value());
75 }
76 }()};
77
78 // TODO: Handle out of bounds conversions.
79 // For example converting F32 65537.0 to U16, the expected value is 0xffff,
80
81 const bool is_signed{f2i.is_signed != 0};
82 const size_t bitsize{BitSize(f2i.dest_format)};
83 const IR::U16U32U64 result{v.ir.ConvertFToI(bitsize, is_signed, rounded_value)};
84
85 v.X(f2i.dest_reg, result);
86
87 if (f2i.cc != 0) {
88 v.SetZFlag(v.ir.GetZeroFromOp(result));
89 if (is_signed) {
90 v.SetSFlag(v.ir.GetSignFromOp(result));
91 } else {
92 v.ResetSFlag();
93 }
94 v.ResetCFlag();
95
96 // TODO: Investigate if out of bound conversions sets the overflow flag
97 v.ResetOFlag();
98 }
99}
100} // Anonymous namespace
101
102void TranslatorVisitor::F2I_reg(u64 insn) {
103 union {
104 F2I base;
105 BitField<20, 8, IR::Reg> src_reg;
106 } const f2i{insn};
107
108 const IR::U16U32U64 op_a{[&]() -> IR::U16U32U64 {
109 switch (f2i.base.src_format) {
110 case SrcFormat::F16:
111 return ir.CompositeExtract(ir.UnpackFloat2x16(X(f2i.src_reg)), f2i.base.half);
112 case SrcFormat::F32:
113 return X(f2i.src_reg);
114 case SrcFormat::F64:
115 return ir.PackDouble2x32(ir.CompositeConstruct(X(f2i.src_reg), X(f2i.src_reg + 1)));
116 default:
117 throw NotImplementedException("Invalid F2I source format {}",
118 f2i.base.src_format.Value());
119 }
120 }()};
121
122 TranslateF2I(*this, insn, op_a);
123}
124
125void TranslatorVisitor::F2I_cbuf(u64) {
126 throw NotImplementedException("{}", Opcode::F2I_cbuf);
127}
128
129void TranslatorVisitor::F2I_imm(u64) {
130 throw NotImplementedException("{}", Opcode::F2I_imm);
131}
132
133} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
new file mode 100644
index 000000000..e2ab0dab2
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/floating_point_multi_function.cpp
@@ -0,0 +1,71 @@
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_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class Operation {
14 Cos = 0,
15 Sin = 1,
16 Ex2 = 2, // Base 2 exponent
17 Lg2 = 3, // Base 2 logarithm
18 Rcp = 4, // Reciprocal
19 Rsq = 5, // Reciprocal square root
20 Rcp64H = 6, // 64-bit reciprocal
21 Rsq64H = 7, // 64-bit reciprocal square root
22 Sqrt = 8,
23};
24} // Anonymous namespace
25
26void TranslatorVisitor::MUFU(u64 insn) {
27 // MUFU is used to implement a bunch of special functions. See Operation.
28 union {
29 u64 raw;
30 BitField<0, 8, IR::Reg> dest_reg;
31 BitField<8, 8, IR::Reg> src_reg;
32 BitField<20, 4, Operation> operation;
33 BitField<46, 1, u64> abs;
34 BitField<48, 1, u64> neg;
35 BitField<50, 1, u64> sat;
36 } const mufu{insn};
37
38 const IR::U32 op_a{ir.FPAbsNeg(X(mufu.src_reg), mufu.abs != 0, mufu.neg != 0)};
39 IR::U32 value{[&]() -> IR::U32 {
40 switch (mufu.operation) {
41 case Operation::Cos:
42 return ir.FPCosNotReduced(op_a);
43 case Operation::Sin:
44 return ir.FPSinNotReduced(op_a);
45 case Operation::Ex2:
46 return ir.FPExp2NotReduced(op_a);
47 case Operation::Lg2:
48 return ir.FPLog2(op_a);
49 case Operation::Rcp:
50 return ir.FPRecip(op_a);
51 case Operation::Rsq:
52 return ir.FPRecipSqrt(op_a);
53 case Operation::Rcp64H:
54 throw NotImplementedException("MUFU.RCP64H");
55 case Operation::Rsq64H:
56 throw NotImplementedException("MUFU.RSQ64H");
57 case Operation::Sqrt:
58 return ir.FPSqrt(op_a);
59 default:
60 throw NotImplementedException("Invalid MUFU operation {}", mufu.operation.Value());
61 }
62 }()};
63
64 if (mufu.sat) {
65 value = ir.FPSaturate(value);
66 }
67
68 X(mufu.dest_reg, value);
69}
70
71} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
new file mode 100644
index 000000000..7bc7ce9f2
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.cpp
@@ -0,0 +1,79 @@
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_field.h"
6#include "shader_recompiler/frontend/ir/ir_emitter.h"
7#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
8
9namespace Shader::Maxwell {
10
11IR::U32 TranslatorVisitor::X(IR::Reg reg) {
12 return ir.GetReg(reg);
13}
14
15void TranslatorVisitor::X(IR::Reg dest_reg, const IR::U32& value) {
16 ir.SetReg(dest_reg, value);
17}
18
19IR::U32 TranslatorVisitor::GetCbuf(u64 insn) {
20 union {
21 u64 raw;
22 BitField<20, 14, s64> offset;
23 BitField<34, 5, u64> binding;
24 } const cbuf{insn};
25 if (cbuf.binding >= 18) {
26 throw NotImplementedException("Out of bounds constant buffer binding {}", cbuf.binding);
27 }
28 if (cbuf.offset >= 0x10'000 || cbuf.offset < 0) {
29 throw NotImplementedException("Out of bounds constant buffer offset {}", cbuf.offset);
30 }
31 const IR::U32 binding{ir.Imm32(static_cast<u32>(cbuf.binding))};
32 const IR::U32 byte_offset{ir.Imm32(static_cast<u32>(cbuf.offset) * 4)};
33 return ir.GetCbuf(binding, byte_offset);
34}
35
36IR::U32 TranslatorVisitor::GetImm(u64 insn) {
37 union {
38 u64 raw;
39 BitField<20, 19, u64> value;
40 BitField<56, 1, u64> is_negative;
41 } const imm{insn};
42 const s32 positive_value{static_cast<s32>(imm.value)};
43 const s32 value{imm.is_negative != 0 ? -positive_value : positive_value};
44 return ir.Imm32(value);
45}
46
47void TranslatorVisitor::SetZFlag(const IR::U1& value) {
48 ir.SetZFlag(value);
49}
50
51void TranslatorVisitor::SetSFlag(const IR::U1& value) {
52 ir.SetSFlag(value);
53}
54
55void TranslatorVisitor::SetCFlag(const IR::U1& value) {
56 ir.SetCFlag(value);
57}
58
59void TranslatorVisitor::SetOFlag(const IR::U1& value) {
60 ir.SetOFlag(value);
61}
62
63void TranslatorVisitor::ResetZero() {
64 SetZFlag(ir.Imm1(false));
65}
66
67void TranslatorVisitor::ResetSFlag() {
68 SetSFlag(ir.Imm1(false));
69}
70
71void TranslatorVisitor::ResetCFlag() {
72 SetCFlag(ir.Imm1(false));
73}
74
75void TranslatorVisitor::ResetOFlag() {
76 SetOFlag(ir.Imm1(false));
77}
78
79} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
new file mode 100644
index 000000000..bc607b002
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h
@@ -0,0 +1,316 @@
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/environment.h"
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/ir/ir_emitter.h"
8#include "shader_recompiler/frontend/maxwell/instruction.h"
9
10namespace Shader::Maxwell {
11
12class TranslatorVisitor {
13public:
14 explicit TranslatorVisitor(Environment& env_, IR::Block& block) : env{env_} ,ir(block) {}
15
16 Environment& env;
17 IR::IREmitter ir;
18
19 void AL2P(u64 insn);
20 void ALD(u64 insn);
21 void AST(u64 insn);
22 void ATOM_cas(u64 insn);
23 void ATOM(u64 insn);
24 void ATOMS_cas(u64 insn);
25 void ATOMS(u64 insn);
26 void B2R(u64 insn);
27 void BAR(u64 insn);
28 void BFE_reg(u64 insn);
29 void BFE_cbuf(u64 insn);
30 void BFE_imm(u64 insn);
31 void BFI_reg(u64 insn);
32 void BFI_rc(u64 insn);
33 void BFI_cr(u64 insn);
34 void BFI_imm(u64 insn);
35 void BPT(u64 insn);
36 void BRA(u64 insn);
37 void BRK(u64 insn);
38 void BRX(u64 insn);
39 void CAL(u64 insn);
40 void CCTL(u64 insn);
41 void CCTLL(u64 insn);
42 void CONT(u64 insn);
43 void CS2R(u64 insn);
44 void CSET(u64 insn);
45 void CSETP(u64 insn);
46 void DADD_reg(u64 insn);
47 void DADD_cbuf(u64 insn);
48 void DADD_imm(u64 insn);
49 void DEPBAR(u64 insn);
50 void DFMA_reg(u64 insn);
51 void DFMA_rc(u64 insn);
52 void DFMA_cr(u64 insn);
53 void DFMA_imm(u64 insn);
54 void DMNMX_reg(u64 insn);
55 void DMNMX_cbuf(u64 insn);
56 void DMNMX_imm(u64 insn);
57 void DMUL_reg(u64 insn);
58 void DMUL_cbuf(u64 insn);
59 void DMUL_imm(u64 insn);
60 void DSET_reg(u64 insn);
61 void DSET_cbuf(u64 insn);
62 void DSET_imm(u64 insn);
63 void DSETP_reg(u64 insn);
64 void DSETP_cbuf(u64 insn);
65 void DSETP_imm(u64 insn);
66 void EXIT(u64 insn);
67 void F2F_reg(u64 insn);
68 void F2F_cbuf(u64 insn);
69 void F2F_imm(u64 insn);
70 void F2I_reg(u64 insn);
71 void F2I_cbuf(u64 insn);
72 void F2I_imm(u64 insn);
73 void FADD_reg(u64 insn);
74 void FADD_cbuf(u64 insn);
75 void FADD_imm(u64 insn);
76 void FADD32I(u64 insn);
77 void FCHK_reg(u64 insn);
78 void FCHK_cbuf(u64 insn);
79 void FCHK_imm(u64 insn);
80 void FCMP_reg(u64 insn);
81 void FCMP_rc(u64 insn);
82 void FCMP_cr(u64 insn);
83 void FCMP_imm(u64 insn);
84 void FFMA_reg(u64 insn);
85 void FFMA_rc(u64 insn);
86 void FFMA_cr(u64 insn);
87 void FFMA_imm(u64 insn);
88 void FFMA32I(u64 insn);
89 void FLO_reg(u64 insn);
90 void FLO_cbuf(u64 insn);
91 void FLO_imm(u64 insn);
92 void FMNMX_reg(u64 insn);
93 void FMNMX_cbuf(u64 insn);
94 void FMNMX_imm(u64 insn);
95 void FMUL_reg(u64 insn);
96 void FMUL_cbuf(u64 insn);
97 void FMUL_imm(u64 insn);
98 void FMUL32I(u64 insn);
99 void FSET_reg(u64 insn);
100 void FSET_cbuf(u64 insn);
101 void FSET_imm(u64 insn);
102 void FSETP_reg(u64 insn);
103 void FSETP_cbuf(u64 insn);
104 void FSETP_imm(u64 insn);
105 void FSWZADD(u64 insn);
106 void GETCRSPTR(u64 insn);
107 void GETLMEMBASE(u64 insn);
108 void HADD2_reg(u64 insn);
109 void HADD2_cbuf(u64 insn);
110 void HADD2_imm(u64 insn);
111 void HADD2_32I(u64 insn);
112 void HFMA2_reg(u64 insn);
113 void HFMA2_rc(u64 insn);
114 void HFMA2_cr(u64 insn);
115 void HFMA2_imm(u64 insn);
116 void HFMA2_32I(u64 insn);
117 void HMUL2_reg(u64 insn);
118 void HMUL2_cbuf(u64 insn);
119 void HMUL2_imm(u64 insn);
120 void HMUL2_32I(u64 insn);
121 void HSET2_reg(u64 insn);
122 void HSET2_cbuf(u64 insn);
123 void HSET2_imm(u64 insn);
124 void HSETP2_reg(u64 insn);
125 void HSETP2_cbuf(u64 insn);
126 void HSETP2_imm(u64 insn);
127 void I2F_reg(u64 insn);
128 void I2F_cbuf(u64 insn);
129 void I2F_imm(u64 insn);
130 void I2I_reg(u64 insn);
131 void I2I_cbuf(u64 insn);
132 void I2I_imm(u64 insn);
133 void IADD_reg(u64 insn);
134 void IADD_cbuf(u64 insn);
135 void IADD_imm(u64 insn);
136 void IADD3_reg(u64 insn);
137 void IADD3_cbuf(u64 insn);
138 void IADD3_imm(u64 insn);
139 void IADD32I(u64 insn);
140 void ICMP_reg(u64 insn);
141 void ICMP_rc(u64 insn);
142 void ICMP_cr(u64 insn);
143 void ICMP_imm(u64 insn);
144 void IDE(u64 insn);
145 void IDP_reg(u64 insn);
146 void IDP_imm(u64 insn);
147 void IMAD_reg(u64 insn);
148 void IMAD_rc(u64 insn);
149 void IMAD_cr(u64 insn);
150 void IMAD_imm(u64 insn);
151 void IMAD32I(u64 insn);
152 void IMADSP_reg(u64 insn);
153 void IMADSP_rc(u64 insn);
154 void IMADSP_cr(u64 insn);
155 void IMADSP_imm(u64 insn);
156 void IMNMX_reg(u64 insn);
157 void IMNMX_cbuf(u64 insn);
158 void IMNMX_imm(u64 insn);
159 void IMUL_reg(u64 insn);
160 void IMUL_cbuf(u64 insn);
161 void IMUL_imm(u64 insn);
162 void IMUL32I(u64 insn);
163 void IPA(u64 insn);
164 void ISBERD(u64 insn);
165 void ISCADD_reg(u64 insn);
166 void ISCADD_cbuf(u64 insn);
167 void ISCADD_imm(u64 insn);
168 void ISCADD32I(u64 insn);
169 void ISET_reg(u64 insn);
170 void ISET_cbuf(u64 insn);
171 void ISET_imm(u64 insn);
172 void ISETP_reg(u64 insn);
173 void ISETP_cbuf(u64 insn);
174 void ISETP_imm(u64 insn);
175 void JCAL(u64 insn);
176 void JMP(u64 insn);
177 void JMX(u64 insn);
178 void KIL(u64 insn);
179 void LD(u64 insn);
180 void LDC(u64 insn);
181 void LDG(u64 insn);
182 void LDL(u64 insn);
183 void LDS(u64 insn);
184 void LEA_hi_reg(u64 insn);
185 void LEA_hi_cbuf(u64 insn);
186 void LEA_lo_reg(u64 insn);
187 void LEA_lo_cbuf(u64 insn);
188 void LEA_lo_imm(u64 insn);
189 void LEPC(u64 insn);
190 void LONGJMP(u64 insn);
191 void LOP_reg(u64 insn);
192 void LOP_cbuf(u64 insn);
193 void LOP_imm(u64 insn);
194 void LOP3_reg(u64 insn);
195 void LOP3_cbuf(u64 insn);
196 void LOP3_imm(u64 insn);
197 void LOP32I(u64 insn);
198 void MEMBAR(u64 insn);
199 void MOV_reg(u64 insn);
200 void MOV_cbuf(u64 insn);
201 void MOV_imm(u64 insn);
202 void MOV32I(u64 insn);
203 void MUFU(u64 insn);
204 void NOP(u64 insn);
205 void OUT_reg(u64 insn);
206 void OUT_cbuf(u64 insn);
207 void OUT_imm(u64 insn);
208 void P2R_reg(u64 insn);
209 void P2R_cbuf(u64 insn);
210 void P2R_imm(u64 insn);
211 void PBK(u64 insn);
212 void PCNT(u64 insn);
213 void PEXIT(u64 insn);
214 void PIXLD(u64 insn);
215 void PLONGJMP(u64 insn);
216 void POPC_reg(u64 insn);
217 void POPC_cbuf(u64 insn);
218 void POPC_imm(u64 insn);
219 void PRET(u64 insn);
220 void PRMT_reg(u64 insn);
221 void PRMT_rc(u64 insn);
222 void PRMT_cr(u64 insn);
223 void PRMT_imm(u64 insn);
224 void PSET(u64 insn);
225 void PSETP(u64 insn);
226 void R2B(u64 insn);
227 void R2P_reg(u64 insn);
228 void R2P_cbuf(u64 insn);
229 void R2P_imm(u64 insn);
230 void RAM(u64 insn);
231 void RED(u64 insn);
232 void RET(u64 insn);
233 void RRO_reg(u64 insn);
234 void RRO_cbuf(u64 insn);
235 void RRO_imm(u64 insn);
236 void RTT(u64 insn);
237 void S2R(u64 insn);
238 void SAM(u64 insn);
239 void SEL_reg(u64 insn);
240 void SEL_cbuf(u64 insn);
241 void SEL_imm(u64 insn);
242 void SETCRSPTR(u64 insn);
243 void SETLMEMBASE(u64 insn);
244 void SHF_l_reg(u64 insn);
245 void SHF_l_imm(u64 insn);
246 void SHF_r_reg(u64 insn);
247 void SHF_r_imm(u64 insn);
248 void SHFL(u64 insn);
249 void SHL_reg(u64 insn);
250 void SHL_cbuf(u64 insn);
251 void SHL_imm(u64 insn);
252 void SHR_reg(u64 insn);
253 void SHR_cbuf(u64 insn);
254 void SHR_imm(u64 insn);
255 void SSY(u64 insn);
256 void ST(u64 insn);
257 void STG(u64 insn);
258 void STL(u64 insn);
259 void STP(u64 insn);
260 void STS(u64 insn);
261 void SUATOM_cas(u64 insn);
262 void SULD(u64 insn);
263 void SURED(u64 insn);
264 void SUST(u64 insn);
265 void SYNC(u64 insn);
266 void TEX(u64 insn);
267 void TEX_b(u64 insn);
268 void TEXS(u64 insn);
269 void TLD(u64 insn);
270 void TLD_b(u64 insn);
271 void TLD4(u64 insn);
272 void TLD4_b(u64 insn);
273 void TLD4S(u64 insn);
274 void TLDS(u64 insn);
275 void TMML(u64 insn);
276 void TMML_b(u64 insn);
277 void TXA(u64 insn);
278 void TXD(u64 insn);
279 void TXD_b(u64 insn);
280 void TXQ(u64 insn);
281 void TXQ_b(u64 insn);
282 void VABSDIFF(u64 insn);
283 void VABSDIFF4(u64 insn);
284 void VADD(u64 insn);
285 void VMAD(u64 insn);
286 void VMNMX(u64 insn);
287 void VOTE(u64 insn);
288 void VOTE_vtg(u64 insn);
289 void VSET(u64 insn);
290 void VSETP(u64 insn);
291 void VSHL(u64 insn);
292 void VSHR(u64 insn);
293 void XMAD_reg(u64 insn);
294 void XMAD_rc(u64 insn);
295 void XMAD_cr(u64 insn);
296 void XMAD_imm(u64 insn);
297
298 [[nodiscard]] IR::U32 X(IR::Reg reg);
299 void X(IR::Reg dest_reg, const IR::U32& value);
300
301 [[nodiscard]] IR::U32 GetCbuf(u64 insn);
302
303 [[nodiscard]] IR::U32 GetImm(u64 insn);
304
305 void SetZFlag(const IR::U1& value);
306 void SetSFlag(const IR::U1& value);
307 void SetCFlag(const IR::U1& value);
308 void SetOFlag(const IR::U1& value);
309
310 void ResetZero();
311 void ResetSFlag();
312 void ResetCFlag();
313 void ResetOFlag();
314};
315
316} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
new file mode 100644
index 000000000..23512db1a
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_attribute.cpp
@@ -0,0 +1,92 @@
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_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class InterpolationMode : u64 {
14 Pass = 0,
15 Multiply = 1,
16 Constant = 2,
17 Sc = 3,
18};
19
20enum class SampleMode : u64 {
21 Default = 0,
22 Centroid = 1,
23 Offset = 2,
24};
25} // Anonymous namespace
26
27void TranslatorVisitor::IPA(u64 insn) {
28 // IPA is the instruction used to read varyings from a fragment shader.
29 // gl_FragCoord is mapped to the gl_Position attribute.
30 // It yields unknown results when used outside of the fragment shader stage.
31 union {
32 u64 raw;
33 BitField<0, 8, IR::Reg> dest_reg;
34 BitField<8, 8, IR::Reg> index_reg;
35 BitField<20, 8, IR::Reg> multiplier;
36 BitField<30, 8, IR::Attribute> attribute;
37 BitField<38, 1, u64> idx;
38 BitField<51, 1, u64> sat;
39 BitField<52, 2, SampleMode> sample_mode;
40 BitField<54, 2, InterpolationMode> interpolation_mode;
41 } const ipa{insn};
42
43 // Indexed IPAs are used for indexed varyings.
44 // For example:
45 //
46 // in vec4 colors[4];
47 // uniform int idx;
48 // void main() {
49 // gl_FragColor = colors[idx];
50 // }
51 const bool is_indexed{ipa.idx != 0 && ipa.index_reg != IR::Reg::RZ};
52 if (is_indexed) {
53 throw NotImplementedException("IPA.IDX");
54 }
55
56 const IR::Attribute attribute{ipa.attribute};
57 IR::U32 value{ir.GetAttribute(attribute)};
58 if (IR::IsGeneric(attribute)) {
59 // const bool is_perspective{UnimplementedReadHeader(GenericAttributeIndex(attribute))};
60 const bool is_perspective{false};
61 if (is_perspective) {
62 const IR::U32 rcp_position_w{ir.FPRecip(ir.GetAttribute(IR::Attribute::PositionW))};
63 value = ir.FPMul(value, rcp_position_w);
64 }
65 }
66
67 switch (ipa.interpolation_mode) {
68 case InterpolationMode::Pass:
69 break;
70 case InterpolationMode::Multiply:
71 value = ir.FPMul(value, ir.GetReg(ipa.multiplier));
72 break;
73 case InterpolationMode::Constant:
74 throw NotImplementedException("IPA.CONSTANT");
75 case InterpolationMode::Sc:
76 throw NotImplementedException("IPA.SC");
77 }
78
79 // Saturated IPAs are generally generated out of clamped varyings.
80 // For example: clamp(some_varying, 0.0, 1.0)
81 const bool is_saturated{ipa.sat != 0};
82 if (is_saturated) {
83 if (attribute == IR::Attribute::FrontFace) {
84 throw NotImplementedException("IPA.SAT on FrontFace");
85 }
86 value = ir.FPSaturate(value);
87 }
88
89 ir.SetReg(ipa.dest_reg, value);
90}
91
92} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
new file mode 100644
index 000000000..d8fd387cf
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/load_store_memory.cpp
@@ -0,0 +1,90 @@
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_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13enum class StoreSize : u64 {
14 U8,
15 S8,
16 U16,
17 S16,
18 B32,
19 B64,
20 B128,
21};
22
23// See Table 28 in https://docs.nvidia.com/cuda/parallel-thread-execution/index.html
24enum class StoreCache : u64 {
25 WB, // Cache write-back all coherent levels
26 CG, // Cache at global level
27 CS, // Cache streaming, likely to be accessed once
28 WT, // Cache write-through (to system memory)
29};
30} // Anonymous namespace
31
32void TranslatorVisitor::STG(u64 insn) {
33 // STG stores registers into global memory.
34 union {
35 u64 raw;
36 BitField<0, 8, IR::Reg> data_reg;
37 BitField<8, 8, IR::Reg> addr_reg;
38 BitField<45, 1, u64> e;
39 BitField<46, 2, StoreCache> cache;
40 BitField<48, 3, StoreSize> size;
41 } const stg{insn};
42
43 const IR::U64 address{[&]() -> IR::U64 {
44 if (stg.e == 0) {
45 // STG without .E uses a 32-bit pointer, zero-extend it
46 return ir.ConvertU(64, X(stg.addr_reg));
47 }
48 if (!IR::IsAligned(stg.addr_reg, 2)) {
49 throw NotImplementedException("Unaligned address register");
50 }
51 // Pack two registers to build the 32-bit address
52 return ir.PackUint2x32(ir.CompositeConstruct(X(stg.addr_reg), X(stg.addr_reg + 1)));
53 }()};
54
55 switch (stg.size) {
56 case StoreSize::U8:
57 ir.WriteGlobalU8(address, X(stg.data_reg));
58 break;
59 case StoreSize::S8:
60 ir.WriteGlobalS8(address, X(stg.data_reg));
61 break;
62 case StoreSize::U16:
63 ir.WriteGlobalU16(address, X(stg.data_reg));
64 break;
65 case StoreSize::S16:
66 ir.WriteGlobalS16(address, X(stg.data_reg));
67 break;
68 case StoreSize::B32:
69 ir.WriteGlobal32(address, X(stg.data_reg));
70 break;
71 case StoreSize::B64: {
72 if (!IR::IsAligned(stg.data_reg, 2)) {
73 throw NotImplementedException("Unaligned data registers");
74 }
75 const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1))};
76 ir.WriteGlobal64(address, vector);
77 break;
78 }
79 case StoreSize::B128:
80 if (!IR::IsAligned(stg.data_reg, 4)) {
81 throw NotImplementedException("Unaligned data registers");
82 }
83 const IR::Value vector{ir.CompositeConstruct(X(stg.data_reg), X(stg.data_reg + 1),
84 X(stg.data_reg + 2), X(stg.data_reg + 3))};
85 ir.WriteGlobal128(address, vector);
86 break;
87 }
88}
89
90} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
new file mode 100644
index 000000000..c907c1ffb
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp
@@ -0,0 +1,1105 @@
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/common_types.h"
6#include "shader_recompiler/exception.h"
7#include "shader_recompiler/frontend/maxwell/opcode.h"
8#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
9
10#include "shader_recompiler/ir_opt/passes.h"
11
12namespace Shader::Maxwell {
13
14[[maybe_unused]] static inline void DumpOptimized(IR::Block& block) {
15 auto raw{IR::DumpBlock(block)};
16
17 Optimization::GetSetElimination(block);
18 Optimization::DeadCodeEliminationPass(block);
19 Optimization::IdentityRemovalPass(block);
20 auto dumped{IR::DumpBlock(block)};
21
22 fmt::print(stderr, "{}", dumped);
23}
24
25[[noreturn]] static void ThrowNotImplemented(Opcode opcode) {
26 throw NotImplementedException("Instruction {} is not implemented", opcode);
27}
28
29void TranslatorVisitor::AL2P(u64) {
30 ThrowNotImplemented(Opcode::AL2P);
31}
32
33void TranslatorVisitor::ALD(u64) {
34 ThrowNotImplemented(Opcode::ALD);
35}
36
37void TranslatorVisitor::AST(u64) {
38 ThrowNotImplemented(Opcode::AST);
39}
40
41void TranslatorVisitor::ATOM_cas(u64) {
42 ThrowNotImplemented(Opcode::ATOM_cas);
43}
44
45void TranslatorVisitor::ATOM(u64) {
46 ThrowNotImplemented(Opcode::ATOM);
47}
48
49void TranslatorVisitor::ATOMS_cas(u64) {
50 ThrowNotImplemented(Opcode::ATOMS_cas);
51}
52
53void TranslatorVisitor::ATOMS(u64) {
54 ThrowNotImplemented(Opcode::ATOMS);
55}
56
57void TranslatorVisitor::B2R(u64) {
58 ThrowNotImplemented(Opcode::B2R);
59}
60
61void TranslatorVisitor::BAR(u64) {
62 ThrowNotImplemented(Opcode::BAR);
63}
64
65void TranslatorVisitor::BFE_reg(u64) {
66 ThrowNotImplemented(Opcode::BFE_reg);
67}
68
69void TranslatorVisitor::BFE_cbuf(u64) {
70 ThrowNotImplemented(Opcode::BFE_cbuf);
71}
72
73void TranslatorVisitor::BFE_imm(u64) {
74 ThrowNotImplemented(Opcode::BFE_imm);
75}
76
77void TranslatorVisitor::BFI_reg(u64) {
78 ThrowNotImplemented(Opcode::BFI_reg);
79}
80
81void TranslatorVisitor::BFI_rc(u64) {
82 ThrowNotImplemented(Opcode::BFI_rc);
83}
84
85void TranslatorVisitor::BFI_cr(u64) {
86 ThrowNotImplemented(Opcode::BFI_cr);
87}
88
89void TranslatorVisitor::BFI_imm(u64) {
90 ThrowNotImplemented(Opcode::BFI_imm);
91}
92
93void TranslatorVisitor::BPT(u64) {
94 ThrowNotImplemented(Opcode::BPT);
95}
96
97void TranslatorVisitor::BRA(u64) {
98 ThrowNotImplemented(Opcode::BRA);
99}
100
101void TranslatorVisitor::BRK(u64) {
102 ThrowNotImplemented(Opcode::BRK);
103}
104
105void TranslatorVisitor::BRX(u64) {
106 ThrowNotImplemented(Opcode::BRX);
107}
108
109void TranslatorVisitor::CAL(u64) {
110 ThrowNotImplemented(Opcode::CAL);
111}
112
113void TranslatorVisitor::CCTL(u64) {
114 ThrowNotImplemented(Opcode::CCTL);
115}
116
117void TranslatorVisitor::CCTLL(u64) {
118 ThrowNotImplemented(Opcode::CCTLL);
119}
120
121void TranslatorVisitor::CONT(u64) {
122 ThrowNotImplemented(Opcode::CONT);
123}
124
125void TranslatorVisitor::CS2R(u64) {
126 ThrowNotImplemented(Opcode::CS2R);
127}
128
129void TranslatorVisitor::CSET(u64) {
130 ThrowNotImplemented(Opcode::CSET);
131}
132
133void TranslatorVisitor::CSETP(u64) {
134 ThrowNotImplemented(Opcode::CSETP);
135}
136
137void TranslatorVisitor::DADD_reg(u64) {
138 ThrowNotImplemented(Opcode::DADD_reg);
139}
140
141void TranslatorVisitor::DADD_cbuf(u64) {
142 ThrowNotImplemented(Opcode::DADD_cbuf);
143}
144
145void TranslatorVisitor::DADD_imm(u64) {
146 ThrowNotImplemented(Opcode::DADD_imm);
147}
148
149void TranslatorVisitor::DEPBAR(u64) {
150 ThrowNotImplemented(Opcode::DEPBAR);
151}
152
153void TranslatorVisitor::DFMA_reg(u64) {
154 ThrowNotImplemented(Opcode::DFMA_reg);
155}
156
157void TranslatorVisitor::DFMA_rc(u64) {
158 ThrowNotImplemented(Opcode::DFMA_rc);
159}
160
161void TranslatorVisitor::DFMA_cr(u64) {
162 ThrowNotImplemented(Opcode::DFMA_cr);
163}
164
165void TranslatorVisitor::DFMA_imm(u64) {
166 ThrowNotImplemented(Opcode::DFMA_imm);
167}
168
169void TranslatorVisitor::DMNMX_reg(u64) {
170 ThrowNotImplemented(Opcode::DMNMX_reg);
171}
172
173void TranslatorVisitor::DMNMX_cbuf(u64) {
174 ThrowNotImplemented(Opcode::DMNMX_cbuf);
175}
176
177void TranslatorVisitor::DMNMX_imm(u64) {
178 ThrowNotImplemented(Opcode::DMNMX_imm);
179}
180
181void TranslatorVisitor::DMUL_reg(u64) {
182 ThrowNotImplemented(Opcode::DMUL_reg);
183}
184
185void TranslatorVisitor::DMUL_cbuf(u64) {
186 ThrowNotImplemented(Opcode::DMUL_cbuf);
187}
188
189void TranslatorVisitor::DMUL_imm(u64) {
190 ThrowNotImplemented(Opcode::DMUL_imm);
191}
192
193void TranslatorVisitor::DSET_reg(u64) {
194 ThrowNotImplemented(Opcode::DSET_reg);
195}
196
197void TranslatorVisitor::DSET_cbuf(u64) {
198 ThrowNotImplemented(Opcode::DSET_cbuf);
199}
200
201void TranslatorVisitor::DSET_imm(u64) {
202 ThrowNotImplemented(Opcode::DSET_imm);
203}
204
205void TranslatorVisitor::DSETP_reg(u64) {
206 ThrowNotImplemented(Opcode::DSETP_reg);
207}
208
209void TranslatorVisitor::DSETP_cbuf(u64) {
210 ThrowNotImplemented(Opcode::DSETP_cbuf);
211}
212
213void TranslatorVisitor::DSETP_imm(u64) {
214 ThrowNotImplemented(Opcode::DSETP_imm);
215}
216
217void TranslatorVisitor::EXIT(u64) {
218 throw LogicError("Visting EXIT instruction");
219}
220
221void TranslatorVisitor::F2F_reg(u64) {
222 ThrowNotImplemented(Opcode::F2F_reg);
223}
224
225void TranslatorVisitor::F2F_cbuf(u64) {
226 ThrowNotImplemented(Opcode::F2F_cbuf);
227}
228
229void TranslatorVisitor::F2F_imm(u64) {
230 ThrowNotImplemented(Opcode::F2F_imm);
231}
232
233void TranslatorVisitor::FADD_reg(u64) {
234 ThrowNotImplemented(Opcode::FADD_reg);
235}
236
237void TranslatorVisitor::FADD_cbuf(u64) {
238 ThrowNotImplemented(Opcode::FADD_cbuf);
239}
240
241void TranslatorVisitor::FADD_imm(u64) {
242 ThrowNotImplemented(Opcode::FADD_imm);
243}
244
245void TranslatorVisitor::FADD32I(u64) {
246 ThrowNotImplemented(Opcode::FADD32I);
247}
248
249void TranslatorVisitor::FCHK_reg(u64) {
250 ThrowNotImplemented(Opcode::FCHK_reg);
251}
252
253void TranslatorVisitor::FCHK_cbuf(u64) {
254 ThrowNotImplemented(Opcode::FCHK_cbuf);
255}
256
257void TranslatorVisitor::FCHK_imm(u64) {
258 ThrowNotImplemented(Opcode::FCHK_imm);
259}
260
261void TranslatorVisitor::FCMP_reg(u64) {
262 ThrowNotImplemented(Opcode::FCMP_reg);
263}
264
265void TranslatorVisitor::FCMP_rc(u64) {
266 ThrowNotImplemented(Opcode::FCMP_rc);
267}
268
269void TranslatorVisitor::FCMP_cr(u64) {
270 ThrowNotImplemented(Opcode::FCMP_cr);
271}
272
273void TranslatorVisitor::FCMP_imm(u64) {
274 ThrowNotImplemented(Opcode::FCMP_imm);
275}
276
277void TranslatorVisitor::FFMA_reg(u64) {
278 ThrowNotImplemented(Opcode::FFMA_reg);
279}
280
281void TranslatorVisitor::FFMA_rc(u64) {
282 ThrowNotImplemented(Opcode::FFMA_rc);
283}
284
285void TranslatorVisitor::FFMA_cr(u64) {
286 ThrowNotImplemented(Opcode::FFMA_cr);
287}
288
289void TranslatorVisitor::FFMA_imm(u64) {
290 ThrowNotImplemented(Opcode::FFMA_imm);
291}
292
293void TranslatorVisitor::FFMA32I(u64) {
294 ThrowNotImplemented(Opcode::FFMA32I);
295}
296
297void TranslatorVisitor::FLO_reg(u64) {
298 ThrowNotImplemented(Opcode::FLO_reg);
299}
300
301void TranslatorVisitor::FLO_cbuf(u64) {
302 ThrowNotImplemented(Opcode::FLO_cbuf);
303}
304
305void TranslatorVisitor::FLO_imm(u64) {
306 ThrowNotImplemented(Opcode::FLO_imm);
307}
308
309void TranslatorVisitor::FMNMX_reg(u64) {
310 ThrowNotImplemented(Opcode::FMNMX_reg);
311}
312
313void TranslatorVisitor::FMNMX_cbuf(u64) {
314 ThrowNotImplemented(Opcode::FMNMX_cbuf);
315}
316
317void TranslatorVisitor::FMNMX_imm(u64) {
318 ThrowNotImplemented(Opcode::FMNMX_imm);
319}
320
321void TranslatorVisitor::FMUL_reg(u64) {
322 ThrowNotImplemented(Opcode::FMUL_reg);
323}
324
325void TranslatorVisitor::FMUL_cbuf(u64) {
326 ThrowNotImplemented(Opcode::FMUL_cbuf);
327}
328
329void TranslatorVisitor::FMUL_imm(u64) {
330 ThrowNotImplemented(Opcode::FMUL_imm);
331}
332
333void TranslatorVisitor::FMUL32I(u64) {
334 ThrowNotImplemented(Opcode::FMUL32I);
335}
336
337void TranslatorVisitor::FSET_reg(u64) {
338 ThrowNotImplemented(Opcode::FSET_reg);
339}
340
341void TranslatorVisitor::FSET_cbuf(u64) {
342 ThrowNotImplemented(Opcode::FSET_cbuf);
343}
344
345void TranslatorVisitor::FSET_imm(u64) {
346 ThrowNotImplemented(Opcode::FSET_imm);
347}
348
349void TranslatorVisitor::FSETP_reg(u64) {
350 ThrowNotImplemented(Opcode::FSETP_reg);
351}
352
353void TranslatorVisitor::FSETP_cbuf(u64) {
354 ThrowNotImplemented(Opcode::FSETP_cbuf);
355}
356
357void TranslatorVisitor::FSETP_imm(u64) {
358 ThrowNotImplemented(Opcode::FSETP_imm);
359}
360
361void TranslatorVisitor::FSWZADD(u64) {
362 ThrowNotImplemented(Opcode::FSWZADD);
363}
364
365void TranslatorVisitor::GETCRSPTR(u64) {
366 ThrowNotImplemented(Opcode::GETCRSPTR);
367}
368
369void TranslatorVisitor::GETLMEMBASE(u64) {
370 ThrowNotImplemented(Opcode::GETLMEMBASE);
371}
372
373void TranslatorVisitor::HADD2_reg(u64) {
374 ThrowNotImplemented(Opcode::HADD2_reg);
375}
376
377void TranslatorVisitor::HADD2_cbuf(u64) {
378 ThrowNotImplemented(Opcode::HADD2_cbuf);
379}
380
381void TranslatorVisitor::HADD2_imm(u64) {
382 ThrowNotImplemented(Opcode::HADD2_imm);
383}
384
385void TranslatorVisitor::HADD2_32I(u64) {
386 ThrowNotImplemented(Opcode::HADD2_32I);
387}
388
389void TranslatorVisitor::HFMA2_reg(u64) {
390 ThrowNotImplemented(Opcode::HFMA2_reg);
391}
392
393void TranslatorVisitor::HFMA2_rc(u64) {
394 ThrowNotImplemented(Opcode::HFMA2_rc);
395}
396
397void TranslatorVisitor::HFMA2_cr(u64) {
398 ThrowNotImplemented(Opcode::HFMA2_cr);
399}
400
401void TranslatorVisitor::HFMA2_imm(u64) {
402 ThrowNotImplemented(Opcode::HFMA2_imm);
403}
404
405void TranslatorVisitor::HFMA2_32I(u64) {
406 ThrowNotImplemented(Opcode::HFMA2_32I);
407}
408
409void TranslatorVisitor::HMUL2_reg(u64) {
410 ThrowNotImplemented(Opcode::HMUL2_reg);
411}
412
413void TranslatorVisitor::HMUL2_cbuf(u64) {
414 ThrowNotImplemented(Opcode::HMUL2_cbuf);
415}
416
417void TranslatorVisitor::HMUL2_imm(u64) {
418 ThrowNotImplemented(Opcode::HMUL2_imm);
419}
420
421void TranslatorVisitor::HMUL2_32I(u64) {
422 ThrowNotImplemented(Opcode::HMUL2_32I);
423}
424
425void TranslatorVisitor::HSET2_reg(u64) {
426 ThrowNotImplemented(Opcode::HSET2_reg);
427}
428
429void TranslatorVisitor::HSET2_cbuf(u64) {
430 ThrowNotImplemented(Opcode::HSET2_cbuf);
431}
432
433void TranslatorVisitor::HSET2_imm(u64) {
434 ThrowNotImplemented(Opcode::HSET2_imm);
435}
436
437void TranslatorVisitor::HSETP2_reg(u64) {
438 ThrowNotImplemented(Opcode::HSETP2_reg);
439}
440
441void TranslatorVisitor::HSETP2_cbuf(u64) {
442 ThrowNotImplemented(Opcode::HSETP2_cbuf);
443}
444
445void TranslatorVisitor::HSETP2_imm(u64) {
446 ThrowNotImplemented(Opcode::HSETP2_imm);
447}
448
449void TranslatorVisitor::I2F_reg(u64) {
450 ThrowNotImplemented(Opcode::I2F_reg);
451}
452
453void TranslatorVisitor::I2F_cbuf(u64) {
454 ThrowNotImplemented(Opcode::I2F_cbuf);
455}
456
457void TranslatorVisitor::I2F_imm(u64) {
458 ThrowNotImplemented(Opcode::I2F_imm);
459}
460
461void TranslatorVisitor::I2I_reg(u64) {
462 ThrowNotImplemented(Opcode::I2I_reg);
463}
464
465void TranslatorVisitor::I2I_cbuf(u64) {
466 ThrowNotImplemented(Opcode::I2I_cbuf);
467}
468
469void TranslatorVisitor::I2I_imm(u64) {
470 ThrowNotImplemented(Opcode::I2I_imm);
471}
472
473void TranslatorVisitor::IADD_reg(u64) {
474 ThrowNotImplemented(Opcode::IADD_reg);
475}
476
477void TranslatorVisitor::IADD_cbuf(u64) {
478 ThrowNotImplemented(Opcode::IADD_cbuf);
479}
480
481void TranslatorVisitor::IADD_imm(u64) {
482 ThrowNotImplemented(Opcode::IADD_imm);
483}
484
485void TranslatorVisitor::IADD3_reg(u64) {
486 ThrowNotImplemented(Opcode::IADD3_reg);
487}
488
489void TranslatorVisitor::IADD3_cbuf(u64) {
490 ThrowNotImplemented(Opcode::IADD3_cbuf);
491}
492
493void TranslatorVisitor::IADD3_imm(u64) {
494 ThrowNotImplemented(Opcode::IADD3_imm);
495}
496
497void TranslatorVisitor::IADD32I(u64) {
498 ThrowNotImplemented(Opcode::IADD32I);
499}
500
501void TranslatorVisitor::ICMP_reg(u64) {
502 ThrowNotImplemented(Opcode::ICMP_reg);
503}
504
505void TranslatorVisitor::ICMP_rc(u64) {
506 ThrowNotImplemented(Opcode::ICMP_rc);
507}
508
509void TranslatorVisitor::ICMP_cr(u64) {
510 ThrowNotImplemented(Opcode::ICMP_cr);
511}
512
513void TranslatorVisitor::ICMP_imm(u64) {
514 ThrowNotImplemented(Opcode::ICMP_imm);
515}
516
517void TranslatorVisitor::IDE(u64) {
518 ThrowNotImplemented(Opcode::IDE);
519}
520
521void TranslatorVisitor::IDP_reg(u64) {
522 ThrowNotImplemented(Opcode::IDP_reg);
523}
524
525void TranslatorVisitor::IDP_imm(u64) {
526 ThrowNotImplemented(Opcode::IDP_imm);
527}
528
529void TranslatorVisitor::IMAD_reg(u64) {
530 ThrowNotImplemented(Opcode::IMAD_reg);
531}
532
533void TranslatorVisitor::IMAD_rc(u64) {
534 ThrowNotImplemented(Opcode::IMAD_rc);
535}
536
537void TranslatorVisitor::IMAD_cr(u64) {
538 ThrowNotImplemented(Opcode::IMAD_cr);
539}
540
541void TranslatorVisitor::IMAD_imm(u64) {
542 ThrowNotImplemented(Opcode::IMAD_imm);
543}
544
545void TranslatorVisitor::IMAD32I(u64) {
546 ThrowNotImplemented(Opcode::IMAD32I);
547}
548
549void TranslatorVisitor::IMADSP_reg(u64) {
550 ThrowNotImplemented(Opcode::IMADSP_reg);
551}
552
553void TranslatorVisitor::IMADSP_rc(u64) {
554 ThrowNotImplemented(Opcode::IMADSP_rc);
555}
556
557void TranslatorVisitor::IMADSP_cr(u64) {
558 ThrowNotImplemented(Opcode::IMADSP_cr);
559}
560
561void TranslatorVisitor::IMADSP_imm(u64) {
562 ThrowNotImplemented(Opcode::IMADSP_imm);
563}
564
565void TranslatorVisitor::IMNMX_reg(u64) {
566 ThrowNotImplemented(Opcode::IMNMX_reg);
567}
568
569void TranslatorVisitor::IMNMX_cbuf(u64) {
570 ThrowNotImplemented(Opcode::IMNMX_cbuf);
571}
572
573void TranslatorVisitor::IMNMX_imm(u64) {
574 ThrowNotImplemented(Opcode::IMNMX_imm);
575}
576
577void TranslatorVisitor::IMUL_reg(u64) {
578 ThrowNotImplemented(Opcode::IMUL_reg);
579}
580
581void TranslatorVisitor::IMUL_cbuf(u64) {
582 ThrowNotImplemented(Opcode::IMUL_cbuf);
583}
584
585void TranslatorVisitor::IMUL_imm(u64) {
586 ThrowNotImplemented(Opcode::IMUL_imm);
587}
588
589void TranslatorVisitor::IMUL32I(u64) {
590 ThrowNotImplemented(Opcode::IMUL32I);
591}
592
593void TranslatorVisitor::ISBERD(u64) {
594 ThrowNotImplemented(Opcode::ISBERD);
595}
596
597void TranslatorVisitor::ISCADD_reg(u64) {
598 ThrowNotImplemented(Opcode::ISCADD_reg);
599}
600
601void TranslatorVisitor::ISCADD_cbuf(u64) {
602 ThrowNotImplemented(Opcode::ISCADD_cbuf);
603}
604
605void TranslatorVisitor::ISCADD_imm(u64) {
606 ThrowNotImplemented(Opcode::ISCADD_imm);
607}
608
609void TranslatorVisitor::ISCADD32I(u64) {
610 ThrowNotImplemented(Opcode::ISCADD32I);
611}
612
613void TranslatorVisitor::ISET_reg(u64) {
614 ThrowNotImplemented(Opcode::ISET_reg);
615}
616
617void TranslatorVisitor::ISET_cbuf(u64) {
618 ThrowNotImplemented(Opcode::ISET_cbuf);
619}
620
621void TranslatorVisitor::ISET_imm(u64) {
622 ThrowNotImplemented(Opcode::ISET_imm);
623}
624
625void TranslatorVisitor::ISETP_reg(u64) {
626 ThrowNotImplemented(Opcode::ISETP_reg);
627}
628
629void TranslatorVisitor::ISETP_cbuf(u64) {
630 ThrowNotImplemented(Opcode::ISETP_cbuf);
631}
632
633void TranslatorVisitor::ISETP_imm(u64) {
634 ThrowNotImplemented(Opcode::ISETP_imm);
635}
636
637void TranslatorVisitor::JCAL(u64) {
638 ThrowNotImplemented(Opcode::JCAL);
639}
640
641void TranslatorVisitor::JMP(u64) {
642 ThrowNotImplemented(Opcode::JMP);
643}
644
645void TranslatorVisitor::JMX(u64) {
646 ThrowNotImplemented(Opcode::JMX);
647}
648
649void TranslatorVisitor::KIL(u64) {
650 ThrowNotImplemented(Opcode::KIL);
651}
652
653void TranslatorVisitor::LD(u64) {
654 ThrowNotImplemented(Opcode::LD);
655}
656
657void TranslatorVisitor::LDC(u64) {
658 ThrowNotImplemented(Opcode::LDC);
659}
660
661void TranslatorVisitor::LDG(u64) {
662 ThrowNotImplemented(Opcode::LDG);
663}
664
665void TranslatorVisitor::LDL(u64) {
666 ThrowNotImplemented(Opcode::LDL);
667}
668
669void TranslatorVisitor::LDS(u64) {
670 ThrowNotImplemented(Opcode::LDS);
671}
672
673void TranslatorVisitor::LEA_hi_reg(u64) {
674 ThrowNotImplemented(Opcode::LEA_hi_reg);
675}
676
677void TranslatorVisitor::LEA_hi_cbuf(u64) {
678 ThrowNotImplemented(Opcode::LEA_hi_cbuf);
679}
680
681void TranslatorVisitor::LEA_lo_reg(u64) {
682 ThrowNotImplemented(Opcode::LEA_lo_reg);
683}
684
685void TranslatorVisitor::LEA_lo_cbuf(u64) {
686 ThrowNotImplemented(Opcode::LEA_lo_cbuf);
687}
688
689void TranslatorVisitor::LEA_lo_imm(u64) {
690 ThrowNotImplemented(Opcode::LEA_lo_imm);
691}
692
693void TranslatorVisitor::LEPC(u64) {
694 ThrowNotImplemented(Opcode::LEPC);
695}
696
697void TranslatorVisitor::LONGJMP(u64) {
698 ThrowNotImplemented(Opcode::LONGJMP);
699}
700
701void TranslatorVisitor::LOP_reg(u64) {
702 ThrowNotImplemented(Opcode::LOP_reg);
703}
704
705void TranslatorVisitor::LOP_cbuf(u64) {
706 ThrowNotImplemented(Opcode::LOP_cbuf);
707}
708
709void TranslatorVisitor::LOP_imm(u64) {
710 ThrowNotImplemented(Opcode::LOP_imm);
711}
712
713void TranslatorVisitor::LOP3_reg(u64) {
714 ThrowNotImplemented(Opcode::LOP3_reg);
715}
716
717void TranslatorVisitor::LOP3_cbuf(u64) {
718 ThrowNotImplemented(Opcode::LOP3_cbuf);
719}
720
721void TranslatorVisitor::LOP3_imm(u64) {
722 ThrowNotImplemented(Opcode::LOP3_imm);
723}
724
725void TranslatorVisitor::LOP32I(u64) {
726 ThrowNotImplemented(Opcode::LOP32I);
727}
728
729void TranslatorVisitor::MEMBAR(u64) {
730 ThrowNotImplemented(Opcode::MEMBAR);
731}
732
733void TranslatorVisitor::MOV32I(u64) {
734 ThrowNotImplemented(Opcode::MOV32I);
735}
736
737void TranslatorVisitor::NOP(u64) {
738 ThrowNotImplemented(Opcode::NOP);
739}
740
741void TranslatorVisitor::OUT_reg(u64) {
742 ThrowNotImplemented(Opcode::OUT_reg);
743}
744
745void TranslatorVisitor::OUT_cbuf(u64) {
746 ThrowNotImplemented(Opcode::OUT_cbuf);
747}
748
749void TranslatorVisitor::OUT_imm(u64) {
750 ThrowNotImplemented(Opcode::OUT_imm);
751}
752
753void TranslatorVisitor::P2R_reg(u64) {
754 ThrowNotImplemented(Opcode::P2R_reg);
755}
756
757void TranslatorVisitor::P2R_cbuf(u64) {
758 ThrowNotImplemented(Opcode::P2R_cbuf);
759}
760
761void TranslatorVisitor::P2R_imm(u64) {
762 ThrowNotImplemented(Opcode::P2R_imm);
763}
764
765void TranslatorVisitor::PBK(u64) {
766 // PBK is a no-op
767}
768
769void TranslatorVisitor::PCNT(u64) {
770 ThrowNotImplemented(Opcode::PCNT);
771}
772
773void TranslatorVisitor::PEXIT(u64) {
774 ThrowNotImplemented(Opcode::PEXIT);
775}
776
777void TranslatorVisitor::PIXLD(u64) {
778 ThrowNotImplemented(Opcode::PIXLD);
779}
780
781void TranslatorVisitor::PLONGJMP(u64) {
782 ThrowNotImplemented(Opcode::PLONGJMP);
783}
784
785void TranslatorVisitor::POPC_reg(u64) {
786 ThrowNotImplemented(Opcode::POPC_reg);
787}
788
789void TranslatorVisitor::POPC_cbuf(u64) {
790 ThrowNotImplemented(Opcode::POPC_cbuf);
791}
792
793void TranslatorVisitor::POPC_imm(u64) {
794 ThrowNotImplemented(Opcode::POPC_imm);
795}
796
797void TranslatorVisitor::PRET(u64) {
798 ThrowNotImplemented(Opcode::PRET);
799}
800
801void TranslatorVisitor::PRMT_reg(u64) {
802 ThrowNotImplemented(Opcode::PRMT_reg);
803}
804
805void TranslatorVisitor::PRMT_rc(u64) {
806 ThrowNotImplemented(Opcode::PRMT_rc);
807}
808
809void TranslatorVisitor::PRMT_cr(u64) {
810 ThrowNotImplemented(Opcode::PRMT_cr);
811}
812
813void TranslatorVisitor::PRMT_imm(u64) {
814 ThrowNotImplemented(Opcode::PRMT_imm);
815}
816
817void TranslatorVisitor::PSET(u64) {
818 ThrowNotImplemented(Opcode::PSET);
819}
820
821void TranslatorVisitor::PSETP(u64) {
822 ThrowNotImplemented(Opcode::PSETP);
823}
824
825void TranslatorVisitor::R2B(u64) {
826 ThrowNotImplemented(Opcode::R2B);
827}
828
829void TranslatorVisitor::R2P_reg(u64) {
830 ThrowNotImplemented(Opcode::R2P_reg);
831}
832
833void TranslatorVisitor::R2P_cbuf(u64) {
834 ThrowNotImplemented(Opcode::R2P_cbuf);
835}
836
837void TranslatorVisitor::R2P_imm(u64) {
838 ThrowNotImplemented(Opcode::R2P_imm);
839}
840
841void TranslatorVisitor::RAM(u64) {
842 ThrowNotImplemented(Opcode::RAM);
843}
844
845void TranslatorVisitor::RED(u64) {
846 ThrowNotImplemented(Opcode::RED);
847}
848
849void TranslatorVisitor::RET(u64) {
850 ThrowNotImplemented(Opcode::RET);
851}
852
853void TranslatorVisitor::RRO_reg(u64) {
854 ThrowNotImplemented(Opcode::RRO_reg);
855}
856
857void TranslatorVisitor::RRO_cbuf(u64) {
858 ThrowNotImplemented(Opcode::RRO_cbuf);
859}
860
861void TranslatorVisitor::RRO_imm(u64) {
862 ThrowNotImplemented(Opcode::RRO_imm);
863}
864
865void TranslatorVisitor::RTT(u64) {
866 ThrowNotImplemented(Opcode::RTT);
867}
868
869void TranslatorVisitor::S2R(u64) {
870 ThrowNotImplemented(Opcode::S2R);
871}
872
873void TranslatorVisitor::SAM(u64) {
874 ThrowNotImplemented(Opcode::SAM);
875}
876
877void TranslatorVisitor::SEL_reg(u64) {
878 ThrowNotImplemented(Opcode::SEL_reg);
879}
880
881void TranslatorVisitor::SEL_cbuf(u64) {
882 ThrowNotImplemented(Opcode::SEL_cbuf);
883}
884
885void TranslatorVisitor::SEL_imm(u64) {
886 ThrowNotImplemented(Opcode::SEL_imm);
887}
888
889void TranslatorVisitor::SETCRSPTR(u64) {
890 ThrowNotImplemented(Opcode::SETCRSPTR);
891}
892
893void TranslatorVisitor::SETLMEMBASE(u64) {
894 ThrowNotImplemented(Opcode::SETLMEMBASE);
895}
896
897void TranslatorVisitor::SHF_l_reg(u64) {
898 ThrowNotImplemented(Opcode::SHF_l_reg);
899}
900
901void TranslatorVisitor::SHF_l_imm(u64) {
902 ThrowNotImplemented(Opcode::SHF_l_imm);
903}
904
905void TranslatorVisitor::SHF_r_reg(u64) {
906 ThrowNotImplemented(Opcode::SHF_r_reg);
907}
908
909void TranslatorVisitor::SHF_r_imm(u64) {
910 ThrowNotImplemented(Opcode::SHF_r_imm);
911}
912
913void TranslatorVisitor::SHFL(u64) {
914 ThrowNotImplemented(Opcode::SHFL);
915}
916
917void TranslatorVisitor::SHL_reg(u64) {
918 ThrowNotImplemented(Opcode::SHL_reg);
919}
920
921void TranslatorVisitor::SHL_cbuf(u64) {
922 ThrowNotImplemented(Opcode::SHL_cbuf);
923}
924
925void TranslatorVisitor::SHL_imm(u64) {
926 ThrowNotImplemented(Opcode::SHL_imm);
927}
928
929void TranslatorVisitor::SHR_reg(u64) {
930 ThrowNotImplemented(Opcode::SHR_reg);
931}
932
933void TranslatorVisitor::SHR_cbuf(u64) {
934 ThrowNotImplemented(Opcode::SHR_cbuf);
935}
936
937void TranslatorVisitor::SHR_imm(u64) {
938 ThrowNotImplemented(Opcode::SHR_imm);
939}
940
941void TranslatorVisitor::SSY(u64) {
942 ThrowNotImplemented(Opcode::SSY);
943}
944
945void TranslatorVisitor::ST(u64) {
946 ThrowNotImplemented(Opcode::ST);
947}
948
949void TranslatorVisitor::STL(u64) {
950 ThrowNotImplemented(Opcode::STL);
951}
952
953void TranslatorVisitor::STP(u64) {
954 ThrowNotImplemented(Opcode::STP);
955}
956
957void TranslatorVisitor::STS(u64) {
958 ThrowNotImplemented(Opcode::STS);
959}
960
961void TranslatorVisitor::SUATOM_cas(u64) {
962 ThrowNotImplemented(Opcode::SUATOM_cas);
963}
964
965void TranslatorVisitor::SULD(u64) {
966 ThrowNotImplemented(Opcode::SULD);
967}
968
969void TranslatorVisitor::SURED(u64) {
970 ThrowNotImplemented(Opcode::SURED);
971}
972
973void TranslatorVisitor::SUST(u64) {
974 ThrowNotImplemented(Opcode::SUST);
975}
976
977void TranslatorVisitor::SYNC(u64) {
978 ThrowNotImplemented(Opcode::SYNC);
979}
980
981void TranslatorVisitor::TEX(u64) {
982 ThrowNotImplemented(Opcode::TEX);
983}
984
985void TranslatorVisitor::TEX_b(u64) {
986 ThrowNotImplemented(Opcode::TEX_b);
987}
988
989void TranslatorVisitor::TEXS(u64) {
990 ThrowNotImplemented(Opcode::TEXS);
991}
992
993void TranslatorVisitor::TLD(u64) {
994 ThrowNotImplemented(Opcode::TLD);
995}
996
997void TranslatorVisitor::TLD_b(u64) {
998 ThrowNotImplemented(Opcode::TLD_b);
999}
1000
1001void TranslatorVisitor::TLD4(u64) {
1002 ThrowNotImplemented(Opcode::TLD4);
1003}
1004
1005void TranslatorVisitor::TLD4_b(u64) {
1006 ThrowNotImplemented(Opcode::TLD4_b);
1007}
1008
1009void TranslatorVisitor::TLD4S(u64) {
1010 ThrowNotImplemented(Opcode::TLD4S);
1011}
1012
1013void TranslatorVisitor::TLDS(u64) {
1014 ThrowNotImplemented(Opcode::TLDS);
1015}
1016
1017void TranslatorVisitor::TMML(u64) {
1018 ThrowNotImplemented(Opcode::TMML);
1019}
1020
1021void TranslatorVisitor::TMML_b(u64) {
1022 ThrowNotImplemented(Opcode::TMML_b);
1023}
1024
1025void TranslatorVisitor::TXA(u64) {
1026 ThrowNotImplemented(Opcode::TXA);
1027}
1028
1029void TranslatorVisitor::TXD(u64) {
1030 ThrowNotImplemented(Opcode::TXD);
1031}
1032
1033void TranslatorVisitor::TXD_b(u64) {
1034 ThrowNotImplemented(Opcode::TXD_b);
1035}
1036
1037void TranslatorVisitor::TXQ(u64) {
1038 ThrowNotImplemented(Opcode::TXQ);
1039}
1040
1041void TranslatorVisitor::TXQ_b(u64) {
1042 ThrowNotImplemented(Opcode::TXQ_b);
1043}
1044
1045void TranslatorVisitor::VABSDIFF(u64) {
1046 ThrowNotImplemented(Opcode::VABSDIFF);
1047}
1048
1049void TranslatorVisitor::VABSDIFF4(u64) {
1050 ThrowNotImplemented(Opcode::VABSDIFF4);
1051}
1052
1053void TranslatorVisitor::VADD(u64) {
1054 ThrowNotImplemented(Opcode::VADD);
1055}
1056
1057void TranslatorVisitor::VMAD(u64) {
1058 ThrowNotImplemented(Opcode::VMAD);
1059}
1060
1061void TranslatorVisitor::VMNMX(u64) {
1062 ThrowNotImplemented(Opcode::VMNMX);
1063}
1064
1065void TranslatorVisitor::VOTE(u64) {
1066 ThrowNotImplemented(Opcode::VOTE);
1067}
1068
1069void TranslatorVisitor::VOTE_vtg(u64) {
1070 ThrowNotImplemented(Opcode::VOTE_vtg);
1071}
1072
1073void TranslatorVisitor::VSET(u64) {
1074 ThrowNotImplemented(Opcode::VSET);
1075}
1076
1077void TranslatorVisitor::VSETP(u64) {
1078 ThrowNotImplemented(Opcode::VSETP);
1079}
1080
1081void TranslatorVisitor::VSHL(u64) {
1082 ThrowNotImplemented(Opcode::VSHL);
1083}
1084
1085void TranslatorVisitor::VSHR(u64) {
1086 ThrowNotImplemented(Opcode::VSHR);
1087}
1088
1089void TranslatorVisitor::XMAD_reg(u64) {
1090 ThrowNotImplemented(Opcode::XMAD_reg);
1091}
1092
1093void TranslatorVisitor::XMAD_rc(u64) {
1094 ThrowNotImplemented(Opcode::XMAD_rc);
1095}
1096
1097void TranslatorVisitor::XMAD_cr(u64) {
1098 ThrowNotImplemented(Opcode::XMAD_cr);
1099}
1100
1101void TranslatorVisitor::XMAD_imm(u64) {
1102 ThrowNotImplemented(Opcode::XMAD_imm);
1103}
1104
1105} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp
new file mode 100644
index 000000000..7fa35ba3a
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/impl/register_move.cpp
@@ -0,0 +1,45 @@
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_field.h"
6#include "common/common_types.h"
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/maxwell/opcode.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10
11namespace Shader::Maxwell {
12namespace {
13union MOV {
14 u64 raw;
15 BitField<0, 8, IR::Reg> dest_reg;
16 BitField<20, 8, IR::Reg> src_reg;
17 BitField<39, 4, u64> mask;
18};
19
20void CheckMask(MOV mov) {
21 if (mov.mask != 0xf) {
22 throw NotImplementedException("Non-full move mask");
23 }
24}
25} // Anonymous namespace
26
27void TranslatorVisitor::MOV_reg(u64 insn) {
28 const MOV mov{insn};
29 CheckMask(mov);
30 X(mov.dest_reg, X(mov.src_reg));
31}
32
33void TranslatorVisitor::MOV_cbuf(u64 insn) {
34 const MOV mov{insn};
35 CheckMask(mov);
36 X(mov.dest_reg, GetCbuf(insn));
37}
38
39void TranslatorVisitor::MOV_imm(u64 insn) {
40 const MOV mov{insn};
41 CheckMask(mov);
42 X(mov.dest_reg, GetImm(insn));
43}
44
45} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/translate.cpp b/src/shader_recompiler/frontend/maxwell/translate/translate.cpp
new file mode 100644
index 000000000..66a306745
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/translate.cpp
@@ -0,0 +1,50 @@
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/environment.h"
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/maxwell/decode.h"
8#include "shader_recompiler/frontend/maxwell/location.h"
9#include "shader_recompiler/frontend/maxwell/translate/impl/impl.h"
10#include "shader_recompiler/frontend/maxwell/translate/translate.h"
11
12namespace Shader::Maxwell {
13
14template <auto visitor_method>
15static void Invoke(TranslatorVisitor& visitor, Location pc, u64 insn) {
16 using MethodType = decltype(visitor_method);
17 if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, Location, u64>) {
18 (visitor.*visitor_method)(pc, insn);
19 } else if constexpr (std::is_invocable_r_v<void, MethodType, TranslatorVisitor&, u64>) {
20 (visitor.*visitor_method)(insn);
21 } else {
22 (visitor.*visitor_method)();
23 }
24}
25
26IR::Block Translate(Environment& env, const Flow::Block& flow_block) {
27 IR::Block block{flow_block.begin.Offset(), flow_block.end.Offset()};
28 TranslatorVisitor visitor{env, block};
29
30 const Location pc_end{flow_block.end};
31 Location pc{flow_block.begin};
32 while (pc != pc_end) {
33 const u64 insn{env.ReadInstruction(pc.Offset())};
34 const Opcode opcode{Decode(insn)};
35 switch (opcode) {
36#define INST(name, cute, mask) \
37 case Opcode::name: \
38 Invoke<&TranslatorVisitor::name>(visitor, pc, insn); \
39 break;
40#include "shader_recompiler/frontend/maxwell/maxwell.inc"
41#undef OPCODE
42 default:
43 throw LogicError("Invalid opcode {}", opcode);
44 }
45 ++pc;
46 }
47 return block;
48}
49
50} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate/translate.h b/src/shader_recompiler/frontend/maxwell/translate/translate.h
new file mode 100644
index 000000000..788742dea
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/translate/translate.h
@@ -0,0 +1,16 @@
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/environment.h"
8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/maxwell/location.h"
10#include "shader_recompiler/frontend/maxwell/control_flow.h"
11
12namespace Shader::Maxwell {
13
14[[nodiscard]] IR::Block Translate(Environment& env, const Flow::Block& flow_block);
15
16} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
new file mode 100644
index 000000000..bbaa412f6
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp
@@ -0,0 +1,23 @@
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 <ranges>
6
7#include "shader_recompiler/frontend/ir/basic_block.h"
8#include "shader_recompiler/frontend/ir/microinstruction.h"
9#include "shader_recompiler/ir_opt/passes.h"
10
11namespace Shader::Optimization {
12
13void DeadCodeEliminationPass(IR::Block& block) {
14 // We iterate over the instructions in reverse order.
15 // This is because removing an instruction reduces the number of uses for earlier instructions.
16 for (IR::Inst& inst : std::views::reverse(block)) {
17 if (!inst.HasUses() && !inst.MayHaveSideEffects()) {
18 inst.Invalidate();
19 }
20 }
21}
22
23} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp b/src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
new file mode 100644
index 000000000..21b8526cd
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp
@@ -0,0 +1,87 @@
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
7#include "shader_recompiler/frontend/ir/basic_block.h"
8#include "shader_recompiler/frontend/ir/microinstruction.h"
9#include "shader_recompiler/ir_opt/passes.h"
10
11namespace Shader::Optimization {
12namespace {
13using Iterator = IR::Block::iterator;
14
15enum class TrackingType {
16 Reg,
17};
18
19struct RegisterInfo {
20 IR::Value register_value;
21 TrackingType tracking_type;
22 Iterator last_set_instruction;
23 bool set_instruction_present = false;
24};
25
26void DoSet(IR::Block& block, RegisterInfo& info, IR::Value value, Iterator set_inst,
27 TrackingType tracking_type) {
28 if (info.set_instruction_present) {
29 info.last_set_instruction->Invalidate();
30 block.Instructions().erase(info.last_set_instruction);
31 }
32 info.register_value = value;
33 info.tracking_type = tracking_type;
34 info.set_instruction_present = true;
35 info.last_set_instruction = set_inst;
36}
37
38RegisterInfo Nothing(Iterator get_inst, TrackingType tracking_type) {
39 RegisterInfo info{};
40 info.register_value = IR::Value{&*get_inst};
41 info.tracking_type = tracking_type;
42 return info;
43}
44
45void DoGet(RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) {
46 if (info.register_value.IsEmpty()) {
47 info = Nothing(get_inst, tracking_type);
48 return;
49 }
50 if (info.tracking_type == tracking_type) {
51 get_inst->ReplaceUsesWith(info.register_value);
52 return;
53 }
54 info = Nothing(get_inst, tracking_type);
55}
56} // Anonymous namespace
57
58void GetSetElimination(IR::Block& block) {
59 std::array<RegisterInfo, 255> reg_info;
60
61 for (Iterator inst = block.begin(); inst != block.end(); ++inst) {
62 switch (inst->Opcode()) {
63 case IR::Opcode::GetRegister: {
64 const IR::Reg reg{inst->Arg(0).Reg()};
65 if (reg == IR::Reg::RZ) {
66 break;
67 }
68 const size_t index{static_cast<size_t>(reg)};
69 DoGet(reg_info.at(index), inst, TrackingType::Reg);
70 break;
71 }
72 case IR::Opcode::SetRegister: {
73 const IR::Reg reg{inst->Arg(0).Reg()};
74 if (reg == IR::Reg::RZ) {
75 break;
76 }
77 const size_t index{static_cast<size_t>(reg)};
78 DoSet(block, reg_info.at(index), inst->Arg(1), inst, TrackingType::Reg);
79 break;
80 }
81 default:
82 break;
83 }
84 }
85}
86
87} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp
new file mode 100644
index 000000000..f9bb063fb
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp
@@ -0,0 +1,37 @@
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 <vector>
6
7#include "shader_recompiler/frontend/ir/basic_block.h"
8#include "shader_recompiler/frontend/ir/microinstruction.h"
9#include "shader_recompiler/ir_opt/passes.h"
10
11namespace Shader::Optimization {
12
13void IdentityRemovalPass(IR::Block& block) {
14 std::vector<IR::Inst*> to_invalidate;
15
16 for (auto inst = block.begin(); inst != block.end();) {
17 const size_t num_args{inst->NumArgs()};
18 for (size_t i = 0; i < num_args; ++i) {
19 IR::Value arg;
20 while ((arg = inst->Arg(i)).IsIdentity()) {
21 inst->SetArg(i, arg.Inst()->Arg(0));
22 }
23 }
24 if (inst->Opcode() == IR::Opcode::Identity || inst->Opcode() == IR::Opcode::Void) {
25 to_invalidate.push_back(&*inst);
26 inst = block.Instructions().erase(inst);
27 } else {
28 ++inst;
29 }
30 }
31
32 for (IR::Inst* const inst : to_invalidate) {
33 inst->Invalidate();
34 }
35}
36
37} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
new file mode 100644
index 000000000..fe5454e9a
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -0,0 +1,16 @@
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/basic_block.h"
8
9namespace Shader::Optimization {
10
11void DeadCodeEliminationPass(IR::Block& block);
12void GetSetElimination(IR::Block& block);
13void IdentityRemovalPass(IR::Block& block);
14void VerificationPass(const IR::Block& block);
15
16} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/verification_pass.cpp b/src/shader_recompiler/ir_opt/verification_pass.cpp
new file mode 100644
index 000000000..36d9ae39b
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/verification_pass.cpp
@@ -0,0 +1,50 @@
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 <map>
6
7#include "shader_recompiler/exception.h"
8#include "shader_recompiler/frontend/ir/basic_block.h"
9#include "shader_recompiler/frontend/ir/microinstruction.h"
10#include "shader_recompiler/ir_opt/passes.h"
11
12namespace Shader::Optimization {
13
14static void ValidateTypes(const IR::Block& block) {
15 for (const IR::Inst& inst : block) {
16 const size_t num_args{inst.NumArgs()};
17 for (size_t i = 0; i < num_args; ++i) {
18 const IR::Type t1{inst.Arg(i).Type()};
19 const IR::Type t2{IR::ArgTypeOf(inst.Opcode(), i)};
20 if (!IR::AreTypesCompatible(t1, t2)) {
21 throw LogicError("Invalid types in block:\n{}", IR::DumpBlock(block));
22 }
23 }
24 }
25}
26
27static void ValidateUses(const IR::Block& block) {
28 std::map<IR::Inst*, int> actual_uses;
29 for (const IR::Inst& inst : block) {
30 const size_t num_args{inst.NumArgs()};
31 for (size_t i = 0; i < num_args; ++i) {
32 const IR::Value arg{inst.Arg(i)};
33 if (!arg.IsImmediate()) {
34 ++actual_uses[arg.Inst()];
35 }
36 }
37 }
38 for (const auto [inst, uses] : actual_uses) {
39 if (inst->UseCount() != uses) {
40 throw LogicError("Invalid uses in block:\n{}", IR::DumpBlock(block));
41 }
42 }
43}
44
45void VerificationPass(const IR::Block& block) {
46 ValidateTypes(block);
47 ValidateUses(block);
48}
49
50} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/main.cpp b/src/shader_recompiler/main.cpp
new file mode 100644
index 000000000..39f0bf333
--- /dev/null
+++ b/src/shader_recompiler/main.cpp
@@ -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#include <filesystem>
6
7#include <fmt/format.h>
8
9#include "shader_recompiler/file_environment.h"
10#include "shader_recompiler/frontend/ir/basic_block.h"
11#include "shader_recompiler/frontend/ir/ir_emitter.h"
12#include "shader_recompiler/frontend/maxwell/control_flow.h"
13#include "shader_recompiler/frontend/maxwell/decode.h"
14#include "shader_recompiler/frontend/maxwell/location.h"
15#include "shader_recompiler/frontend/maxwell/program.h"
16#include "shader_recompiler/frontend/maxwell/translate/translate.h"
17
18using namespace Shader;
19using namespace Shader::Maxwell;
20
21template <typename Func>
22static void ForEachFile(const std::filesystem::path& path, Func&& func) {
23 std::filesystem::directory_iterator end;
24 for (std::filesystem::directory_iterator it{path}; it != end; ++it) {
25 if (std::filesystem::is_directory(*it)) {
26 ForEachFile(*it, func);
27 } else {
28 func(*it);
29 }
30 }
31}
32
33void RunDatabase() {
34 std::vector<std::unique_ptr<FileEnvironment>> map;
35 ForEachFile("D:\\Shaders\\Database", [&](const std::filesystem::path& path) {
36 map.emplace_back(std::make_unique<FileEnvironment>(path.string().c_str()));
37 });
38 for (int i = 0; i < 1; ++i) {
39 for (auto& env : map) {
40 // fmt::print(stdout, "Decoding {}\n", path.string());
41 const Location start_address{0};
42 auto cfg{std::make_unique<Flow::CFG>(*env, start_address)};
43 // fmt::print(stdout, "{}\n", cfg.Dot());
44 // IR::Program program{env, cfg};
45 // Optimize(program);
46 // const std::string code{EmitGLASM(program)};
47 }
48 }
49}
50
51int main() {
52 // RunDatabase();
53
54 FileEnvironment env{"D:\\Shaders\\Database\\test.bin"};
55 auto cfg{std::make_unique<Flow::CFG>(env, 0)};
56 // fmt::print(stdout, "{}\n", cfg->Dot());
57
58 Program program{env, *cfg};
59 fmt::print(stdout, "{}\n", DumpProgram(program));
60}