summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2018-04-20 20:49:05 -0400
committerGravatar bunnei2018-04-20 22:30:56 -0400
commit9f6d305eabd6534cdb80a20f611f92977d3953f0 (patch)
treefa05a530ab374c72d1ec08011ac49827ea5618e3
parentMerge pull request #369 from Subv/shader_instr2 (diff)
downloadyuzu-9f6d305eabd6534cdb80a20f611f92977d3953f0.tar.gz
yuzu-9f6d305eabd6534cdb80a20f611f92977d3953f0.tar.xz
yuzu-9f6d305eabd6534cdb80a20f611f92977d3953f0.zip
shader_bytecode: Decode instructions based on bit strings.
-rw-r--r--src/video_core/engines/shader_bytecode.h357
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp49
2 files changed, 201 insertions, 205 deletions
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e6c2fd367..f4dfef76a 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -4,10 +4,16 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <bitset>
7#include <cstring> 8#include <cstring>
8#include <map> 9#include <map>
9#include <string> 10#include <string>
11#include <vector>
12
13#include <boost/optional.hpp>
14
10#include "common/bit_field.h" 15#include "common/bit_field.h"
16#include "common/common_types.h"
11 17
12namespace Tegra { 18namespace Tegra {
13namespace Shader { 19namespace Shader {
@@ -89,188 +95,12 @@ union Uniform {
89 BitField<34, 5, u64> index; 95 BitField<34, 5, u64> index;
90}; 96};
91 97
92union OpCode {
93 enum class Id : u64 {
94 TEXS = 0x6C,
95 IPA = 0xE0,
96 FMUL32_IMM = 0x1E,
97 FFMA_IMM = 0x65,
98 FFMA_CR = 0x93,
99 FFMA_RC = 0xA3,
100 FFMA_RR = 0xB3,
101
102 FADD_C = 0x98B,
103 FMUL_C = 0x98D,
104 MUFU = 0xA10,
105 FADD_R = 0xB8B,
106 FMUL_R = 0xB8D,
107 LD_A = 0x1DFB,
108 ST_A = 0x1DFE,
109
110 FSETP_R = 0x5BB,
111 FSETP_C = 0x4BB,
112 FSETP_IMM = 0x36B,
113 FSETP_NEG_IMM = 0x37B,
114 EXIT = 0xE30,
115 KIL = 0xE33,
116
117 FMUL_IMM = 0x70D,
118 FMUL_IMM_x = 0x72D,
119 FADD_IMM = 0x70B,
120 FADD_IMM_x = 0x72B,
121 };
122
123 enum class Type {
124 Trivial,
125 Arithmetic,
126 Ffma,
127 Flow,
128 Memory,
129 FloatPredicate,
130 Unknown,
131 };
132
133 struct Info {
134 Type type;
135 std::string name;
136 };
137
138 OpCode() = default;
139
140 constexpr OpCode(Id value) : value(static_cast<u64>(value)) {}
141
142 constexpr OpCode(u64 value) : value{value} {}
143
144 constexpr Id EffectiveOpCode() const {
145 switch (op1) {
146 case Id::TEXS:
147 return op1;
148 }
149
150 switch (op2) {
151 case Id::IPA:
152 case Id::FMUL32_IMM:
153 return op2;
154 }
155
156 switch (op3) {
157 case Id::FFMA_IMM:
158 case Id::FFMA_CR:
159 case Id::FFMA_RC:
160 case Id::FFMA_RR:
161 return op3;
162 }
163
164 switch (op4) {
165 case Id::EXIT:
166 case Id::FSETP_R:
167 case Id::FSETP_C:
168 case Id::KIL:
169 return op4;
170 case Id::FSETP_IMM:
171 case Id::FSETP_NEG_IMM:
172 return Id::FSETP_IMM;
173 }
174
175 switch (op5) {
176 case Id::MUFU:
177 case Id::LD_A:
178 case Id::ST_A:
179 case Id::FADD_R:
180 case Id::FADD_C:
181 case Id::FMUL_R:
182 case Id::FMUL_C:
183 return op5;
184
185 case Id::FMUL_IMM:
186 case Id::FMUL_IMM_x:
187 return Id::FMUL_IMM;
188
189 case Id::FADD_IMM:
190 case Id::FADD_IMM_x:
191 return Id::FADD_IMM;
192 }
193
194 return static_cast<Id>(value);
195 }
196
197 static const Info& GetInfo(const OpCode& opcode) {
198 static const std::map<Id, Info> info_table{BuildInfoTable()};
199 const auto& search{info_table.find(opcode.EffectiveOpCode())};
200 if (search != info_table.end()) {
201 return search->second;
202 }
203
204 static const Info unknown{Type::Unknown, "UNK"};
205 return unknown;
206 }
207
208 constexpr operator Id() const {
209 return static_cast<Id>(value);
210 }
211
212 constexpr OpCode operator<<(size_t bits) const {
213 return value << bits;
214 }
215
216 constexpr OpCode operator>>(size_t bits) const {
217 return value >> bits;
218 }
219
220 template <typename T>
221 constexpr u64 operator-(const T& oth) const {
222 return value - oth;
223 }
224
225 constexpr u64 operator&(const OpCode& oth) const {
226 return value & oth.value;
227 }
228
229 constexpr u64 operator~() const {
230 return ~value;
231 }
232
233 static std::map<Id, Info> BuildInfoTable() {
234 std::map<Id, Info> info_table;
235 info_table[Id::TEXS] = {Type::Memory, "texs"};
236 info_table[Id::LD_A] = {Type::Memory, "ld_a"};
237 info_table[Id::ST_A] = {Type::Memory, "st_a"};
238 info_table[Id::MUFU] = {Type::Arithmetic, "mufu"};
239 info_table[Id::FFMA_IMM] = {Type::Ffma, "ffma_imm"};
240 info_table[Id::FFMA_CR] = {Type::Ffma, "ffma_cr"};
241 info_table[Id::FFMA_RC] = {Type::Ffma, "ffma_rc"};
242 info_table[Id::FFMA_RR] = {Type::Ffma, "ffma_rr"};
243 info_table[Id::FADD_R] = {Type::Arithmetic, "fadd_r"};
244 info_table[Id::FADD_C] = {Type::Arithmetic, "fadd_c"};
245 info_table[Id::FADD_IMM] = {Type::Arithmetic, "fadd_imm"};
246 info_table[Id::FMUL_R] = {Type::Arithmetic, "fmul_r"};
247 info_table[Id::FMUL_C] = {Type::Arithmetic, "fmul_c"};
248 info_table[Id::FMUL_IMM] = {Type::Arithmetic, "fmul_imm"};
249 info_table[Id::FMUL32_IMM] = {Type::Arithmetic, "fmul32_imm"};
250 info_table[Id::FSETP_C] = {Type::FloatPredicate, "fsetp_c"};
251 info_table[Id::FSETP_R] = {Type::FloatPredicate, "fsetp_r"};
252 info_table[Id::FSETP_IMM] = {Type::FloatPredicate, "fsetp_imm"};
253 info_table[Id::EXIT] = {Type::Trivial, "exit"};
254 info_table[Id::IPA] = {Type::Trivial, "ipa"};
255 info_table[Id::KIL] = {Type::Flow, "kil"};
256 return info_table;
257 }
258
259 BitField<57, 7, Id> op1;
260 BitField<56, 8, Id> op2;
261 BitField<55, 9, Id> op3;
262 BitField<52, 12, Id> op4;
263 BitField<51, 13, Id> op5;
264 u64 value{};
265};
266static_assert(sizeof(OpCode) == 0x8, "Incorrect structure size");
267
268} // namespace Shader 98} // namespace Shader
269} // namespace Tegra 99} // namespace Tegra
270 100
271namespace std { 101namespace std {
272 102
273// TODO(bunne): The below is forbidden by the C++ standard, but works fine. See #330. 103// TODO(bunnei): The below is forbidden by the C++ standard, but works fine. See #330.
274template <> 104template <>
275struct make_unsigned<Tegra::Shader::Attribute> { 105struct make_unsigned<Tegra::Shader::Attribute> {
276 using type = Tegra::Shader::Attribute; 106 using type = Tegra::Shader::Attribute;
@@ -281,11 +111,6 @@ struct make_unsigned<Tegra::Shader::Register> {
281 using type = Tegra::Shader::Register; 111 using type = Tegra::Shader::Register;
282}; 112};
283 113
284template <>
285struct make_unsigned<Tegra::Shader::OpCode> {
286 using type = Tegra::Shader::OpCode;
287};
288
289} // namespace std 114} // namespace std
290 115
291namespace Tegra { 116namespace Tegra {
@@ -324,11 +149,12 @@ enum class SubOp : u64 {
324 149
325union Instruction { 150union Instruction {
326 Instruction& operator=(const Instruction& instr) { 151 Instruction& operator=(const Instruction& instr) {
327 hex = instr.hex; 152 value = instr.value;
328 return *this; 153 return *this;
329 } 154 }
330 155
331 OpCode opcode; 156 constexpr Instruction(u64 value) : value{value} {}
157
332 BitField<0, 8, Register> gpr0; 158 BitField<0, 8, Register> gpr0;
333 BitField<8, 8, Register> gpr8; 159 BitField<8, 8, Register> gpr8;
334 union { 160 union {
@@ -340,6 +166,7 @@ union Instruction {
340 BitField<20, 7, SubOp> sub_op; 166 BitField<20, 7, SubOp> sub_op;
341 BitField<28, 8, Register> gpr28; 167 BitField<28, 8, Register> gpr28;
342 BitField<39, 8, Register> gpr39; 168 BitField<39, 8, Register> gpr39;
169 BitField<48, 16, u64> opcode;
343 170
344 union { 171 union {
345 BitField<20, 19, u64> imm20_19; 172 BitField<20, 19, u64> imm20_19;
@@ -395,11 +222,171 @@ union Instruction {
395 Uniform uniform; 222 Uniform uniform;
396 Sampler sampler; 223 Sampler sampler;
397 224
398 u64 hex; 225 u64 value;
399}; 226};
400static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size"); 227static_assert(sizeof(Instruction) == 0x8, "Incorrect structure size");
401static_assert(std::is_standard_layout<Instruction>::value, 228static_assert(std::is_standard_layout<Instruction>::value,
402 "Structure does not have standard layout"); 229 "Structure does not have standard layout");
403 230
231class OpCode {
232public:
233 enum class Id {
234 KIL,
235 LD_A,
236 ST_A,
237 TEXS,
238 EXIT,
239 IPA,
240 FFMA_IMM,
241 FFMA_CR,
242 FFMA_RC,
243 FFMA_RR,
244 FADD_C,
245 FADD_R,
246 FADD_IMM,
247 FMUL_C,
248 FMUL_R,
249 FMUL_IMM,
250 FMUL32_IMM,
251 MUFU,
252 FSETP_R,
253 FSETP_C,
254 FSETP_IMM,
255 };
256
257 enum class Type {
258 Trivial,
259 Arithmetic,
260 Ffma,
261 Flow,
262 Memory,
263 FloatPredicate,
264 Unknown,
265 };
266
267 class Matcher {
268 public:
269 Matcher(const char* const name, u16 mask, u16 expected, OpCode::Id id, OpCode::Type type)
270 : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {}
271
272 const char* GetName() const {
273 return name;
274 }
275
276 u16 GetMask() const {
277 return mask;
278 }
279
280 Id GetId() const {
281 return id;
282 }
283
284 Type GetType() const {
285 return type;
286 }
287
288 /**
289 * Tests to see if the given instruction is the instruction this matcher represents.
290 * @param instruction The instruction to test
291 * @returns true if the given instruction matches.
292 */
293 bool Matches(u16 instruction) const {
294 return (instruction & mask) == expected;
295 }
296
297 private:
298 const char* name;
299 u16 mask;
300 u16 expected;
301 Id id;
302 Type type;
303 };
304
305 static boost::optional<const Matcher&> Decode(Instruction instr) {
306 static const auto table{GetDecodeTable()};
307
308 const auto matches_instruction = [instr](const auto& matcher) {
309 return matcher.Matches(static_cast<u16>(instr.opcode));
310 };
311
312 auto iter = std::find_if(table.begin(), table.end(), matches_instruction);
313 return iter != table.end() ? boost::optional<const Matcher&>(*iter) : boost::none;
314 }
315
316private:
317 struct Detail {
318 private:
319 static constexpr size_t opcode_bitsize = 16;
320
321 /**
322 * Generates the mask and the expected value after masking from a given bitstring.
323 * A '0' in a bitstring indicates that a zero must be present at that bit position.
324 * A '1' in a bitstring indicates that a one must be present at that bit position.
325 */
326 static auto GetMaskAndExpect(const char* const bitstring) {
327 u16 mask = 0, expect = 0;
328 for (size_t i = 0; i < opcode_bitsize; i++) {
329 const size_t bit_position = opcode_bitsize - i - 1;
330 switch (bitstring[i]) {
331 case '0':
332 mask |= 1 << bit_position;
333 break;
334 case '1':
335 expect |= 1 << bit_position;
336 mask |= 1 << bit_position;
337 break;
338 default:
339 // Ignore
340 break;
341 }
342 }
343 return std::make_tuple(mask, expect);
344 }
345
346 public:
347 /// Creates a matcher that can match and parse instructions based on bitstring.
348 static auto GetMatcher(const char* const bitstring, OpCode::Id op, OpCode::Type type,
349 const char* const name) {
350 const auto mask_expect = GetMaskAndExpect(bitstring);
351 return Matcher(name, std::get<0>(mask_expect), std::get<1>(mask_expect), op, type);
352 }
353 };
354
355 static std::vector<Matcher> GetDecodeTable() {
356 std::vector<Matcher> table = {
357#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
358 INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
359 INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
360 INST("1110111111110---", Id::ST_A, Type::Memory, "ST_A"),
361 INST("1101100---------", Id::TEXS, Type::Memory, "TEXS"),
362 INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
363 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
364 INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
365 INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
366 INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
367 INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"),
368 INST("0100110001011---", Id::FADD_C, Type::Arithmetic, "FADD_C"),
369 INST("0101110001011---", Id::FADD_R, Type::Arithmetic, "FADD_R"),
370 INST("0011100-01011---", Id::FADD_IMM, Type::Arithmetic, "FADD_IMM"),
371 INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
372 INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
373 INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
374 INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"),
375 INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
376 INST("010110111011----", Id::FSETP_R, Type::FloatPredicate, "FSETP_R"),
377 INST("010010111011----", Id::FSETP_C, Type::FloatPredicate, "FSETP_C"),
378 INST("0011011-1011----", Id::FSETP_IMM, Type::FloatPredicate, "FSETP_IMM"),
379 };
380#undef INST
381 std::stable_sort(table.begin(), table.end(), [](const auto& a, const auto& b) {
382 // If a matcher has more bits in its mask it is more specific, so it
383 // should come first.
384 return std::bitset<16>(a.GetMask()).count() > std::bitset<16>(b.GetMask()).count();
385 });
386
387 return table;
388 }
389};
390
404} // namespace Shader 391} // namespace Shader
405} // namespace Tegra 392} // namespace Tegra
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 2395945c3..5919b3c9e 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -97,11 +97,12 @@ private:
97 return exit_method; 97 return exit_method;
98 98
99 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) { 99 for (u32 offset = begin; offset != end && offset != PROGRAM_END; ++offset) {
100 const Instruction instr = {program_code[offset]}; 100 if (const auto opcode = OpCode::Decode({program_code[offset]})) {
101 switch (instr.opcode.EffectiveOpCode()) { 101 switch (opcode->GetId()) {
102 case OpCode::Id::EXIT: { 102 case OpCode::Id::EXIT: {
103 return exit_method = ExitMethod::AlwaysEnd; 103 return exit_method = ExitMethod::AlwaysEnd;
104 } 104 }
105 }
105 } 106 }
106 } 107 }
107 return exit_method = ExitMethod::AlwaysReturn; 108 return exit_method = ExitMethod::AlwaysReturn;
@@ -332,12 +333,20 @@ private:
332 */ 333 */
333 u32 CompileInstr(u32 offset) { 334 u32 CompileInstr(u32 offset) {
334 // Ignore sched instructions when generating code. 335 // Ignore sched instructions when generating code.
335 if (IsSchedInstruction(offset)) 336 if (IsSchedInstruction(offset)) {
336 return offset + 1; 337 return offset + 1;
338 }
337 339
338 const Instruction instr = {program_code[offset]}; 340 const Instruction instr = {program_code[offset]};
341 const auto opcode = OpCode::Decode(instr);
342
343 // Decoding failure
344 if (!opcode) {
345 NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", instr.value);
346 UNREACHABLE();
347 }
339 348
340 shader.AddLine("// " + std::to_string(offset) + ": " + OpCode::GetInfo(instr.opcode).name); 349 shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName());
341 350
342 using Tegra::Shader::Pred; 351 using Tegra::Shader::Pred;
343 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute, 352 ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -349,7 +358,7 @@ private:
349 ++shader.scope; 358 ++shader.scope;
350 } 359 }
351 360
352 switch (OpCode::GetInfo(instr.opcode).type) { 361 switch (opcode->GetType()) {
353 case OpCode::Type::Arithmetic: { 362 case OpCode::Type::Arithmetic: {
354 std::string dest = GetRegister(instr.gpr0); 363 std::string dest = GetRegister(instr.gpr0);
355 std::string op_a = instr.alu.negate_a ? "-" : ""; 364 std::string op_a = instr.alu.negate_a ? "-" : "";
@@ -374,7 +383,7 @@ private:
374 op_b = "abs(" + op_b + ")"; 383 op_b = "abs(" + op_b + ")";
375 } 384 }
376 385
377 switch (instr.opcode.EffectiveOpCode()) { 386 switch (opcode->GetId()) {
378 case OpCode::Id::FMUL_C: 387 case OpCode::Id::FMUL_C:
379 case OpCode::Id::FMUL_R: 388 case OpCode::Id::FMUL_R:
380 case OpCode::Id::FMUL_IMM: { 389 case OpCode::Id::FMUL_IMM: {
@@ -424,8 +433,8 @@ private:
424 } 433 }
425 default: { 434 default: {
426 NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}", 435 NGLOG_CRITICAL(HW_GPU, "Unhandled arithmetic instruction: {} ({}): {}",
427 static_cast<unsigned>(instr.opcode.EffectiveOpCode()), 436 static_cast<unsigned>(opcode->GetId()), opcode->GetName(),
428 OpCode::GetInfo(instr.opcode).name, instr.hex); 437 instr.value);
429 UNREACHABLE(); 438 UNREACHABLE();
430 } 439 }
431 } 440 }
@@ -437,7 +446,7 @@ private:
437 std::string op_b = instr.ffma.negate_b ? "-" : ""; 446 std::string op_b = instr.ffma.negate_b ? "-" : "";
438 std::string op_c = instr.ffma.negate_c ? "-" : ""; 447 std::string op_c = instr.ffma.negate_c ? "-" : "";
439 448
440 switch (instr.opcode.EffectiveOpCode()) { 449 switch (opcode->GetId()) {
441 case OpCode::Id::FFMA_CR: { 450 case OpCode::Id::FFMA_CR: {
442 op_b += GetUniform(instr.uniform); 451 op_b += GetUniform(instr.uniform);
443 op_c += GetRegister(instr.gpr39); 452 op_c += GetRegister(instr.gpr39);
@@ -460,8 +469,8 @@ private:
460 } 469 }
461 default: { 470 default: {
462 NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}", 471 NGLOG_CRITICAL(HW_GPU, "Unhandled FFMA instruction: {} ({}): {}",
463 static_cast<unsigned>(instr.opcode.EffectiveOpCode()), 472 static_cast<unsigned>(opcode->GetId()), opcode->GetName(),
464 OpCode::GetInfo(instr.opcode).name, instr.hex); 473 instr.value);
465 UNREACHABLE(); 474 UNREACHABLE();
466 } 475 }
467 } 476 }
@@ -473,7 +482,7 @@ private:
473 std::string gpr0 = GetRegister(instr.gpr0); 482 std::string gpr0 = GetRegister(instr.gpr0);
474 const Attribute::Index attribute = instr.attribute.fmt20.index; 483 const Attribute::Index attribute = instr.attribute.fmt20.index;
475 484
476 switch (instr.opcode.EffectiveOpCode()) { 485 switch (opcode->GetId()) {
477 case OpCode::Id::LD_A: { 486 case OpCode::Id::LD_A: {
478 ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); 487 ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested");
479 SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4); 488 SetDest(instr.attribute.fmt20.element, gpr0, GetInputAttribute(attribute), 1, 4);
@@ -505,8 +514,8 @@ private:
505 } 514 }
506 default: { 515 default: {
507 NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}", 516 NGLOG_CRITICAL(HW_GPU, "Unhandled memory instruction: {} ({}): {}",
508 static_cast<unsigned>(instr.opcode.EffectiveOpCode()), 517 static_cast<unsigned>(opcode->GetId()), opcode->GetName(),
509 OpCode::GetInfo(instr.opcode).name, instr.hex); 518 instr.value);
510 UNREACHABLE(); 519 UNREACHABLE();
511 } 520 }
512 } 521 }
@@ -564,7 +573,7 @@ private:
564 break; 573 break;
565 } 574 }
566 default: { 575 default: {
567 switch (instr.opcode.EffectiveOpCode()) { 576 switch (opcode->GetId()) {
568 case OpCode::Id::EXIT: { 577 case OpCode::Id::EXIT: {
569 ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex), 578 ASSERT_MSG(instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex),
570 "Predicated exits not implemented"); 579 "Predicated exits not implemented");
@@ -584,8 +593,8 @@ private:
584 } 593 }
585 default: { 594 default: {
586 NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}", 595 NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {} ({}): {}",
587 static_cast<unsigned>(instr.opcode.EffectiveOpCode()), 596 static_cast<unsigned>(opcode->GetId()), opcode->GetName(),
588 OpCode::GetInfo(instr.opcode).name, instr.hex); 597 instr.value);
589 UNREACHABLE(); 598 UNREACHABLE();
590 } 599 }
591 } 600 }