Files
KytyPS5/src/graphics/shader/recompiler/ShaderDecoder.cpp
T
2026-07-21 07:37:52 +02:00

1203 lines
50 KiB
C++

#include "graphics/shader/recompiler/ShaderDecoder.h"
#include "graphics/shader/recompiler/ExportOps.h"
#include "graphics/shader/recompiler/ImageOps.h"
#include "graphics/shader/recompiler/MemoryOps.h"
#include "graphics/shader/recompiler/ScalarAluOps.h"
#include "graphics/shader/recompiler/VectorAluOps.h"
#include <algorithm>
#include <bit>
#include <fmt/format.h>
#include <set>
namespace Libs::Graphics::ShaderRecompiler::Decoder {
namespace {
uint32_t FloatBits(float value) {
return std::bit_cast<uint32_t>(value);
}
void SetError(std::string* error, const char* message) {
if (error != nullptr) {
*error = message;
}
}
bool HasLiteral(const Instruction& inst) {
return inst.src0.kind == OperandKind::LiteralConstant ||
inst.src1.kind == OperandKind::LiteralConstant ||
inst.src2.kind == OperandKind::LiteralConstant ||
inst.src3.kind == OperandKind::LiteralConstant;
}
bool IsControlFlowBranch(Opcode opcode) {
switch (opcode) {
case Opcode::SBranch:
case Opcode::SCbranchScc0:
case Opcode::SCbranchScc1:
case Opcode::SCbranchVccz:
case Opcode::SCbranchVccnz:
case Opcode::SCbranchExecz:
case Opcode::SCbranchExecnz: return true;
default: return false;
}
}
void ApplyLiteral(Operand& operand, uint32_t literal) {
if (operand.kind == OperandKind::LiteralConstant) {
operand.value = literal;
operand.signed_val = static_cast<int32_t>(literal);
}
}
std::string RawWordsToString(const Instruction& inst) {
std::string text;
for (uint32_t i = 0; i < inst.raw_count; i++) {
if (i != 0) {
text += " ";
}
text += fmt::format("0x{:08x}", inst.raw[i]);
}
return text;
}
std::string FormatBinary(const Instruction& inst) {
std::string text = fmt::format("0x{:08x}: {} {}", inst.pc, OpcodeToString(inst.opcode).c_str(),
OperandToString(inst.dst).c_str());
if (inst.dst2.kind != OperandKind::Unknown) {
text += ", ";
text += OperandToString(inst.dst2);
}
const Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2, &inst.src3};
for (uint32_t i = 0; i < inst.src_count && i < 4u; i++) {
text += ", ";
text += OperandToString(*sources[i]);
}
return text;
}
std::string FormatMemory(const Instruction& inst) {
std::string text = fmt::format("0x{:08x}: {} {}", inst.pc, OpcodeToString(inst.opcode).c_str(),
OperandToString(inst.dst).c_str());
const Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2, &inst.src3};
for (uint32_t i = 0; i < inst.src_count && i < 4u; i++) {
text += ", ";
text += OperandToString(*sources[i]);
}
text += fmt::format(" ; offset={} offset2={} dwords={} bits={} dfmt={} nfmt={} signed={} "
"typed={} formatted={} segment={} glc={} slc={} idxen={} offen={}",
inst.offset, inst.secondary_offset, inst.data_dwords, inst.data_bits,
inst.data_format, inst.number_format, inst.data_signed ? 1u : 0u,
inst.typed ? 1u : 0u, inst.formatted ? 1u : 0u, inst.memory_segment,
inst.glc ? 1u : 0u, inst.slc ? 1u : 0u, inst.idxen ? 1u : 0u,
inst.offen ? 1u : 0u);
return text;
}
std::string WithUnsupportedReason(const Instruction& inst, const std::string& text) {
if (inst.unsupported_reason.empty()) {
return text;
}
return text + fmt::format(" ; family={} opcode=0x{:02x} raw=[{}] reason={}",
FamilyToString(inst.family).c_str(), inst.opcode_id,
RawWordsToString(inst).c_str(), inst.unsupported_reason.c_str());
}
void AppendFlag(std::string* text, bool* first, uint32_t flags, uint32_t flag, const char* name) {
if ((flags & flag) == 0) {
return;
}
if (!*first) {
*text += "|";
}
*text += name;
*first = false;
}
std::string ImageSampleFlagsToString(uint32_t flags) {
if (flags == 0) {
return "none";
}
std::string text;
bool first = true;
AppendFlag(&text, &first, flags, ImageSampleFlagLod, "lod");
AppendFlag(&text, &first, flags, ImageSampleFlagBias, "bias");
AppendFlag(&text, &first, flags, ImageSampleFlagDerivative, "derivative");
AppendFlag(&text, &first, flags, ImageSampleFlagCompare, "compare");
AppendFlag(&text, &first, flags, ImageSampleFlagOffset, "offset");
AppendFlag(&text, &first, flags, ImageSampleFlagLevelZero, "level_zero");
AppendFlag(&text, &first, flags, ImageSampleFlagLodClamp, "lod_clamp");
AppendFlag(&text, &first, flags, ImageSampleFlagA16, "a16");
AppendFlag(&text, &first, flags, ImageSampleFlagCd, "cd");
AppendFlag(&text, &first, flags, ImageSampleFlagGatherHorizontal, "gather_horizontal");
return text;
}
const char* ImageDimensionToString(ImageDimension dimension) {
switch (dimension) {
case ImageDimension::Dim2D: return "2d";
case ImageDimension::Dim3D: return "3d";
case ImageDimension::Dim2DArray: return "2d_array";
default: return "unknown";
}
}
std::string FormatMimg(const Instruction& inst) {
const char* sample_name =
inst.opcode == Opcode::ImageSample ? MimgSampleOpcodeName(inst.opcode_id) : nullptr;
const std::string name =
sample_name != nullptr ? std::string(sample_name) : OpcodeToString(inst.opcode);
std::string text = fmt::format(
"0x{:08x}: {} {}, {}, {}, {} ; dmask=0x{:x} image_dim={}", inst.pc, name.c_str(),
OperandToString(inst.dst).c_str(), OperandToString(inst.src0).c_str(),
OperandToString(inst.src1).c_str(), OperandToString(inst.src2).c_str(), inst.dmask,
ImageDimensionToString(inst.image_dimension));
switch (inst.opcode) {
case Opcode::ImageSample:
case Opcode::ImageGather4Lz:
case Opcode::ImageGather4C:
case Opcode::ImageGather4CLz:
case Opcode::ImageGather4LzO:
case Opcode::ImageGather4CO:
case Opcode::ImageGather4CLzO:
case Opcode::ImageGather4H:
text += fmt::format(" sample_flags={} addr_components={}",
ImageSampleFlagsToString(inst.image_sample_flags).c_str(),
inst.image_address_components);
break;
default: break;
}
if (inst.image_nsa_dwords != 0) {
text += fmt::format(" nsa_dwords={} nsa_addr=", inst.image_nsa_dwords);
const auto count =
std::min<uint32_t>(inst.image_nsa_dwords * 4u, MaxImageNsaAddressComponents);
for (uint32_t i = 0; i < count; i++) {
if (i != 0) {
text += ",";
}
text += fmt::format("{}", inst.image_nsa_addr[i]);
}
}
return text;
}
std::string FormatExp(const Instruction& inst) {
std::string text = fmt::format("0x{:08x}: exp target=0x{:02x} en=0x{:x} done={} compr={} vm={}",
inst.pc, inst.exp.target, inst.exp.en, inst.exp.done ? 1u : 0u,
inst.exp.compr ? 1u : 0u, inst.exp.vm ? 1u : 0u);
const Operand* sources[] = {&inst.src0, &inst.src1, &inst.src2, &inst.src3};
for (uint32_t i = 0; i < inst.src_count && i < 4u; i++) {
text += i == 0 ? " " : ", ";
text += OperandToString(*sources[i]);
}
return text;
}
} // namespace
bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
operand = {};
if (code <= 105u) {
operand.kind = OperandKind::Sgpr;
operand.reg = code;
return true;
}
if (code >= 128u && code <= 192u) {
operand.kind = OperandKind::IntegerInlineConstant;
operand.signed_val = static_cast<int32_t>(code - 128u);
operand.value = static_cast<uint32_t>(operand.signed_val);
return true;
}
if (code >= 193u && code <= 208u) {
operand.kind = OperandKind::IntegerInlineConstant;
operand.signed_val = 192 - static_cast<int32_t>(code);
operand.value = static_cast<uint32_t>(operand.signed_val);
return true;
}
if (code >= 240u && code <= 247u) {
constexpr float values[] = {0.5f, -0.5f, 1.0f, -1.0f, 2.0f, -2.0f, 4.0f, -4.0f};
operand.kind = OperandKind::FloatInlineConstant;
operand.float_val = values[code - 240u];
operand.value = FloatBits(operand.float_val);
return true;
}
if (code >= 256u && code <= 511u) {
return DecodeVectorGpr(code - 256u, operand, error);
}
switch (code) {
case 106u: operand.kind = OperandKind::VccLo; return true;
case 107u: operand.kind = OperandKind::VccHi; return true;
case 124u: operand.kind = OperandKind::M0; return true;
case 125u: operand.kind = OperandKind::Null; return true;
case 126u: operand.kind = OperandKind::ExecLo; return true;
case 127u: operand.kind = OperandKind::ExecHi; return true;
case 239u: operand.kind = OperandKind::PopsExitingWaveId; return true;
case 248u:
operand.kind = OperandKind::FloatInlineConstant;
operand.float_val = 0.15915494309189535f;
operand.value = FloatBits(operand.float_val);
return true;
case 251u: operand.kind = OperandKind::VccZ; return true;
case 252u: operand.kind = OperandKind::ExecZ; return true;
case 253u: operand.kind = OperandKind::Scc; return true;
case 255u: operand.kind = OperandKind::LiteralConstant; return true;
default:
if (error != nullptr) {
*error = fmt::format("unsupported scalar source operand 0x{:08x} at pc 0x{:08x}",
code, pc);
}
return false;
}
}
bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error) {
operand = {};
if (code <= 105u) {
operand.kind = OperandKind::Sgpr;
operand.reg = code;
return true;
}
switch (code) {
case 106u: operand.kind = OperandKind::VccLo; return true;
case 107u: operand.kind = OperandKind::VccHi; return true;
case 124u: operand.kind = OperandKind::M0; return true;
case 125u: operand.kind = OperandKind::Null; return true;
case 126u: operand.kind = OperandKind::ExecLo; return true;
case 127u: operand.kind = OperandKind::ExecHi; return true;
default:
if (error != nullptr) {
*error = fmt::format(
"unsupported scalar destination operand 0x{:08x} at pc 0x{:08x}", code, pc);
}
return false;
}
}
bool DecodeVectorGpr(uint32_t reg, Operand& operand, std::string* error) {
if (reg > 255u) {
SetError(error, "VGPR index is out of range");
return false;
}
operand = {};
operand.kind = OperandKind::Vgpr;
operand.reg = reg;
return true;
}
bool ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
if (!HasLiteral(inst)) {
return true;
}
if (word_index + inst.word_count >= code.size()) {
if (error != nullptr) {
*error = fmt::format("missing literal constant at pc 0x{:08x}", inst.pc);
}
return false;
}
const auto literal = code[word_index + inst.word_count];
ApplyLiteral(inst.src0, literal);
ApplyLiteral(inst.src1, literal);
ApplyLiteral(inst.src2, literal);
ApplyLiteral(inst.src3, literal);
inst.word_count++;
SetRawWords(inst, code, word_index, inst.word_count);
return true;
}
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
uint32_t word_count) {
inst.word_count = word_count;
inst.raw_count = std::min<uint32_t>(word_count, MaxInstructionRawWords);
for (uint32_t i = 0; i < inst.raw_count; i++) {
inst.raw[i] = code[word_index + i];
}
}
void SetUnsupported(Instruction& inst, Family family, uint32_t opcode_id, const char* reason) {
inst.opcode = Opcode::Unsupported;
inst.family = family;
inst.opcode_id = opcode_id;
inst.unsupported_reason = reason;
}
bool DecodeProgram(std::span<const uint32_t> code, Program& program, std::string* error) {
if (code.empty() || code.size() > UINT32_MAX / sizeof(uint32_t)) {
SetError(error, "invalid shader decoder input");
return false;
}
program.instructions.clear();
program.code = code;
std::set<uint32_t> branch_targets;
for (uint32_t word_index = 0; word_index < code.size();) {
const uint32_t pc = word_index * 4u;
const uint32_t word = code[word_index];
Instruction inst;
bool ok = false;
if ((word & 0x80000000u) == 0u) {
ok = DecodeVop2(pc, code, word_index, inst, error);
} else if ((word & 0xc0000000u) == 0x80000000u) {
const auto opcode = (word >> 23u) & 0x7fu;
switch (opcode) {
case 0x7du: ok = DecodeSop1(pc, code, word_index, inst, error); break;
case 0x7eu: ok = DecodeSopc(pc, code, word_index, inst, error); break;
case 0x7fu: ok = DecodeSopp(pc, code, word_index, inst, error); break;
default:
ok = opcode >= 0x60u ? DecodeSopk(pc, code, word_index, inst, error)
: DecodeSop2(pc, code, word_index, inst, error);
break;
}
} else {
switch (word >> 26u) {
case 0x32u: ok = DecodeVintrp(pc, code, word_index, inst, error); break;
case 0x33u: ok = DecodeVop3p(pc, code, word_index, inst, error); break;
case 0x35u: ok = DecodeVop3(pc, code, word_index, inst, error); break;
case 0x36u: ok = DecodeDs(pc, code, word_index, inst, error); break;
case 0x37u: ok = DecodeFlat(pc, code, word_index, inst, error); break;
case 0x38u: ok = DecodeMubuf(pc, code, word_index, inst, error); break;
case 0x3au: ok = DecodeMtbuf(pc, code, word_index, inst, error); break;
case 0x3cu: ok = DecodeMimg(pc, code, word_index, inst, error); break;
case 0x3du: ok = DecodeSmem(pc, code, word_index, inst, error); break;
case 0x3eu: ok = DecodeExp(pc, code, word_index, inst, error); break;
default:
if (error != nullptr) {
*error = fmt::format(
"unknown RDNA2 instruction family at pc 0x{:08x}, raw=0x{:08x}", pc,
word);
}
return false;
}
}
if (!ok) {
return false;
}
program.instructions.push_back(inst);
word_index += inst.word_count;
if (IsControlFlowBranch(inst.opcode)) {
branch_targets.insert(inst.branch_target);
}
if (inst.opcode == Opcode::SEndpgm &&
(word_index >= code.size() || !branch_targets.contains(word_index * 4u))) {
return true;
}
}
SetError(error, "shader decode reached the code boundary before S_ENDPGM");
return false;
}
std::string FamilyToString(Family family) {
switch (family) {
case Family::SOP1: return "SOP1";
case Family::SOP2: return "SOP2";
case Family::SOPK: return "SOPK";
case Family::SOPC: return "SOPC";
case Family::SOPP: return "SOPP";
case Family::VOP1: return "VOP1";
case Family::VOP2: return "VOP2";
case Family::VOP3: return "VOP3";
case Family::VOP3P: return "VOP3P";
case Family::VOPC: return "VOPC";
case Family::VINTRP: return "VINTRP";
case Family::SMEM: return "SMEM";
case Family::MUBUF: return "MUBUF";
case Family::MTBUF: return "MTBUF";
case Family::FLAT: return "FLAT";
case Family::DS: return "DS";
case Family::MIMG: return "MIMG";
case Family::EXP: return "EXP";
default: return "Unknown";
}
}
std::string OpcodeToString(Opcode opcode) {
switch (opcode) {
case Opcode::SMovB32: return "s_mov_b32";
case Opcode::SMovB64: return "s_mov_b64";
case Opcode::SMovkI32: return "s_movk_i32";
case Opcode::SAbsI32: return "s_abs_i32";
case Opcode::SBrevB32: return "s_brev_b32";
case Opcode::SBcnt1I32B32: return "s_bcnt1_i32_b32";
case Opcode::SBcnt1I32B64: return "s_bcnt1_i32_b64";
case Opcode::SFf1I32B32: return "s_ff1_i32_b32";
case Opcode::SFlbitI32B64: return "s_flbit_i32_b64";
case Opcode::SBitreplicateB64B32: return "s_bitreplicate_b64_b32";
case Opcode::SGetpcB64: return "s_getpc_b64";
case Opcode::SSetpcB64: return "s_setpc_b64";
case Opcode::SAndSaveexecB32: return "s_and_saveexec_b32";
case Opcode::SAndn1SaveexecB32: return "s_andn1_saveexec_b32";
case Opcode::SAndSaveexecB64: return "s_and_saveexec_b64";
case Opcode::SOrn2SaveexecB64: return "s_orn2_saveexec_b64";
case Opcode::SAndn1SaveexecB64: return "s_andn1_saveexec_b64";
case Opcode::SNotB32: return "s_not_b32";
case Opcode::SNotB64: return "s_not_b64";
case Opcode::SWqmB64: return "s_wqm_b64";
case Opcode::SAddU32: return "s_add_u32";
case Opcode::SAddcU32: return "s_addc_u32";
case Opcode::SSubbU32: return "s_subb_u32";
case Opcode::SSubU32: return "s_sub_u32";
case Opcode::SAddI32: return "s_add_i32";
case Opcode::SSubI32: return "s_sub_i32";
case Opcode::SBitcmp0B32: return "s_bitcmp0_b32";
case Opcode::SBitcmp1B32: return "s_bitcmp1_b32";
case Opcode::SBitset0B32: return "s_bitset0_b32";
case Opcode::SBitset1B32: return "s_bitset1_b32";
case Opcode::SMinI32: return "s_min_i32";
case Opcode::SMaxI32: return "s_max_i32";
case Opcode::SMinU32: return "s_min_u32";
case Opcode::SMaxU32: return "s_max_u32";
case Opcode::SAndB32: return "s_and_b32";
case Opcode::SAndB64: return "s_and_b64";
case Opcode::SAndn2B32: return "s_andn2_b32";
case Opcode::SAndn2B64: return "s_andn2_b64";
case Opcode::SOrB32: return "s_or_b32";
case Opcode::SOrB64: return "s_or_b64";
case Opcode::SOrn2B32: return "s_orn2_b32";
case Opcode::SOrn2B64: return "s_orn2_b64";
case Opcode::SXorB32: return "s_xor_b32";
case Opcode::SXorB64: return "s_xor_b64";
case Opcode::SNandB32: return "s_nand_b32";
case Opcode::SNandB64: return "s_nand_b64";
case Opcode::SNorB32: return "s_nor_b32";
case Opcode::SNorB64: return "s_nor_b64";
case Opcode::SXnorB32: return "s_xnor_b32";
case Opcode::SXnorB64: return "s_xnor_b64";
case Opcode::SLshlB32: return "s_lshl_b32";
case Opcode::SLshlB64: return "s_lshl_b64";
case Opcode::SLshl1AddU32: return "s_lshl1_add_u32";
case Opcode::SLshl2AddU32: return "s_lshl2_add_u32";
case Opcode::SLshl3AddU32: return "s_lshl3_add_u32";
case Opcode::SLshl4AddU32: return "s_lshl4_add_u32";
case Opcode::SLshrB32: return "s_lshr_b32";
case Opcode::SLshrB64: return "s_lshr_b64";
case Opcode::SAshrI32: return "s_ashr_i32";
case Opcode::SMulI32: return "s_mul_i32";
case Opcode::SMulHiU32: return "s_mul_hi_u32";
case Opcode::SMulkI32: return "s_mulk_i32";
case Opcode::SBfeU32: return "s_bfe_u32";
case Opcode::SBfeU64: return "s_bfe_u64";
case Opcode::SBfmB32: return "s_bfm_b32";
case Opcode::SBfmB64: return "s_bfm_b64";
case Opcode::SCselectB32: return "s_cselect_b32";
case Opcode::SCselectB64: return "s_cselect_b64";
case Opcode::SPackLlB32B16: return "s_pack_ll_b32_b16";
case Opcode::SPackLhB32B16: return "s_pack_lh_b32_b16";
case Opcode::SPackHhB32B16: return "s_pack_hh_b32_b16";
case Opcode::SCmpEqI32: return "s_cmp_eq_i32";
case Opcode::SCmpLgI32: return "s_cmp_lg_i32";
case Opcode::SCmpGtI32: return "s_cmp_gt_i32";
case Opcode::SCmpGeI32: return "s_cmp_ge_i32";
case Opcode::SCmpLtI32: return "s_cmp_lt_i32";
case Opcode::SCmpLeI32: return "s_cmp_le_i32";
case Opcode::SCmpEqU32: return "s_cmp_eq_u32";
case Opcode::SCmpLgU32: return "s_cmp_lg_u32";
case Opcode::SCmpGtU32: return "s_cmp_gt_u32";
case Opcode::SCmpGeU32: return "s_cmp_ge_u32";
case Opcode::SCmpLtU32: return "s_cmp_lt_u32";
case Opcode::SCmpLeU32: return "s_cmp_le_u32";
case Opcode::SCmpLgU64: return "s_cmp_lg_u64";
case Opcode::VNop: return "v_nop";
case Opcode::VMovB32: return "v_mov_b32";
case Opcode::VMovreldB32: return "v_movreld_b32";
case Opcode::VMovrelsB32: return "v_movrels_b32";
case Opcode::VReadfirstlaneB32: return "v_readfirstlane_b32";
case Opcode::VReadlaneB32: return "v_readlane_b32";
case Opcode::VWritelaneB32: return "v_writelane_b32";
case Opcode::VPermlane16B32: return "v_permlane16_b32";
case Opcode::VPermlanex16B32: return "v_permlanex16_b32";
case Opcode::VCubeidF32: return "v_cubeid_f32";
case Opcode::VCubescF32: return "v_cubesc_f32";
case Opcode::VCubetcF32: return "v_cubetc_f32";
case Opcode::VCubemaF32: return "v_cubema_f32";
case Opcode::VCndmaskB32: return "v_cndmask_b32";
case Opcode::VDot2cF32F16: return "v_dot2c_f32_f16";
case Opcode::VCvtF32I32: return "v_cvt_f32_i32";
case Opcode::VCvtF32U32: return "v_cvt_f32_u32";
case Opcode::VCvtU32F32: return "v_cvt_u32_f32";
case Opcode::VCvtI32F32: return "v_cvt_i32_f32";
case Opcode::VCvtF16F32: return "v_cvt_f16_f32";
case Opcode::VCvtF32F16: return "v_cvt_f32_f16";
case Opcode::VCvtF16U16: return "v_cvt_f16_u16";
case Opcode::VCvtU16F16: return "v_cvt_u16_f16";
case Opcode::VCvtF16I16: return "v_cvt_f16_i16";
case Opcode::VCvtI16F16: return "v_cvt_i16_f16";
case Opcode::VCvtRpiI32F32: return "v_cvt_rpi_i32_f32";
case Opcode::VCvtFlrI32F32: return "v_cvt_flr_i32_f32";
case Opcode::VCvtOffF32I4: return "v_cvt_off_f32_i4";
case Opcode::VCvtF32Ubyte0: return "v_cvt_f32_ubyte0";
case Opcode::VCvtF32Ubyte1: return "v_cvt_f32_ubyte1";
case Opcode::VCvtF32Ubyte2: return "v_cvt_f32_ubyte2";
case Opcode::VCvtF32Ubyte3: return "v_cvt_f32_ubyte3";
case Opcode::VRcpF32: return "v_rcp_f32";
case Opcode::VFractF32: return "v_fract_f32";
case Opcode::VTruncF32: return "v_trunc_f32";
case Opcode::VCeilF32: return "v_ceil_f32";
case Opcode::VRndneF32: return "v_rndne_f32";
case Opcode::VFloorF32: return "v_floor_f32";
case Opcode::VExpF32: return "v_exp_f32";
case Opcode::VLogF32: return "v_log_f32";
case Opcode::VRsqF32: return "v_rsq_f32";
case Opcode::VSqrtF32: return "v_sqrt_f32";
case Opcode::VRcpF16: return "v_rcp_f16";
case Opcode::VSqrtF16: return "v_sqrt_f16";
case Opcode::VRsqF16: return "v_rsq_f16";
case Opcode::VLogF16: return "v_log_f16";
case Opcode::VExpF16: return "v_exp_f16";
case Opcode::VFloorF16: return "v_floor_f16";
case Opcode::VCeilF16: return "v_ceil_f16";
case Opcode::VTruncF16: return "v_trunc_f16";
case Opcode::VRndneF16: return "v_rndne_f16";
case Opcode::VSinF32: return "v_sin_f32";
case Opcode::VCosF32: return "v_cos_f32";
case Opcode::VNotB32: return "v_not_b32";
case Opcode::VBfrevB32: return "v_bfrev_b32";
case Opcode::VFfbhU32: return "v_ffbh_u32";
case Opcode::VFfblB32: return "v_ffbl_b32";
case Opcode::VAddF32: return "v_add_f32";
case Opcode::VSubF32: return "v_sub_f32";
case Opcode::VSubrevF32: return "v_subrev_f32";
case Opcode::VMulF32: return "v_mul_f32";
case Opcode::VMacF32: return "v_mac_f32";
case Opcode::VMadmkF32: return "v_madmk_f32";
case Opcode::VMadakF32: return "v_madak_f32";
case Opcode::VMinF32: return "v_min_f32";
case Opcode::VMaxF32: return "v_max_f32";
case Opcode::VAddF16: return "v_add_f16";
case Opcode::VSubF16: return "v_sub_f16";
case Opcode::VSubrevF16: return "v_subrev_f16";
case Opcode::VMulF16: return "v_mul_f16";
case Opcode::VFmacF16: return "v_fmac_f16";
case Opcode::VFmamkF16: return "v_fmamk_f16";
case Opcode::VFmaakF16: return "v_fmaak_f16";
case Opcode::VMinI32: return "v_min_i32";
case Opcode::VMaxI32: return "v_max_i32";
case Opcode::VMulI32I24: return "v_mul_i32_i24";
case Opcode::VMulU32U24: return "v_mul_u32_u24";
case Opcode::VMulLoU32: return "v_mul_lo_u32";
case Opcode::VMulHiU32: return "v_mul_hi_u32";
case Opcode::VMulLoI32: return "v_mul_lo_i32";
case Opcode::VMulHiI32: return "v_mul_hi_i32";
case Opcode::VCvtPkrtzF16F32: return "v_cvt_pkrtz_f16_f32";
case Opcode::VCvtPkU8F32: return "v_cvt_pk_u8_f32";
case Opcode::VMadF32: return "v_mad_f32";
case Opcode::VMadI32I24: return "v_mad_i32_i24";
case Opcode::VMadU32U24: return "v_mad_u32_u24";
case Opcode::VMadU64U32: return "v_mad_u64_u32";
case Opcode::VFmaF32: return "v_fma_f32";
case Opcode::VFmaF16: return "v_fma_f16";
case Opcode::VPackB32F16: return "v_pack_b32_f16";
case Opcode::VBfeU32: return "v_bfe_u32";
case Opcode::VBfeI32: return "v_bfe_i32";
case Opcode::VBfiB32: return "v_bfi_b32";
case Opcode::VAlignbitB32: return "v_alignbit_b32";
case Opcode::VMin3F32: return "v_min3_f32";
case Opcode::VMin3I32: return "v_min3_i32";
case Opcode::VMin3U32: return "v_min3_u32";
case Opcode::VMin3F16: return "v_min3_f16";
case Opcode::VMax3F32: return "v_max3_f32";
case Opcode::VMax3I32: return "v_max3_i32";
case Opcode::VMax3U32: return "v_max3_u32";
case Opcode::VMax3F16: return "v_max3_f16";
case Opcode::VMed3F32: return "v_med3_f32";
case Opcode::VMed3I32: return "v_med3_i32";
case Opcode::VMed3U32: return "v_med3_u32";
case Opcode::VMed3F16: return "v_med3_f16";
case Opcode::VSadU32: return "v_sad_u32";
case Opcode::VAdd3U32: return "v_add3_u32";
case Opcode::VLshlAddU32: return "v_lshl_add_u32";
case Opcode::VAddLshlU32: return "v_add_lshl_u32";
case Opcode::VXadU32: return "v_xad_u32";
case Opcode::VLshlOrB32: return "v_lshl_or_b32";
case Opcode::VAndOrB32: return "v_and_or_b32";
case Opcode::VOr3B32: return "v_or3_b32";
case Opcode::VXor3B32: return "v_xor3_b32";
case Opcode::VAddI32: return "v_add_i32";
case Opcode::VSubI32: return "v_sub_i32";
case Opcode::VSubrevI32: return "v_subrev_i32";
case Opcode::VBfmB32: return "v_bfm_b32";
case Opcode::VLdexpF32: return "v_ldexp_f32";
case Opcode::VCvtPknormI16F32: return "v_cvt_pknorm_i16_f32";
case Opcode::VCvtPknormU16F32: return "v_cvt_pknorm_u16_f32";
case Opcode::VCvtPkU16U32: return "v_cvt_pk_u16_u32";
case Opcode::VPkMadI16: return "v_pk_mad_i16";
case Opcode::VPkMulLoU16: return "v_pk_mul_lo_u16";
case Opcode::VPkAddI16: return "v_pk_add_i16";
case Opcode::VPkSubI16: return "v_pk_sub_i16";
case Opcode::VPkLshlrevB16: return "v_pk_lshlrev_b16";
case Opcode::VPkLshrrevB16: return "v_pk_lshrrev_b16";
case Opcode::VPkAshrrevI16: return "v_pk_ashrrev_i16";
case Opcode::VPkMaxI16: return "v_pk_max_i16";
case Opcode::VPkMinI16: return "v_pk_min_i16";
case Opcode::VPkMadU16: return "v_pk_mad_u16";
case Opcode::VPkAddF16: return "v_pk_add_f16";
case Opcode::VPkMulF16: return "v_pk_mul_f16";
case Opcode::VPkMinF16: return "v_pk_min_f16";
case Opcode::VPkMaxF16: return "v_pk_max_f16";
case Opcode::VPkFmaF16: return "v_pk_fma_f16";
case Opcode::VPkFmacF16: return "v_pk_fmac_f16";
case Opcode::VPkAddU16: return "v_pk_add_u16";
case Opcode::VPkSubU16: return "v_pk_sub_u16";
case Opcode::VPkMaxU16: return "v_pk_max_u16";
case Opcode::VPkMinU16: return "v_pk_min_u16";
case Opcode::VMadMixloF16: return "v_mad_mixlo_f16";
case Opcode::VMadMixhiF16: return "v_mad_mixhi_f16";
case Opcode::VAddNcU32: return "v_add_nc_u32";
case Opcode::VAddcU32: return "v_addc_u32";
case Opcode::VSubNcU32: return "v_sub_nc_u32";
case Opcode::VSubrevNcU32: return "v_subrev_nc_u32";
case Opcode::VAddNcU16: return "v_add_nc_u16";
case Opcode::VSubNcU16: return "v_sub_nc_u16";
case Opcode::VMaxU16: return "v_max_u16";
case Opcode::VMaxI16: return "v_max_i16";
case Opcode::VMinU16: return "v_min_u16";
case Opcode::VMinI16: return "v_min_i16";
case Opcode::VAddNcI16: return "v_add_nc_i16";
case Opcode::VSubNcI16: return "v_sub_nc_i16";
case Opcode::VAndB32: return "v_and_b32";
case Opcode::VOrB32: return "v_or_b32";
case Opcode::VXorB32: return "v_xor_b32";
case Opcode::VXnorB32: return "v_xnor_b32";
case Opcode::VLshlB32: return "v_lshl_b32";
case Opcode::VLshlrevB32: return "v_lshlrev_b32";
case Opcode::VLshrB32: return "v_lshr_b32";
case Opcode::VLshrrevB32: return "v_lshrrev_b32";
case Opcode::VAshrI32: return "v_ashr_i32";
case Opcode::VAshrrevI32: return "v_ashrrev_i32";
case Opcode::VLshlrevB16: return "v_lshlrev_b16";
case Opcode::VLshrrevB16: return "v_lshrrev_b16";
case Opcode::VAshrrevI16: return "v_ashrrev_i16";
case Opcode::VBcntU32B32: return "v_bcnt_u32_b32";
case Opcode::VMbcntLoU32B32: return "v_mbcnt_lo_u32_b32";
case Opcode::VMbcntHiU32B32: return "v_mbcnt_hi_u32_b32";
case Opcode::VMinU32: return "v_min_u32";
case Opcode::VMaxU32: return "v_max_u32";
case Opcode::VMaxF16: return "v_max_f16";
case Opcode::VMinF16: return "v_min_f16";
case Opcode::VCmpFF32: return "v_cmp_f_f32";
case Opcode::VCmpLtF32: return "v_cmp_lt_f32";
case Opcode::VCmpEqF32: return "v_cmp_eq_f32";
case Opcode::VCmpLeF32: return "v_cmp_le_f32";
case Opcode::VCmpGtF32: return "v_cmp_gt_f32";
case Opcode::VCmpLgF32: return "v_cmp_lg_f32";
case Opcode::VCmpGeF32: return "v_cmp_ge_f32";
case Opcode::VCmpOF32: return "v_cmp_o_f32";
case Opcode::VCmpUF32: return "v_cmp_u_f32";
case Opcode::VCmpNgeF32: return "v_cmp_nge_f32";
case Opcode::VCmpNlgF32: return "v_cmp_nlg_f32";
case Opcode::VCmpNgtF32: return "v_cmp_ngt_f32";
case Opcode::VCmpNleF32: return "v_cmp_nle_f32";
case Opcode::VCmpNeqF32: return "v_cmp_neq_f32";
case Opcode::VCmpNltF32: return "v_cmp_nlt_f32";
case Opcode::VCmpTruF32: return "v_cmp_tru_f32";
case Opcode::VCmpxLtF32: return "v_cmpx_lt_f32";
case Opcode::VCmpxEqF32: return "v_cmpx_eq_f32";
case Opcode::VCmpxLeF32: return "v_cmpx_le_f32";
case Opcode::VCmpxGtF32: return "v_cmpx_gt_f32";
case Opcode::VCmpxLgF32: return "v_cmpx_lg_f32";
case Opcode::VCmpxGeF32: return "v_cmpx_ge_f32";
case Opcode::VCmpxNgeF32: return "v_cmpx_nge_f32";
case Opcode::VCmpxNlgF32: return "v_cmpx_nlg_f32";
case Opcode::VCmpxNgtF32: return "v_cmpx_ngt_f32";
case Opcode::VCmpxNleF32: return "v_cmpx_nle_f32";
case Opcode::VCmpxNeqF32: return "v_cmpx_neq_f32";
case Opcode::VCmpxNltF32: return "v_cmpx_nlt_f32";
case Opcode::VCmpFI32: return "v_cmp_f_i32";
case Opcode::VCmpLtI32: return "v_cmp_lt_i32";
case Opcode::VCmpEqI32: return "v_cmp_eq_i32";
case Opcode::VCmpLeI32: return "v_cmp_le_i32";
case Opcode::VCmpGtI32: return "v_cmp_gt_i32";
case Opcode::VCmpNeI32: return "v_cmp_ne_i32";
case Opcode::VCmpGeI32: return "v_cmp_ge_i32";
case Opcode::VCmpTI32: return "v_cmp_t_i32";
case Opcode::VCmpClassF32: return "v_cmp_class_f32";
case Opcode::VCmpLtI16: return "v_cmp_lt_i16";
case Opcode::VCmpEqI16: return "v_cmp_eq_i16";
case Opcode::VCmpLeI16: return "v_cmp_le_i16";
case Opcode::VCmpGtI16: return "v_cmp_gt_i16";
case Opcode::VCmpNeI16: return "v_cmp_ne_i16";
case Opcode::VCmpGeI16: return "v_cmp_ge_i16";
case Opcode::VCmpLtF16: return "v_cmp_lt_f16";
case Opcode::VCmpEqF16: return "v_cmp_eq_f16";
case Opcode::VCmpLeF16: return "v_cmp_le_f16";
case Opcode::VCmpGtF16: return "v_cmp_gt_f16";
case Opcode::VCmpLgF16: return "v_cmp_lg_f16";
case Opcode::VCmpGeF16: return "v_cmp_ge_f16";
case Opcode::VCmpNeqF16: return "v_cmp_neq_f16";
case Opcode::VCmpxLtF16: return "v_cmpx_lt_f16";
case Opcode::VCmpxEqF16: return "v_cmpx_eq_f16";
case Opcode::VCmpxLeF16: return "v_cmpx_le_f16";
case Opcode::VCmpxGtF16: return "v_cmpx_gt_f16";
case Opcode::VCmpxGeF16: return "v_cmpx_ge_f16";
case Opcode::VCmpxNeqF16: return "v_cmpx_neq_f16";
case Opcode::VCmpxNltF16: return "v_cmpx_nlt_f16";
case Opcode::VCmpxLtI32: return "v_cmpx_lt_i32";
case Opcode::VCmpxEqI32: return "v_cmpx_eq_i32";
case Opcode::VCmpxLeI32: return "v_cmpx_le_i32";
case Opcode::VCmpxGtI32: return "v_cmpx_gt_i32";
case Opcode::VCmpxNeI32: return "v_cmpx_ne_i32";
case Opcode::VCmpxGeI32: return "v_cmpx_ge_i32";
case Opcode::VCmpLtU16: return "v_cmp_lt_u16";
case Opcode::VCmpEqU16: return "v_cmp_eq_u16";
case Opcode::VCmpLeU16: return "v_cmp_le_u16";
case Opcode::VCmpGtU16: return "v_cmp_gt_u16";
case Opcode::VCmpNeU16: return "v_cmp_ne_u16";
case Opcode::VCmpGeU16: return "v_cmp_ge_u16";
case Opcode::VCmpFU32: return "v_cmp_f_u32";
case Opcode::VCmpLtU32: return "v_cmp_lt_u32";
case Opcode::VCmpEqU32: return "v_cmp_eq_u32";
case Opcode::VCmpLeU32: return "v_cmp_le_u32";
case Opcode::VCmpGtU32: return "v_cmp_gt_u32";
case Opcode::VCmpNeU32: return "v_cmp_ne_u32";
case Opcode::VCmpGeU32: return "v_cmp_ge_u32";
case Opcode::VCmpTU32: return "v_cmp_t_u32";
case Opcode::VCmpNeU64: return "v_cmp_ne_u64";
case Opcode::VCmpxLtU32: return "v_cmpx_lt_u32";
case Opcode::VCmpxEqU32: return "v_cmpx_eq_u32";
case Opcode::VCmpxLeU32: return "v_cmpx_le_u32";
case Opcode::VCmpxGtU32: return "v_cmpx_gt_u32";
case Opcode::VCmpxNeU32: return "v_cmpx_ne_u32";
case Opcode::VCmpxGeU32: return "v_cmpx_ge_u32";
case Opcode::SLoadDword: return "s_load_dword";
case Opcode::SLoadDwordx2: return "s_load_dwordx2";
case Opcode::SLoadDwordx4: return "s_load_dwordx4";
case Opcode::SLoadDwordx8: return "s_load_dwordx8";
case Opcode::SLoadDwordx16: return "s_load_dwordx16";
case Opcode::SBufferLoadDword: return "s_buffer_load_dword";
case Opcode::SBufferLoadDwordx2: return "s_buffer_load_dwordx2";
case Opcode::SBufferLoadDwordx4: return "s_buffer_load_dwordx4";
case Opcode::SBufferLoadDwordx8: return "s_buffer_load_dwordx8";
case Opcode::SBufferLoadDwordx16: return "s_buffer_load_dwordx16";
case Opcode::BufferLoadFormatX: return "buffer_load_format_x";
case Opcode::BufferLoadFormatXy: return "buffer_load_format_xy";
case Opcode::BufferLoadFormatXyz: return "buffer_load_format_xyz";
case Opcode::BufferLoadFormatXyzw: return "buffer_load_format_xyzw";
case Opcode::BufferStoreFormatX: return "buffer_store_format_x";
case Opcode::BufferStoreFormatXy: return "buffer_store_format_xy";
case Opcode::BufferStoreFormatXyz: return "buffer_store_format_xyz";
case Opcode::BufferStoreFormatXyzw: return "buffer_store_format_xyzw";
case Opcode::BufferLoadUbyte: return "buffer_load_ubyte";
case Opcode::BufferLoadSbyte: return "buffer_load_sbyte";
case Opcode::BufferLoadUshort: return "buffer_load_ushort";
case Opcode::BufferLoadSshort: return "buffer_load_sshort";
case Opcode::BufferLoadDword: return "buffer_load_dword";
case Opcode::BufferLoadDwordx2: return "buffer_load_dwordx2";
case Opcode::BufferLoadDwordx3: return "buffer_load_dwordx3";
case Opcode::BufferLoadDwordx4: return "buffer_load_dwordx4";
case Opcode::BufferStoreByte: return "buffer_store_byte";
case Opcode::BufferStoreShort: return "buffer_store_short";
case Opcode::BufferStoreDword: return "buffer_store_dword";
case Opcode::BufferStoreDwordx2: return "buffer_store_dwordx2";
case Opcode::BufferStoreDwordx3: return "buffer_store_dwordx3";
case Opcode::BufferStoreDwordx4: return "buffer_store_dwordx4";
case Opcode::TBufferLoadFormatX: return "tbuffer_load_format_x";
case Opcode::TBufferLoadFormatXy: return "tbuffer_load_format_xy";
case Opcode::TBufferLoadFormatXyz: return "tbuffer_load_format_xyz";
case Opcode::TBufferLoadFormatXyzw: return "tbuffer_load_format_xyzw";
case Opcode::TBufferStoreFormatX: return "tbuffer_store_format_x";
case Opcode::TBufferStoreFormatXy: return "tbuffer_store_format_xy";
case Opcode::TBufferStoreFormatXyz: return "tbuffer_store_format_xyz";
case Opcode::TBufferStoreFormatXyzw: return "tbuffer_store_format_xyzw";
case Opcode::BufferAtomicSwap: return "buffer_atomic_swap";
case Opcode::BufferAtomicAdd: return "buffer_atomic_add";
case Opcode::BufferAtomicSub: return "buffer_atomic_sub";
case Opcode::BufferAtomicSMin: return "buffer_atomic_smin";
case Opcode::BufferAtomicUMin: return "buffer_atomic_umin";
case Opcode::BufferAtomicSMax: return "buffer_atomic_smax";
case Opcode::BufferAtomicUMax: return "buffer_atomic_umax";
case Opcode::BufferAtomicAnd: return "buffer_atomic_and";
case Opcode::BufferAtomicOr: return "buffer_atomic_or";
case Opcode::BufferAtomicXor: return "buffer_atomic_xor";
case Opcode::FlatLoadUbyte: return "flat_load_ubyte";
case Opcode::FlatLoadSbyte: return "flat_load_sbyte";
case Opcode::FlatLoadUshort: return "flat_load_ushort";
case Opcode::FlatLoadSshort: return "flat_load_sshort";
case Opcode::FlatLoadDword: return "flat_load_dword";
case Opcode::FlatLoadDwordx2: return "flat_load_dwordx2";
case Opcode::FlatLoadDwordx3: return "flat_load_dwordx3";
case Opcode::FlatLoadDwordx4: return "flat_load_dwordx4";
case Opcode::FlatStoreByte: return "flat_store_byte";
case Opcode::FlatStoreShort: return "flat_store_short";
case Opcode::FlatStoreDword: return "flat_store_dword";
case Opcode::FlatStoreDwordx2: return "flat_store_dwordx2";
case Opcode::FlatStoreDwordx3: return "flat_store_dwordx3";
case Opcode::FlatStoreDwordx4: return "flat_store_dwordx4";
case Opcode::DsAddU32: return "ds_add_u32";
case Opcode::DsAddRtnU32: return "ds_add_rtn_u32";
case Opcode::DsSubU32: return "ds_sub_u32";
case Opcode::DsSubRtnU32: return "ds_sub_rtn_u32";
case Opcode::DsMinI32: return "ds_min_i32";
case Opcode::DsMinRtnI32: return "ds_min_rtn_i32";
case Opcode::DsMaxI32: return "ds_max_i32";
case Opcode::DsMaxRtnI32: return "ds_max_rtn_i32";
case Opcode::DsMinU32: return "ds_min_u32";
case Opcode::DsMinRtnU32: return "ds_min_rtn_u32";
case Opcode::DsMaxU32: return "ds_max_u32";
case Opcode::DsMaxRtnU32: return "ds_max_rtn_u32";
case Opcode::DsAndB32: return "ds_and_b32";
case Opcode::DsAndRtnB32: return "ds_and_rtn_b32";
case Opcode::DsOrB32: return "ds_or_b32";
case Opcode::DsOrRtnB32: return "ds_or_rtn_b32";
case Opcode::DsXorB32: return "ds_xor_b32";
case Opcode::DsXorRtnB32: return "ds_xor_rtn_b32";
case Opcode::DsWrxchgRtnB32: return "ds_wrxchg_rtn_b32";
case Opcode::DsMinF32: return "ds_min_f32";
case Opcode::DsMaxF32: return "ds_max_f32";
case Opcode::DsSwizzleB32: return "ds_swizzle_b32";
case Opcode::DsConsume: return "ds_consume";
case Opcode::DsAppend: return "ds_append";
case Opcode::DsReadSbyte: return "ds_read_i8";
case Opcode::DsReadUbyte: return "ds_read_u8";
case Opcode::DsReadSshort: return "ds_read_i16";
case Opcode::DsReadUshort: return "ds_read_u16";
case Opcode::DsRead2B32: return "ds_read2_b32";
case Opcode::DsReadB32: return "ds_read_b32";
case Opcode::DsReadB64: return "ds_read_b64";
case Opcode::DsRead2B64: return "ds_read2_b64";
case Opcode::DsRead2St64B64: return "ds_read2st64_b64";
case Opcode::DsReadB96: return "ds_read_b96";
case Opcode::DsReadB128: return "ds_read_b128";
case Opcode::DsWriteByte: return "ds_write_b8";
case Opcode::DsWriteShort: return "ds_write_b16";
case Opcode::DsWrite2B32: return "ds_write2_b32";
case Opcode::DsWrite2St64B32: return "ds_write2st64_b32";
case Opcode::DsWrite2B64: return "ds_write2_b64";
case Opcode::DsWrite2St64B64: return "ds_write2st64_b64";
case Opcode::DsWriteB32: return "ds_write_b32";
case Opcode::DsWriteB64: return "ds_write_b64";
case Opcode::DsWriteB96: return "ds_write_b96";
case Opcode::DsWriteB128: return "ds_write_b128";
case Opcode::DsWriteAddtidB32: return "ds_write_addtid_b32";
case Opcode::DsReadAddtidB32: return "ds_read_addtid_b32";
case Opcode::ImageGetResinfo: return "image_get_resinfo";
case Opcode::ImageGetLod: return "image_get_lod";
case Opcode::ImageLoad: return "image_load";
case Opcode::ImageLoadMip: return "image_load_mip";
case Opcode::ImageStore: return "image_store";
case Opcode::ImageStoreMip: return "image_store_mip";
case Opcode::ImageAtomicAdd: return "image_atomic_add";
case Opcode::ImageAtomicUMin: return "image_atomic_umin";
case Opcode::ImageAtomicUMax: return "image_atomic_umax";
case Opcode::ImageAtomicAnd: return "image_atomic_and";
case Opcode::ImageAtomicOr: return "image_atomic_or";
case Opcode::ImageAtomicXor: return "image_atomic_xor";
case Opcode::ImageSample: return "image_sample";
case Opcode::ImageGather4Lz: return "image_gather4_lz";
case Opcode::ImageGather4C: return "image_gather4_c";
case Opcode::ImageGather4CLz: return "image_gather4_c_lz";
case Opcode::ImageGather4LzO: return "image_gather4_lz_o";
case Opcode::ImageGather4CO: return "image_gather4_c_o";
case Opcode::ImageGather4CLzO: return "image_gather4_c_lz_o";
case Opcode::ImageGather4H: return "image_gather4h";
case Opcode::VInterpP1F32: return "v_interp_p1_f32";
case Opcode::VInterpP2F32: return "v_interp_p2_f32";
case Opcode::VInterpMovF32: return "v_interp_mov_f32";
case Opcode::SNop: return "s_nop";
case Opcode::SWaitcnt: return "s_waitcnt";
case Opcode::SBarrier: return "s_barrier";
case Opcode::SBranch: return "s_branch";
case Opcode::SCbranchScc0: return "s_cbranch_scc0";
case Opcode::SCbranchScc1: return "s_cbranch_scc1";
case Opcode::SCbranchVccz: return "s_cbranch_vccz";
case Opcode::SCbranchVccnz: return "s_cbranch_vccnz";
case Opcode::SCbranchExecz: return "s_cbranch_execz";
case Opcode::SCbranchExecnz: return "s_cbranch_execnz";
case Opcode::SSendmsg: return "s_sendmsg";
case Opcode::SSetregB32: return "s_setreg_b32";
case Opcode::SSleep: return "s_sleep";
case Opcode::STtraceData: return "s_ttracedata";
case Opcode::SInstPrefetch: return "s_inst_prefetch";
case Opcode::SEndpgm: return "s_endpgm";
case Opcode::Exp: return "exp";
case Opcode::Unsupported: return "unsupported";
default: return "unknown";
}
}
std::string OperandToString(const Operand& operand) {
std::string text;
switch (operand.kind) {
case OperandKind::LiteralConstant: text = fmt::format("0x{:08x}", operand.value); break;
case OperandKind::IntegerInlineConstant:
text = fmt::format("{}", operand.signed_val);
break;
case OperandKind::FloatInlineConstant: text = fmt::format("{:f}", operand.float_val); break;
case OperandKind::Sgpr: text = fmt::format("s{}", operand.reg); break;
case OperandKind::Vgpr: text = fmt::format("v{}", operand.reg); break;
case OperandKind::VccLo: text = "vcc_lo"; break;
case OperandKind::VccHi: text = "vcc_hi"; break;
case OperandKind::VccZ: text = "vccz"; break;
case OperandKind::ExecLo: text = "exec_lo"; break;
case OperandKind::ExecHi: text = "exec_hi"; break;
case OperandKind::ExecZ: text = "execz"; break;
case OperandKind::Scc: text = "scc"; break;
case OperandKind::M0: text = "m0"; break;
case OperandKind::PopsExitingWaveId: text = "pops_exiting_wave_id"; break;
case OperandKind::Null: text = "null"; break;
default: text = "unknown"; break;
}
if (operand.sdwa_sel != 6 || operand.sdwa_sext) {
text += fmt::format(".sdwa(sel={},sext={})", operand.sdwa_sel, operand.sdwa_sext ? 1u : 0u);
}
if (operand.absolute) {
text += ".abs";
}
if (operand.negate) {
text += ".neg";
}
if (operand.op_sel || operand.op_sel_hi || operand.negate_hi) {
text += fmt::format(".opsel(lo={},hi={},neghi={})", operand.op_sel ? 1u : 0u,
operand.op_sel_hi ? 1u : 0u, operand.negate_hi ? 1u : 0u);
}
if (operand.omod != 0) {
text += fmt::format(".omod({})", operand.omod);
}
if (operand.clamp) {
text += ".clamp";
}
if (operand.dpp) {
text += fmt::format(".dpp(ctrl=0x{:x},fi={},bc={})", operand.dpp_ctrl,
operand.dpp_fetch_inactive ? 1u : 0u, operand.dpp_bound_ctrl ? 1u : 0u);
}
return text;
}
std::string InstructionToString(const Instruction& inst) {
if (inst.opcode == Opcode::Unsupported) {
return fmt::format("0x{:08x}: unsupported family={} opcode=0x{:02x} raw=[{}] reason={}",
inst.pc, FamilyToString(inst.family).c_str(), inst.opcode_id,
RawWordsToString(inst).c_str(), inst.unsupported_reason.c_str());
}
switch (inst.opcode) {
case Opcode::SMovB32:
case Opcode::SMovB64:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}, {}", inst.pc,
OpcodeToString(inst.opcode).c_str(),
OperandToString(inst.dst).c_str(),
OperandToString(inst.src0).c_str()));
case Opcode::SAbsI32:
case Opcode::SBrevB32:
case Opcode::SBcnt1I32B32:
case Opcode::SFf1I32B32:
case Opcode::SNotB64:
case Opcode::SWqmB64:
case Opcode::SAndSaveexecB32:
case Opcode::SAndn1SaveexecB32:
case Opcode::SAndSaveexecB64:
case Opcode::SOrn2SaveexecB64:
case Opcode::SAndn1SaveexecB64:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}, {}", inst.pc,
OpcodeToString(inst.opcode).c_str(),
OperandToString(inst.dst).c_str(),
OperandToString(inst.src0).c_str()));
case Opcode::SGetpcB64:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: s_getpc_b64 {}", inst.pc,
OperandToString(inst.dst).c_str()));
case Opcode::SSetpcB64:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: s_setpc_b64 {}", inst.pc,
OperandToString(inst.src0).c_str()));
case Opcode::SSetregB32:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: s_setreg_b32 {}, {}", inst.pc,
OperandToString(inst.src0).c_str(),
OperandToString(inst.src1).c_str()));
case Opcode::SNop:
case Opcode::SWaitcnt:
case Opcode::SSleep:
case Opcode::SSendmsg:
case Opcode::STtraceData:
case Opcode::SInstPrefetch:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}", inst.pc,
OpcodeToString(inst.opcode).c_str(),
OperandToString(inst.src0).c_str()));
case Opcode::SBarrier:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: s_barrier", inst.pc));
case Opcode::VNop:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: v_nop", inst.pc));
case Opcode::SEndpgm:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: s_endpgm", inst.pc));
case Opcode::SBranch:
case Opcode::SCbranchScc0:
case Opcode::SCbranchScc1:
case Opcode::SCbranchVccz:
case Opcode::SCbranchVccnz:
case Opcode::SCbranchExecz:
case Opcode::SCbranchExecnz:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} 0x{:08x}", inst.pc,
OpcodeToString(inst.opcode).c_str(),
inst.branch_target));
case Opcode::Exp: return WithUnsupportedReason(inst, FormatExp(inst));
case Opcode::ImageSample:
case Opcode::ImageStore:
case Opcode::ImageStoreMip:
case Opcode::ImageAtomicAdd:
case Opcode::ImageAtomicUMin:
case Opcode::ImageAtomicUMax:
case Opcode::ImageAtomicAnd:
case Opcode::ImageAtomicOr:
case Opcode::ImageAtomicXor:
case Opcode::ImageLoad:
case Opcode::ImageLoadMip:
case Opcode::ImageGetResinfo:
case Opcode::ImageGetLod:
case Opcode::ImageGather4Lz:
case Opcode::ImageGather4C:
case Opcode::ImageGather4CLz:
case Opcode::ImageGather4LzO:
case Opcode::ImageGather4CO:
case Opcode::ImageGather4CLzO:
case Opcode::ImageGather4H: return WithUnsupportedReason(inst, FormatMimg(inst));
case Opcode::SLoadDword:
case Opcode::SLoadDwordx2:
case Opcode::SLoadDwordx4:
case Opcode::SLoadDwordx8:
case Opcode::SLoadDwordx16:
case Opcode::SBufferLoadDword:
case Opcode::SBufferLoadDwordx2:
case Opcode::SBufferLoadDwordx4:
case Opcode::SBufferLoadDwordx8:
case Opcode::SBufferLoadDwordx16:
case Opcode::BufferLoadFormatX:
case Opcode::BufferLoadFormatXy:
case Opcode::BufferLoadFormatXyz:
case Opcode::BufferLoadFormatXyzw:
case Opcode::BufferStoreFormatX:
case Opcode::BufferStoreFormatXy:
case Opcode::BufferStoreFormatXyz:
case Opcode::BufferStoreFormatXyzw:
case Opcode::BufferLoadUbyte:
case Opcode::BufferLoadUshort:
case Opcode::BufferLoadDword:
case Opcode::BufferLoadDwordx2:
case Opcode::BufferLoadDwordx3:
case Opcode::BufferLoadDwordx4:
case Opcode::BufferStoreByte:
case Opcode::BufferStoreShort:
case Opcode::BufferStoreDword:
case Opcode::BufferStoreDwordx2:
case Opcode::BufferStoreDwordx3:
case Opcode::BufferStoreDwordx4:
case Opcode::TBufferLoadFormatX:
case Opcode::TBufferLoadFormatXy:
case Opcode::TBufferLoadFormatXyz:
case Opcode::TBufferLoadFormatXyzw:
case Opcode::TBufferStoreFormatX:
case Opcode::TBufferStoreFormatXy:
case Opcode::TBufferStoreFormatXyz:
case Opcode::TBufferStoreFormatXyzw:
case Opcode::BufferAtomicSwap:
case Opcode::BufferAtomicAdd:
case Opcode::BufferAtomicSub:
case Opcode::BufferAtomicSMin:
case Opcode::BufferAtomicUMin:
case Opcode::BufferAtomicSMax:
case Opcode::BufferAtomicUMax:
case Opcode::BufferAtomicAnd:
case Opcode::BufferAtomicOr:
case Opcode::BufferAtomicXor:
case Opcode::BufferLoadSbyte:
case Opcode::BufferLoadSshort:
case Opcode::FlatLoadUbyte:
case Opcode::FlatLoadSbyte:
case Opcode::FlatLoadUshort:
case Opcode::FlatLoadSshort:
case Opcode::FlatLoadDword:
case Opcode::FlatLoadDwordx2:
case Opcode::FlatLoadDwordx3:
case Opcode::FlatLoadDwordx4:
case Opcode::FlatStoreByte:
case Opcode::FlatStoreShort:
case Opcode::FlatStoreDword:
case Opcode::FlatStoreDwordx2:
case Opcode::FlatStoreDwordx3:
case Opcode::FlatStoreDwordx4:
case Opcode::DsAddU32:
case Opcode::DsAddRtnU32:
case Opcode::DsSubU32:
case Opcode::DsSubRtnU32:
case Opcode::DsMinI32:
case Opcode::DsMinRtnI32:
case Opcode::DsMaxI32:
case Opcode::DsMaxRtnI32:
case Opcode::DsMinU32:
case Opcode::DsMinRtnU32:
case Opcode::DsMaxU32:
case Opcode::DsMaxRtnU32:
case Opcode::DsAndB32:
case Opcode::DsAndRtnB32:
case Opcode::DsOrB32:
case Opcode::DsOrRtnB32:
case Opcode::DsXorB32:
case Opcode::DsXorRtnB32:
case Opcode::DsWrxchgRtnB32:
case Opcode::DsMinF32:
case Opcode::DsMaxF32:
case Opcode::DsSwizzleB32:
case Opcode::DsReadSbyte:
case Opcode::DsReadUbyte:
case Opcode::DsReadSshort:
case Opcode::DsReadUshort:
case Opcode::DsRead2B32:
case Opcode::DsReadB32:
case Opcode::DsReadB64:
case Opcode::DsRead2B64:
case Opcode::DsRead2St64B64:
case Opcode::DsReadB96:
case Opcode::DsReadB128:
case Opcode::DsWriteByte:
case Opcode::DsWriteShort:
case Opcode::DsWrite2B32:
case Opcode::DsWrite2St64B32:
case Opcode::DsWrite2B64:
case Opcode::DsWrite2St64B64:
case Opcode::DsWriteB32:
case Opcode::DsWriteB64:
case Opcode::DsWriteB96:
case Opcode::DsWriteB128:
case Opcode::DsWriteAddtidB32:
case Opcode::DsReadAddtidB32: return WithUnsupportedReason(inst, FormatMemory(inst));
case Opcode::SCmpEqU32:
case Opcode::SCmpEqI32:
case Opcode::SCmpLgU32:
case Opcode::SCmpLgI32:
case Opcode::SCmpGtU32:
case Opcode::SCmpGtI32:
case Opcode::SCmpGeU32:
case Opcode::SCmpGeI32:
case Opcode::SCmpLtU32:
case Opcode::SCmpLtI32:
case Opcode::SCmpLeU32:
case Opcode::SCmpLeI32:
case Opcode::SBitcmp0B32:
case Opcode::SBitcmp1B32:
case Opcode::SCmpLgU64:
return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}, {}", inst.pc,
OpcodeToString(inst.opcode).c_str(),
OperandToString(inst.src0).c_str(),
OperandToString(inst.src1).c_str()));
default: return WithUnsupportedReason(inst, FormatBinary(inst));
}
}
std::string ProgramToString(const Program& program) {
std::string text;
for (const auto& inst: program.instructions) {
text += InstructionToString(inst);
text += "\n";
}
return text;
}
} // namespace Libs::Graphics::ShaderRecompiler::Decoder