From 5bfdf4267e6669481816530eebcfdb75eeacac96 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 15 Aug 2026 21:38:44 +0200 Subject: [PATCH] Optimize decoder --- .../recompiler/frontend/decode/ExportOps.cpp | 25 +- .../recompiler/frontend/decode/ExportOps.h | 3 +- .../recompiler/frontend/decode/ImageOps.cpp | 69 ++---- .../recompiler/frontend/decode/ImageOps.h | 4 +- .../recompiler/frontend/decode/MemoryOps.cpp | 162 +++++-------- .../recompiler/frontend/decode/MemoryOps.h | 19 +- .../recompiler/frontend/decode/OpcodeTable.h | 51 ++-- .../frontend/decode/ScalarAluOps.cpp | 39 ++- .../recompiler/frontend/decode/ScalarAluOps.h | 4 +- .../frontend/decode/ShaderDecoder.cpp | 154 ++++++------ .../frontend/decode/ShaderDecoder.h | 13 +- .../frontend/decode/VectorAluOps.cpp | 228 ++++++++---------- .../recompiler/frontend/decode/VectorAluOps.h | 4 +- tests/ShaderRecompilerComputeTests.cpp | 18 +- tests/shaderCfgTests.cpp | 163 +++++++++---- 15 files changed, 471 insertions(+), 485 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ExportOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ExportOps.cpp index 02ae830b3..f0d68c1ba 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ExportOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ExportOps.cpp @@ -1,18 +1,9 @@ #include "graphics/shader/recompiler/frontend/decode/ExportOps.h" -#include - namespace Libs::Graphics::ShaderRecompiler::Decoder { -bool DecodeExp(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated EXP instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeExp(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t target = (word0 >> 4u) & 0x3fu; @@ -31,12 +22,10 @@ bool DecodeExp(uint32_t pc, std::span code, uint32_t word_index, inst.exp.vm = ((word0 >> 12u) & 1u) != 0; SetRawWords(inst, code, word_index, 2); - if (!DecodeVectorGpr(word1 & 0xffu, inst.src0, error) || - !DecodeVectorGpr((word1 >> 8u) & 0xffu, inst.src1, error) || - !DecodeVectorGpr((word1 >> 16u) & 0xffu, inst.src2, error) || - !DecodeVectorGpr((word1 >> 24u) & 0xffu, inst.src3, error)) { - return false; - } + DecodeVectorGpr(word1 & 0xffu, inst.src0); + DecodeVectorGpr((word1 >> 8u) & 0xffu, inst.src1); + DecodeVectorGpr((word1 >> 16u) & 0xffu, inst.src2); + DecodeVectorGpr((word1 >> 24u) & 0xffu, inst.src3); if (target == 0x14u && inst.exp.done && en == 0x1u) { inst.src_count = 1u; @@ -46,8 +35,6 @@ bool DecodeExp(uint32_t pc, std::span code, uint32_t word_index, if (en == 0u) { inst.src_count = 0; } - - return true; } } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/ExportOps.h b/src/graphics/shader/recompiler/frontend/decode/ExportOps.h index f0faee33a..051d84654 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ExportOps.h +++ b/src/graphics/shader/recompiler/frontend/decode/ExportOps.h @@ -5,8 +5,7 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder { -bool DecodeExp(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error); +void DecodeExp(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst); } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp index 9e1c29b6c..2f2ddd1ae 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp @@ -1,21 +1,21 @@ #include "graphics/shader/recompiler/frontend/decode/ImageOps.h" +#include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h" + #include -#include -#include namespace Libs::Graphics::ShaderRecompiler::Decoder { namespace { struct MimgSampleInfo { - uint32_t opcode = 0; + uint32_t encoding = 0; const char* name = nullptr; uint32_t flags = 0; uint32_t address_components = 3; }; struct MimgGatherInfo { - uint32_t opcode = 0; + uint32_t encoding = 0; const char* name = nullptr; Opcode decoded = Opcode::Unsupported; uint32_t flags = 0; @@ -23,9 +23,9 @@ struct MimgGatherInfo { }; struct MimgAtomicInfo { - uint32_t opcode = 0; - const char* name = nullptr; - Opcode decoded = Opcode::Unsupported; + uint32_t encoding = 0; + const char* name = nullptr; + Opcode decoded = Opcode::Unsupported; }; constexpr ImageDimension DecodeImageDimension(uint32_t dim) { @@ -88,7 +88,7 @@ constexpr MimgSampleInfo SampleInfo(uint32_t opcode, const char* name, uint32_t return {opcode, name, flags, ImageSampleAddressComponents(flags, ImageDimension::Dim2D)}; } -constexpr MimgSampleInfo MIMG_SAMPLE_OPS[] = { +constexpr MimgSampleInfo MIMG_SAMPLE_OPCODE_LIST[] = { SampleInfo(0x20u, "image_sample", 0), SampleInfo(0x21u, "image_sample_cl", ImageSampleFlagLodClamp), SampleInfo(0x22u, "image_sample_d", ImageSampleFlagDerivative), @@ -177,7 +177,7 @@ constexpr MimgSampleInfo MIMG_SAMPLE_OPS[] = { ImageSampleFlagOffset), }; -constexpr MimgGatherInfo MIMG_GATHER_OPS[] = { +constexpr MimgGatherInfo MIMG_GATHER_OPCODE_LIST[] = { {0x47u, "image_gather4_lz", Opcode::ImageGather4Lz, ImageSampleFlagLevelZero, 2u}, {0x48u, "image_gather4_c", Opcode::ImageGather4C, ImageSampleFlagCompare, 3u}, {0x4fu, "image_gather4_c_lz", Opcode::ImageGather4CLz, @@ -191,7 +191,7 @@ constexpr MimgGatherInfo MIMG_GATHER_OPS[] = { {0x61u, "image_gather4h", Opcode::ImageGather4H, ImageSampleFlagGatherHorizontal, 2u}, }; -constexpr MimgAtomicInfo MIMG_ATOMIC_OPS[] = { +constexpr MimgAtomicInfo MIMG_ATOMIC_OPCODE_LIST[] = { {0x11u, "image_atomic_add", Opcode::ImageAtomicAdd}, {0x15u, "image_atomic_umin", Opcode::ImageAtomicUMin}, {0x17u, "image_atomic_umax", Opcode::ImageAtomicUMax}, @@ -200,31 +200,20 @@ constexpr MimgAtomicInfo MIMG_ATOMIC_OPS[] = { {0x1au, "image_atomic_xor", Opcode::ImageAtomicXor}, }; +constexpr auto MIMG_SAMPLE_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_SAMPLE_OPCODE_LIST); +constexpr auto MIMG_GATHER_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_GATHER_OPCODE_LIST); +constexpr auto MIMG_ATOMIC_OPS = Detail::MakeOpcodeTable<0x100>(MIMG_ATOMIC_OPCODE_LIST); + const MimgSampleInfo* LookupSample(uint32_t opcode) { - for (const auto& info: MIMG_SAMPLE_OPS) { - if (info.opcode == opcode) { - return &info; - } - } - return nullptr; + return Detail::FindOpcode(MIMG_SAMPLE_OPS, opcode); } const MimgGatherInfo* LookupGather(uint32_t opcode) { - for (const auto& info: MIMG_GATHER_OPS) { - if (info.opcode == opcode) { - return &info; - } - } - return nullptr; + return Detail::FindOpcode(MIMG_GATHER_OPS, opcode); } const MimgAtomicInfo* LookupAtomic(uint32_t opcode) { - for (const auto& info: MIMG_ATOMIC_OPS) { - if (info.opcode == opcode) { - return &info; - } - } - return nullptr; + return Detail::FindOpcode(MIMG_ATOMIC_OPS, opcode); } Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const MimgGatherInfo* gather, @@ -297,27 +286,14 @@ bool IsSingleDmaskBit(uint32_t dmask) { } // namespace -bool DecodeMimg(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated MIMG instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = ((word0 >> 18u) & 0x7fu) | ((word0 & 1u) << 7u); const uint32_t nsa_dwords = (word0 >> 1u) & 0x3u; const auto dimension = DecodeImageDimension((word0 >> 3u) & 0x7u); const uint32_t word_count = 2u + nsa_dwords; - if (word_index + word_count > code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated MIMG NSA instruction at pc 0x{:08x}", pc); - } - return false; - } const uint32_t vdata = (word1 >> 8u) & 0xffu; const uint32_t vaddr = word1 & 0xffu; @@ -344,7 +320,7 @@ bool DecodeMimg(uint32_t pc, std::span code, uint32_t word_index } inst.image_dimension = dimension; inst.image_nsa_dwords = nsa_dwords; - for (uint32_t i = 0; i < nsa_dwords * 4u && i < MaxImageNsaAddressComponents; i++) { + for (uint32_t i = 0; i < nsa_dwords * 4u; i++) { inst.image_nsa_addr[i] = (code[word_index + 2u + i / 4u] >> ((i % 4u) * 8u)) & 0xffu; } inst.image_address_components = @@ -359,12 +335,11 @@ bool DecodeMimg(uint32_t pc, std::span code, uint32_t word_index "MIMG image gather requires exactly one dmask bit"); } - DecodeVectorGpr(vdata, inst.dst, nullptr); - DecodeVectorGpr(vaddr, inst.src0, nullptr); + DecodeVectorGpr(vdata, inst.dst); + DecodeVectorGpr(vaddr, inst.src0); DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr); DecodeScalarSource(ssamp * 4u, pc, inst.src2, nullptr); inst.src_count = 3; - return true; } const char* MimgSampleOpcodeName(uint32_t opcode) { diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.h b/src/graphics/shader/recompiler/frontend/decode/ImageOps.h index 887789941..8fb0c1d57 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.h +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.h @@ -5,8 +5,8 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder { -bool DecodeMimg(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error); +void DecodeMimg(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); const char* MimgSampleOpcodeName(uint32_t opcode); diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index bd1e4b459..163914a19 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -1,13 +1,12 @@ #include "graphics/shader/recompiler/frontend/decode/MemoryOps.h" -#include -#include +#include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h" namespace Libs::Graphics::ShaderRecompiler::Decoder { namespace { struct MemoryOpcodeInfo { - uint32_t opcode = 0; + uint32_t encoding = 0; Opcode decoded = Opcode::Unsupported; uint32_t data_dwords = 1; uint32_t data_bits = 32; @@ -16,7 +15,7 @@ struct MemoryOpcodeInfo { bool formatted = false; }; -constexpr MemoryOpcodeInfo SMEM_OPS[] = { +constexpr MemoryOpcodeInfo SMEM_OPCODE_LIST[] = { {0x00u, Opcode::SLoadDword, 1, 32}, {0x01u, Opcode::SLoadDwordx2, 2, 32}, {0x02u, Opcode::SLoadDwordx4, 4, 32}, {0x03u, Opcode::SLoadDwordx8, 8, 32}, {0x04u, Opcode::SLoadDwordx16, 16, 32}, {0x08u, Opcode::SBufferLoadDword, 1, 32}, @@ -24,7 +23,7 @@ constexpr MemoryOpcodeInfo SMEM_OPS[] = { {0x0bu, Opcode::SBufferLoadDwordx8, 8, 32}, {0x0cu, Opcode::SBufferLoadDwordx16, 16, 32}, }; -constexpr MemoryOpcodeInfo MUBUF_OPS[] = { +constexpr MemoryOpcodeInfo MUBUF_OPCODE_LIST[] = { {0x00u, Opcode::BufferLoadFormatX, 1, 32, false, false, true}, {0x01u, Opcode::BufferLoadFormatXy, 2, 32, false, false, true}, {0x02u, Opcode::BufferLoadFormatXyz, 3, 32, false, false, true}, @@ -61,7 +60,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPS[] = { {0x40u, Opcode::BufferAtomicFMax, 1, 32}, }; -constexpr MemoryOpcodeInfo MTBUF_OPS[] = { +constexpr MemoryOpcodeInfo MTBUF_OPCODE_LIST[] = { {0x00u, Opcode::TBufferLoadFormatX, 1, 32, false, true, true}, {0x01u, Opcode::TBufferLoadFormatXy, 2, 32, false, true, true}, {0x02u, Opcode::TBufferLoadFormatXyz, 3, 32, false, true, true}, @@ -72,7 +71,7 @@ constexpr MemoryOpcodeInfo MTBUF_OPS[] = { {0x07u, Opcode::TBufferStoreFormatXyzw, 4, 32, false, true, true}, }; -constexpr MemoryOpcodeInfo FLAT_OPS[] = { +constexpr MemoryOpcodeInfo FLAT_OPCODE_LIST[] = { {0x08u, Opcode::FlatLoadUbyte, 1, 8}, {0x09u, Opcode::FlatLoadSbyte, 1, 8, true}, {0x0au, Opcode::FlatLoadUshort, 1, 16}, {0x0bu, Opcode::FlatLoadSshort, 1, 16, true}, {0x0cu, Opcode::FlatLoadDword, 1, 32}, {0x0du, Opcode::FlatLoadDwordx2, 2, 32}, @@ -82,7 +81,7 @@ constexpr MemoryOpcodeInfo FLAT_OPS[] = { {0x1eu, Opcode::FlatStoreDwordx4, 4, 32}, {0x1fu, Opcode::FlatStoreDwordx3, 3, 32}, }; -constexpr MemoryOpcodeInfo DS_OPS[] = { +constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = { {0x00u, Opcode::DsAddU32, 1, 32}, {0x01u, Opcode::DsSubU32, 1, 32}, {0x05u, Opcode::DsMinI32, 1, 32}, {0x06u, Opcode::DsMaxI32, 1, 32}, {0x07u, Opcode::DsMinU32, 1, 32}, {0x08u, Opcode::DsMaxU32, 1, 32}, @@ -109,15 +108,11 @@ constexpr MemoryOpcodeInfo DS_OPS[] = { {0xfeu, Opcode::DsReadB96, 3, 32}, {0xffu, Opcode::DsReadB128, 4, 32}, }; -const MemoryOpcodeInfo* LookupMemoryOpcode(const MemoryOpcodeInfo* ops, uint32_t count, - uint32_t opcode) { - for (uint32_t i = 0; i < count; i++) { - if (ops[i].opcode == opcode) { - return &ops[i]; - } - } - return nullptr; -} +constexpr auto SMEM_OPS = Detail::MakeOpcodeTable<0x100>(SMEM_OPCODE_LIST); +constexpr auto MUBUF_OPS = Detail::MakeOpcodeTable<0x100>(MUBUF_OPCODE_LIST); +constexpr auto MTBUF_OPS = Detail::MakeOpcodeTable<0x10>(MTBUF_OPCODE_LIST); +constexpr auto FLAT_OPS = Detail::MakeOpcodeTable<0x80>(FLAT_OPCODE_LIST); +constexpr auto DS_OPS = Detail::MakeOpcodeTable<0x100>(DS_OPCODE_LIST); uint32_t SignExtendU32(uint32_t value, uint32_t bits) { if (bits == 0u || bits >= 32u) { @@ -217,15 +212,8 @@ bool IsFlatStoreOpcode(Opcode opcode) { } // namespace -bool DecodeSmem(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated SMEM instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeSmem(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = (word0 >> 18u) & 0xffu; @@ -233,15 +221,14 @@ bool DecodeSmem(uint32_t pc, std::span code, uint32_t word_index const uint32_t sbase = word0 & 0x3fu; const uint32_t soffset = (word1 >> 25u) & 0x7fu; - inst.pc = pc; - inst.word = word0; - inst.word_count = 2; - inst.offset = SignExtendU32(word1 & 0x1fffffu, 21u); - inst.glc = ((word0 >> 16u) & 1u) != 0; - inst.family = Family::SMEM; - inst.opcode_id = opcode; - const auto* info = - LookupMemoryOpcode(SMEM_OPS, static_cast(std::size(SMEM_OPS)), opcode); + inst.pc = pc; + inst.word = word0; + inst.word_count = 2; + inst.offset = SignExtendU32(word1 & 0x1fffffu, 21u); + inst.glc = ((word0 >> 16u) & 1u) != 0; + inst.family = Family::SMEM; + inst.opcode_id = opcode; + const auto* info = Detail::FindOpcode(SMEM_OPS, opcode); ApplyMemoryInfo(inst, info); SetRawWords(inst, code, word_index, 2); if (inst.opcode == Opcode::Unsupported) { @@ -254,18 +241,10 @@ bool DecodeSmem(uint32_t pc, std::span code, uint32_t word_index DecodeScalarSource(sbase * 2u, pc, inst.src0, nullptr); DecodeScalarSource(soffset, pc, inst.src1, nullptr); inst.src_count = 2; - return true; } -bool DecodeMubuf(uint32_t pc, std::span code, uint32_t word_index, - Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated MUBUF instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = ((word0 >> 18u) & 0x7fu) | (((word0 >> 25u) & 1u) << 7u); @@ -274,41 +253,32 @@ bool DecodeMubuf(uint32_t pc, std::span code, uint32_t word_inde const uint32_t srsrc = (word1 >> 16u) & 0x1fu; const uint32_t soffset = (word1 >> 24u) & 0xffu; - inst.pc = pc; - inst.word = word0; - inst.word_count = 2; - inst.offset = word0 & 0xfffu; - inst.idxen = ((word0 >> 13u) & 1u) != 0; - inst.offen = ((word0 >> 12u) & 1u) != 0; - inst.glc = ((word0 >> 14u) & 1u) != 0; - inst.slc = ((word1 >> 22u) & 1u) != 0; - inst.family = Family::MUBUF; - inst.opcode_id = opcode; - const auto* info = - LookupMemoryOpcode(MUBUF_OPS, static_cast(std::size(MUBUF_OPS)), opcode); + inst.pc = pc; + inst.word = word0; + inst.word_count = 2; + inst.offset = word0 & 0xfffu; + inst.idxen = ((word0 >> 13u) & 1u) != 0; + inst.offen = ((word0 >> 12u) & 1u) != 0; + inst.glc = ((word0 >> 14u) & 1u) != 0; + inst.slc = ((word1 >> 22u) & 1u) != 0; + inst.family = Family::MUBUF; + inst.opcode_id = opcode; + const auto* info = Detail::FindOpcode(MUBUF_OPS, opcode); ApplyMemoryInfo(inst, info); SetRawWords(inst, code, word_index, 2); if (inst.opcode == Opcode::Unsupported) { MarkMemoryUnsupported(inst, Family::MUBUF, opcode, "MUBUF opcode is not implemented"); } - DecodeVectorGpr(vdata, inst.dst, nullptr); - DecodeVectorGpr(vaddr, inst.src0, nullptr); + DecodeVectorGpr(vdata, inst.dst); + DecodeVectorGpr(vaddr, inst.src0); DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr); DecodeScalarSource(soffset, pc, inst.src2, nullptr); inst.src_count = 3; - return true; } -bool DecodeMtbuf(uint32_t pc, std::span code, uint32_t word_index, - Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated MTBUF instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeMtbuf(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = ((word0 >> 16u) & 0x7u) | (((word1 >> 21u) & 1u) << 3u); @@ -331,31 +301,22 @@ bool DecodeMtbuf(uint32_t pc, std::span code, uint32_t word_inde inst.opcode_id = opcode; inst.data_format = dfmt; inst.number_format = nfmt; - const auto* info = - LookupMemoryOpcode(MTBUF_OPS, static_cast(std::size(MTBUF_OPS)), opcode); + const auto* info = Detail::FindOpcode(MTBUF_OPS, opcode); ApplyMemoryInfo(inst, info); SetRawWords(inst, code, word_index, 2); if (inst.opcode == Opcode::Unsupported) { MarkMemoryUnsupported(inst, Family::MTBUF, opcode, "MTBUF opcode is not implemented"); } - DecodeVectorGpr(vdata, inst.dst, nullptr); - DecodeVectorGpr(vaddr, inst.src0, nullptr); + DecodeVectorGpr(vdata, inst.dst); + DecodeVectorGpr(vaddr, inst.src0); DecodeScalarSource(srsrc * 4u, pc, inst.src1, nullptr); DecodeScalarSource(soffset, pc, inst.src2, nullptr); inst.src_count = 3; - return true; } -bool DecodeFlat(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated FLAT instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeFlat(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t offset = word0 & 0xfffu; @@ -377,42 +338,32 @@ bool DecodeFlat(uint32_t pc, std::span code, uint32_t word_index inst.family = Family::FLAT; inst.opcode_id = opcode; inst.memory_segment = seg; - const auto* info = - LookupMemoryOpcode(FLAT_OPS, static_cast(std::size(FLAT_OPS)), opcode); + const auto* info = Detail::FindOpcode(FLAT_OPS, opcode); ApplyMemoryInfo(inst, info); SetRawWords(inst, code, word_index, 2); if (dlc != 0 || lds != 0 || inst.glc || inst.slc || seg == 3u) { SetUnsupported(inst, Family::FLAT, opcode, "FLAT modifiers or segment are not implemented"); - return true; + return; } if (inst.opcode == Opcode::Unsupported) { MarkMemoryUnsupported(inst, Family::FLAT, opcode, "FLAT opcode is not implemented"); - return true; + return; } - DecodeVectorGpr(IsFlatStoreOpcode(inst.opcode) ? data : vdst, inst.dst, nullptr); - DecodeVectorGpr(addr, inst.src0, nullptr); + DecodeVectorGpr(IsFlatStoreOpcode(inst.opcode) ? data : vdst, inst.dst); + DecodeVectorGpr(addr, inst.src0); inst.src_count = 1; if (seg == 0u || saddr == 0x7du || saddr == 0x7fu) { - DecodeVectorGpr(addr + 1u, inst.src1, nullptr); + DecodeVectorGpr(addr + 1u, inst.src1); inst.src_count = 2; } else { DecodeScalarSource(saddr, pc, inst.src1, nullptr); inst.src_count = 2; } - return true; } -bool DecodeDs(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated DS instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeDs(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst) { const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = (word0 >> 18u) & 0xffu; @@ -430,7 +381,7 @@ bool DecodeDs(uint32_t pc, std::span code, uint32_t word_index, inst.gds = ((word0 >> 17u) & 1u) != 0u; inst.family = Family::DS; inst.opcode_id = opcode; - const auto* info = LookupMemoryOpcode(DS_OPS, static_cast(std::size(DS_OPS)), opcode); + const auto* info = Detail::FindOpcode(DS_OPS, opcode); ApplyMemoryInfo(inst, info); SetRawWords(inst, code, word_index, 2); if (inst.opcode == Opcode::Unsupported) { @@ -466,12 +417,11 @@ bool DecodeDs(uint32_t pc, std::span code, uint32_t word_index, inst.secondary_offset = offset1 * 512u; } - DecodeVectorGpr(vdst, inst.dst, nullptr); - DecodeVectorGpr(addr, inst.src0, nullptr); - DecodeVectorGpr(data0, inst.src1, nullptr); - DecodeVectorGpr(data1, inst.src2, nullptr); + DecodeVectorGpr(vdst, inst.dst); + DecodeVectorGpr(addr, inst.src0); + DecodeVectorGpr(data0, inst.src1); + DecodeVectorGpr(data1, inst.src2); inst.src_count = DsSourceCount(inst.opcode); - return true; } } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.h b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.h index b7d6217ed..585b57d0d 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.h +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.h @@ -5,16 +5,15 @@ namespace Libs::Graphics::ShaderRecompiler::Decoder { -bool DecodeSmem(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error); -bool DecodeMubuf(uint32_t pc, std::span code, uint32_t word_index, - Instruction& inst, std::string* error); -bool DecodeMtbuf(uint32_t pc, std::span code, uint32_t word_index, - Instruction& inst, std::string* error); -bool DecodeFlat(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error); -bool DecodeDs(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error); +void DecodeSmem(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); +void DecodeMubuf(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); +void DecodeMtbuf(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); +void DecodeFlat(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); +void DecodeDs(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst); } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/OpcodeTable.h b/src/graphics/shader/recompiler/frontend/decode/OpcodeTable.h index e49581d4f..887a58787 100644 --- a/src/graphics/shader/recompiler/frontend/decode/OpcodeTable.h +++ b/src/graphics/shader/recompiler/frontend/decode/OpcodeTable.h @@ -3,7 +3,9 @@ #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" +#include #include +#include namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail { @@ -12,34 +14,43 @@ struct OpcodeMap { Opcode decoded = Opcode::Unknown; }; -template -constexpr const Entry* FindOpcode(const Entry (&table)[N], uint32_t encoding) { - for (const auto& entry: table) { - if (entry.encoding == encoding) { - return &entry; +template +struct OpcodeTable { + std::array entries = {}; + std::array indices = {}; +}; + +template +consteval auto MakeOpcodeTable(const Entry (&entries)[EntryCount]) { + static_assert(EntryCount < UINT16_MAX); + OpcodeTable table; + for (size_t i = 0; i < EntryCount; i++) { + const auto encoding = entries[i].encoding; + if (encoding >= EncodingCount || table.indices[encoding] != 0) { + throw "invalid opcode table"; } + table.entries[i] = entries[i]; + table.indices[encoding] = static_cast(i + 1); } - return nullptr; + return table; } -template -constexpr Opcode LookupOpcode(const Entry (&table)[N], uint32_t encoding) { +template +constexpr const Entry* FindOpcode(const OpcodeTable& table, + uint32_t encoding) { + if (table.indices[encoding] == 0) { + return nullptr; + } + return &table.entries[table.indices[encoding] - 1]; +} + +template +constexpr Opcode LookupOpcode(const OpcodeTable& table, + uint32_t encoding) { const auto* entry = FindOpcode(table, encoding); return entry != nullptr ? entry->decoded : Opcode::Unsupported; } -template -constexpr bool HasUniqueEncodings(const Entry (&table)[N]) { - for (size_t i = 0; i < N; i++) { - for (size_t j = i + 1; j < N; j++) { - if (table[i].encoding == table[j].encoding) { - return false; - } - } - } - return true; -} - } // namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail #endif // EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_DECOMPILER_OPCODETABLE_H_ diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index 8320a2fbb..b3530caa3 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -7,7 +7,7 @@ namespace { using Detail::OpcodeMap; -constexpr OpcodeMap SOP2_OPS[] = { +constexpr OpcodeMap SOP2_OPCODE_LIST[] = { {0x00u, Opcode::SAddU32}, {0x01u, Opcode::SSubU32}, {0x02u, Opcode::SAddI32}, {0x03u, Opcode::SSubI32}, {0x04u, Opcode::SAddcU32}, {0x05u, Opcode::SSubbU32}, {0x06u, Opcode::SMinI32}, {0x07u, Opcode::SMinU32}, {0x08u, Opcode::SMaxI32}, @@ -26,7 +26,7 @@ constexpr OpcodeMap SOP2_OPS[] = { {0x35u, Opcode::SMulHiU32}, }; -constexpr OpcodeMap SOP1_OPS[] = { +constexpr OpcodeMap SOP1_OPCODE_LIST[] = { {0x03u, Opcode::SMovB32}, {0x04u, Opcode::SMovB64}, {0x07u, Opcode::SNotB32}, {0x08u, Opcode::SNotB64}, {0x0au, Opcode::SWqmB64}, {0x0bu, Opcode::SBrevB32}, @@ -40,7 +40,7 @@ constexpr OpcodeMap SOP1_OPS[] = { {0x3cu, Opcode::SAndSaveexecB32}, {0x44u, Opcode::SAndn1SaveexecB32}, }; -constexpr OpcodeMap SOPC_OPS[] = { +constexpr OpcodeMap SOPC_OPCODE_LIST[] = { {0x00u, Opcode::SCmpEqI32}, {0x01u, Opcode::SCmpLgI32}, {0x02u, Opcode::SCmpGtI32}, {0x03u, Opcode::SCmpGeI32}, {0x04u, Opcode::SCmpLtI32}, {0x05u, Opcode::SCmpLeI32}, {0x06u, Opcode::SCmpEqU32}, {0x07u, Opcode::SCmpLgU32}, {0x08u, Opcode::SCmpGtU32}, @@ -49,7 +49,7 @@ constexpr OpcodeMap SOPC_OPS[] = { {0x13u, Opcode::SCmpLgU64}, }; -constexpr OpcodeMap SOPK_OPS[] = { +constexpr OpcodeMap SOPK_OPCODE_LIST[] = { {0x00u, Opcode::SMovkI32}, {0x03u, Opcode::SCmpEqI32}, {0x04u, Opcode::SCmpLgI32}, {0x05u, Opcode::SCmpGtI32}, {0x06u, Opcode::SCmpGeI32}, {0x07u, Opcode::SCmpLtI32}, {0x08u, Opcode::SCmpLeI32}, {0x09u, Opcode::SCmpEqU32}, {0x0au, Opcode::SCmpLgU32}, @@ -59,7 +59,7 @@ constexpr OpcodeMap SOPK_OPS[] = { {0x19u, Opcode::SWaitcnt}, {0x1au, Opcode::SWaitcnt}, }; -constexpr OpcodeMap SOPP_OPS[] = { +constexpr OpcodeMap SOPP_OPCODE_LIST[] = { {0x00u, Opcode::SNop}, {0x01u, Opcode::SEndpgm}, {0x02u, Opcode::SBranch}, {0x04u, Opcode::SCbranchScc0}, {0x05u, Opcode::SCbranchScc1}, {0x06u, Opcode::SCbranchVccz}, {0x07u, Opcode::SCbranchVccnz}, {0x08u, Opcode::SCbranchExecz}, {0x09u, Opcode::SCbranchExecnz}, @@ -67,11 +67,12 @@ constexpr OpcodeMap SOPP_OPS[] = { {0x10u, Opcode::SSendmsg}, {0x12u, Opcode::STrap}, {0x16u, Opcode::STtraceData}, {0x20u, Opcode::SInstPrefetch}, }; -static_assert(Detail::HasUniqueEncodings(SOP1_OPS)); -static_assert(Detail::HasUniqueEncodings(SOP2_OPS)); -static_assert(Detail::HasUniqueEncodings(SOPK_OPS)); -static_assert(Detail::HasUniqueEncodings(SOPC_OPS)); -static_assert(Detail::HasUniqueEncodings(SOPP_OPS)); + +constexpr auto SOP1_OPS = Detail::MakeOpcodeTable<0x100>(SOP1_OPCODE_LIST); +constexpr auto SOP2_OPS = Detail::MakeOpcodeTable<0x80>(SOP2_OPCODE_LIST); +constexpr auto SOPK_OPS = Detail::MakeOpcodeTable<0x20>(SOPK_OPCODE_LIST); +constexpr auto SOPC_OPS = Detail::MakeOpcodeTable<0x80>(SOPC_OPCODE_LIST); +constexpr auto SOPP_OPS = Detail::MakeOpcodeTable<0x80>(SOPP_OPCODE_LIST); bool DecodeBinarySources(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, uint32_t ssrc0, uint32_t ssrc1, std::string* error) { @@ -80,7 +81,8 @@ bool DecodeBinarySources(uint32_t pc, std::span code, uint32_t w return false; } inst.src_count = 2; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } } // namespace @@ -114,7 +116,8 @@ bool DecodeSop1(uint32_t pc, std::span code, uint32_t word_index if (!DecodeScalarSource(ssrc0, pc, inst.src0, error)) { return false; } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; default: break; } @@ -123,7 +126,8 @@ bool DecodeSop1(uint32_t pc, std::span code, uint32_t word_index return false; } inst.src_count = 1; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } bool DecodeSop2(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, @@ -159,8 +163,6 @@ bool DecodeSopk(uint32_t pc, std::span code, uint32_t word_index const uint32_t sdst = (word >> 16u) & 0x7fu; const auto imm = static_cast(word & 0xffffu); - (void)error; - inst.pc = pc; inst.word = word; inst.family = Family::SOPK; @@ -234,14 +236,12 @@ bool DecodeSopc(uint32_t pc, std::span code, uint32_t word_index return DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1, error); } -bool DecodeSopp(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { +void DecodeSopp(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word = code[word_index]; const uint32_t opcode = (word >> 16u) & 0x7fu; const uint32_t simm = word & 0xffffu; - (void)error; - inst.pc = pc; inst.word = word; inst.family = Family::SOPP; @@ -265,7 +265,6 @@ bool DecodeSopp(uint32_t pc, std::span code, uint32_t word_index if (inst.opcode == Opcode::Unsupported) { SetUnsupported(inst, Family::SOPP, opcode, "SOPP control-flow opcode is not implemented"); } - return true; } } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.h b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.h index 4ec91c14a..bf943dc9f 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.h +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.h @@ -13,8 +13,8 @@ bool DecodeSopk(uint32_t pc, std::span code, uint32_t word_index std::string* error); bool DecodeSopc(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, std::string* error); -bool DecodeSopp(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, - std::string* error); +void DecodeSopp(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 10aeda7ab..d0b1cf8ba 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -9,7 +9,6 @@ #include #include #include -#include namespace Libs::Graphics::ShaderRecompiler::Decoder { namespace { @@ -111,7 +110,7 @@ std::string WithUnsupportedReason(const Instruction& inst, const std::string& te } 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()); + RawWordsToString(inst).c_str(), inst.unsupported_reason); } void AppendFlag(std::string* text, bool* first, uint32_t flags, uint32_t flag, const char* name) { @@ -238,7 +237,8 @@ bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::strin return true; } if (code >= 256u && code <= 511u) { - return DecodeVectorGpr(code - 256u, operand, error); + DecodeVectorGpr(code - 256u, operand); + return true; } switch (code) { @@ -292,27 +292,15 @@ bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std:: } } -bool DecodeVectorGpr(uint32_t reg, Operand& operand, std::string* error) { - if (reg > 255u) { - SetError(error, "VGPR index is out of range"); - return false; - } +void DecodeVectorGpr(uint32_t reg, Operand& operand) { operand = {}; operand.kind = OperandKind::Vgpr; operand.reg = reg; - return true; } -bool ReadLiteralOperands(std::span code, uint32_t word_index, Instruction& inst, - std::string* error) { +void ReadLiteralOperands(std::span code, uint32_t word_index, Instruction& inst) { 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; + return; } const auto literal = code[word_index + inst.word_count]; @@ -322,13 +310,12 @@ bool ReadLiteralOperands(std::span code, uint32_t word_index, In ApplyLiteral(inst.src3, literal); inst.word_count++; SetRawWords(inst, code, word_index, inst.word_count); - return true; } void SetRawWords(Instruction& inst, std::span code, uint32_t word_index, uint32_t word_count) { inst.word_count = word_count; - inst.raw_count = std::min(word_count, MaxInstructionRawWords); + inst.raw_count = word_count; for (uint32_t i = 0; i < inst.raw_count; i++) { inst.raw[i] = code[word_index + i]; } @@ -341,69 +328,96 @@ void SetUnsupported(Instruction& inst, Family family, uint32_t opcode_id, const inst.unsupported_reason = reason; } -bool DecodeProgram(std::span code, Program& program, std::string* error) { - if (code.empty() || code.size() > UINT32_MAX / sizeof(uint32_t)) { - SetError(error, "invalid shader decoder input"); - return false; +Family GetInstructionFamily(uint32_t word) { + if ((word & 0x80000000u) == 0u) { + switch ((word >> 25u) & 0x3fu) { + case 0x3eu: return Family::VOPC; + case 0x3fu: return Family::VOP1; + default: return Family::VOP2; + } + } + if ((word & 0xc0000000u) == 0x80000000u) { + const auto opcode = (word >> 23u) & 0x7fu; + switch (opcode) { + case 0x7du: return Family::SOP1; + case 0x7eu: return Family::SOPC; + case 0x7fu: return Family::SOPP; + default: return opcode >= 0x60u ? Family::SOPK : Family::SOP2; + } } + switch (word >> 26u) { + case 0x32u: return Family::VINTRP; + case 0x33u: return Family::VOP3P; + case 0x35u: return Family::VOP3; + case 0x36u: return Family::DS; + case 0x37u: return Family::FLAT; + case 0x38u: return Family::MUBUF; + case 0x3au: return Family::MTBUF; + case 0x3cu: return Family::MIMG; + case 0x3du: return Family::SMEM; + case 0x3eu: return Family::EXP; + default: return Family::Unknown; + } +} + +bool DecodeInstruction(std::span code, uint32_t word_index, Instruction& inst, + std::string* error) { + const uint32_t pc = word_index * sizeof(uint32_t); + switch (GetInstructionFamily(code[word_index])) { + case Family::SOP1: return DecodeSop1(pc, code, word_index, inst, error); + case Family::SOP2: return DecodeSop2(pc, code, word_index, inst, error); + case Family::SOPK: return DecodeSopk(pc, code, word_index, inst, error); + case Family::SOPC: return DecodeSopc(pc, code, word_index, inst, error); + case Family::SOPP: DecodeSopp(pc, code, word_index, inst); return true; + case Family::VOP1: return DecodeVop1(pc, code, word_index, inst, error); + case Family::VOP2: return DecodeVop2(pc, code, word_index, inst, error); + case Family::VOP3: return DecodeVop3(pc, code, word_index, inst, error); + case Family::VOP3P: return DecodeVop3p(pc, code, word_index, inst, error); + case Family::VOPC: return DecodeVopc(pc, code, word_index, inst, error); + case Family::VINTRP: DecodeVintrp(pc, code, word_index, inst); return true; + case Family::SMEM: DecodeSmem(pc, code, word_index, inst); return true; + case Family::MUBUF: DecodeMubuf(pc, code, word_index, inst); return true; + case Family::MTBUF: DecodeMtbuf(pc, code, word_index, inst); return true; + case Family::FLAT: DecodeFlat(pc, code, word_index, inst); return true; + case Family::DS: DecodeDs(pc, code, word_index, inst); return true; + case Family::MIMG: DecodeMimg(pc, code, word_index, inst); return true; + case Family::EXP: DecodeExp(pc, code, word_index, inst); return true; + default: + if (error != nullptr) { + *error = + fmt::format("unknown RDNA2 instruction family at pc 0x{:08x}, raw=0x{:08x}", pc, + code[word_index]); + } + return false; + } +} + +bool DecodeProgram(std::span code, Program& program, std::string* error) { program.instructions.clear(); + program.instructions.reserve(code.size()); program.code = code; - std::set branch_targets; + std::vector 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) { + program.instructions.emplace_back(); + if (!DecodeInstruction(code, word_index, program.instructions.back(), error)) { + program.instructions.pop_back(); return false; } - program.instructions.push_back(inst); + const auto& inst = program.instructions.back(); word_index += inst.word_count; if (IsControlFlowBranch(inst.opcode)) { - branch_targets.insert(inst.branch_target); + const auto target_index = inst.branch_target / sizeof(uint32_t); + if (branch_targets.empty()) { + branch_targets.resize(code.size()); + } + branch_targets[target_index] = true; } if (inst.opcode == Opcode::SEndpgm && - (word_index >= code.size() || !branch_targets.contains(word_index * 4u))) { + (word_index >= code.size() || branch_targets.empty() || !branch_targets[word_index])) { return true; } } @@ -1001,7 +1015,7 @@ 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()); + RawWordsToString(inst).c_str(), inst.unsupported_reason); } if (inst.family == Family::SOPC) { return WithUnsupportedReason(inst, FormatSources(inst)); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 0f5e1d313..f5c0c9878 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -5,6 +5,7 @@ #include "common/stringUtils.h" #include +#include #include namespace Libs::Graphics::ShaderRecompiler::Decoder { @@ -664,7 +665,7 @@ struct Instruction { bool compr = false; bool vm = false; } exp; - std::string unsupported_reason; + std::string_view unsupported_reason; }; struct Program { @@ -672,13 +673,17 @@ struct Program { std::vector instructions; }; +// Code spans are trusted to contain complete instructions, valid branch targets, and 32-bit PCs. +Family GetInstructionFamily(uint32_t word); +// The output object must be freshly initialized. +bool DecodeInstruction(std::span code, uint32_t word_index, Instruction& inst, + std::string* error); bool DecodeProgram(std::span code, Program& program, std::string* error); bool DecodeScalarSource(uint32_t code, uint32_t pc, Operand& operand, std::string* error); bool DecodeScalarDestination(uint32_t code, uint32_t pc, Operand& operand, std::string* error); -bool DecodeVectorGpr(uint32_t reg, Operand& operand, std::string* error); -bool ReadLiteralOperands(std::span code, uint32_t word_index, Instruction& inst, - std::string* error); +void DecodeVectorGpr(uint32_t reg, Operand& operand); +void ReadLiteralOperands(std::span code, uint32_t word_index, Instruction& inst); void SetRawWords(Instruction& inst, std::span code, uint32_t word_index, uint32_t word_count); void SetUnsupported(Instruction& inst, Family family, uint32_t opcode_id, const char* reason); diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 480d66d87..6400346f1 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -2,8 +2,6 @@ #include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h" -#include - namespace Libs::Graphics::ShaderRecompiler::Decoder { namespace { @@ -30,7 +28,7 @@ struct Vop2OpcodeInfo { Vop2SdwaProfile sdwa_profile = Vop2SdwaProfile::None; }; -constexpr Vop2OpcodeInfo VOP2_OPS[] = { +constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = { {0x01u, Opcode::VCndmaskB32, Vop2SdwaProfile::Cndmask}, {0x02u, Opcode::VDot2cF32F16}, {0x03u, Opcode::VAddF32, Vop2SdwaProfile::Float32}, @@ -81,13 +79,14 @@ constexpr Vop2OpcodeInfo VOP2_OPS[] = { {0x3au, Opcode::VMinF16, Vop2SdwaProfile::Float16}, {0x3cu, Opcode::VPkFmacF16}, }; -static_assert(Detail::HasUniqueEncodings(VOP2_OPS)); + +constexpr auto VOP2_OPS = Detail::MakeOpcodeTable<0x40>(VOP2_OPCODE_LIST); constexpr Opcode LookupVop2Opcode(uint32_t encoding) { return Detail::LookupOpcode(VOP2_OPS, encoding); } -constexpr OpcodeMap VOP1_OPS[] = { +constexpr OpcodeMap VOP1_OPCODE_LIST[] = { {0x00u, Opcode::VNop}, {0x01u, Opcode::VMovB32}, {0x02u, Opcode::VReadfirstlaneB32}, @@ -138,7 +137,9 @@ constexpr OpcodeMap VOP1_OPS[] = { {0x5eu, Opcode::VRndneF16}, }; -constexpr OpcodeMap VOP3_ENCODED_VOP1_OPS[] = { +constexpr auto VOP1_OPS = Detail::MakeOpcodeTable<0x100>(VOP1_OPCODE_LIST); + +constexpr OpcodeMap VOP3_ENCODED_VOP1_OPCODE_LIST[] = { {0x00u, Opcode::VNop}, {0x01u, Opcode::VMovB32}, {0x02u, Opcode::VReadfirstlaneB32}, @@ -184,13 +185,15 @@ constexpr OpcodeMap VOP3_ENCODED_VOP1_OPS[] = { {0x5eu, Opcode::VRndneF16}, }; +constexpr auto VOP3_ENCODED_VOP1_OPS = Detail::MakeOpcodeTable<0x80>(VOP3_ENCODED_VOP1_OPCODE_LIST); + struct VopcOpcodeInfo { uint32_t encoding = 0; Opcode decoded = Opcode::Unknown; bool supports_dpp = true; }; -constexpr VopcOpcodeInfo VOPC_OPS[] = { +constexpr VopcOpcodeInfo VOPC_OPCODE_LIST[] = { {0x00u, Opcode::VCmpFF32}, {0x01u, Opcode::VCmpLtF32}, {0x02u, Opcode::VCmpEqF32}, {0x03u, Opcode::VCmpLeF32}, {0x04u, Opcode::VCmpGtF32}, {0x05u, Opcode::VCmpLgF32}, @@ -235,9 +238,10 @@ constexpr VopcOpcodeInfo VOPC_OPS[] = { {0xdcu, Opcode::VCmpxGtF16}, {0xdeu, Opcode::VCmpxGeF16}, {0xfdu, Opcode::VCmpxNeqF16}, {0xfeu, Opcode::VCmpxNltF16}, }; -static_assert(Detail::HasUniqueEncodings(VOPC_OPS)); -constexpr OpcodeMap VOP3_OPS[] = { +constexpr auto VOPC_OPS = Detail::MakeOpcodeTable<0x100>(VOPC_OPCODE_LIST); + +constexpr OpcodeMap VOP3_OPCODE_LIST[] = { {0x141u, Opcode::VMadF32}, {0x142u, Opcode::VMadI32I24}, {0x143u, Opcode::VMadU32U24}, {0x176u, Opcode::VMadU64U32}, {0x144u, Opcode::VCubeidF32}, {0x145u, Opcode::VCubescF32}, @@ -275,7 +279,9 @@ constexpr OpcodeMap VOP3_OPS[] = { {0x14fu, Opcode::VAlignbyteB32}, }; -constexpr OpcodeMap VOP3P_OPS[] = { +constexpr auto VOP3_OPS = Detail::MakeOpcodeTable<0x400>(VOP3_OPCODE_LIST); + +constexpr OpcodeMap VOP3P_OPCODE_LIST[] = { {0x00u, Opcode::VPkMadI16}, {0x01u, Opcode::VPkMulLoU16}, {0x02u, Opcode::VPkAddI16}, {0x03u, Opcode::VPkSubI16}, {0x04u, Opcode::VPkLshlrevB16}, {0x05u, Opcode::VPkLshrrevB16}, {0x06u, Opcode::VPkAshrrevI16}, {0x07u, Opcode::VPkMaxI16}, {0x08u, Opcode::VPkMinI16}, @@ -286,6 +292,8 @@ constexpr OpcodeMap VOP3P_OPS[] = { {0x22u, Opcode::VMadMixhiF16}, }; +constexpr auto VOP3P_OPS = Detail::MakeOpcodeTable<0x80>(VOP3P_OPCODE_LIST); + bool IsVop2LiteralMadOpcode(uint32_t opcode) { return opcode == 0x20u || opcode == 0x21u || opcode == 0x2cu || opcode == 0x2du; } @@ -312,7 +320,7 @@ Opcode LookupVop3Opcode(uint32_t opcode) { if (opcode >= 0x180u && opcode <= 0x1ffu) { return Detail::LookupOpcode(VOP3_ENCODED_VOP1_OPS, opcode - 0x180u); } - return Detail::LookupOpcode(VOP3_OPS, opcode); + return Opcode::Unsupported; } bool IsVop3EncodedVopc(uint32_t opcode) { @@ -573,13 +581,6 @@ bool ValidateVop1Sdwa(Instruction& inst, uint32_t opcode, uint32_t modifier) { bool DecodeVop1Sdwa(uint32_t pc, std::span code, uint32_t word_index, uint32_t opcode, uint32_t vdst, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOP1 SDWA instruction at pc 0x{:08x}", pc); - } - return false; - } - const auto modifier = code[word_index + 1u]; const auto src0 = modifier & 0xffu; const auto dst_sel = (modifier >> 8u) & 0x7u; @@ -598,9 +599,14 @@ bool DecodeVop1Sdwa(uint32_t pc, std::span code, uint32_t word_i } const bool scalar_dst = UsesScalarDestination(inst.opcode); - if (!(scalar_dst ? DecodeScalarDestination(vdst, pc, inst.dst, error) - : DecodeVectorGpr(vdst, inst.dst, error)) || - !DecodeScalarSource(src0 + (s0 == 0u ? 256u : 0u), pc, inst.src0, error)) { + if (scalar_dst) { + if (!DecodeScalarDestination(vdst, pc, inst.dst, error)) { + return false; + } + } else { + DecodeVectorGpr(vdst, inst.dst); + } + if (!DecodeScalarSource(src0 + (s0 == 0u ? 256u : 0u), pc, inst.src0, error)) { return false; } inst.dst.sdwa_sel = dst_sel; @@ -612,7 +618,8 @@ bool DecodeVop1Sdwa(uint32_t pc, std::span code, uint32_t word_i inst.src0.negate = src0_neg != 0u; inst.src0.absolute = src0_abs != 0u; inst.src_count = 1; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } void ApplyDppModifier(Operand& operand, uint32_t modifier) { @@ -628,21 +635,19 @@ void ApplyDppModifier(Operand& operand, uint32_t modifier) { bool DecodeVop1Dpp(uint32_t pc, std::span code, uint32_t word_index, uint32_t opcode, uint32_t vdst, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOP1 DPP instruction at pc 0x{:08x}", pc); - } - return false; - } - const auto modifier = code[word_index + 1u]; const auto src0 = modifier & 0xffu; SetRawWords(inst, code, word_index, 2); const bool scalar_dst = UsesScalarDestination(inst.opcode); - if (!(scalar_dst ? DecodeScalarDestination(vdst, pc, inst.dst, error) - : DecodeVectorGpr(vdst, inst.dst, error)) || - !DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) { + if (scalar_dst) { + if (!DecodeScalarDestination(vdst, pc, inst.dst, error)) { + return false; + } + } else { + DecodeVectorGpr(vdst, inst.dst); + } + if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) { return false; } ApplyDppModifier(inst.src0, modifier); @@ -653,7 +658,8 @@ bool DecodeVop1Dpp(uint32_t pc, std::span code, uint32_t word_in "VOP1 DPP integer source modifiers are not supported"); return true; } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } using Vop1ModifierDecodeFn = bool (*)(uint32_t pc, std::span code, @@ -976,19 +982,13 @@ bool FinalizeVop2Instruction(uint32_t pc, std::span code, uint32 break; default: inst.src_count = 2; break; } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } bool DecodeVop2Sdwa(uint32_t pc, std::span code, uint32_t word_index, uint32_t opcode, uint32_t vdst, uint32_t vsrc1, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOP2 SDWA instruction at pc 0x{:08x}", pc); - } - return false; - } - const auto modifier = code[word_index + 1u]; const auto fields = DecodeVop2SdwaFields(modifier); SetRawWords(inst, code, word_index, 2); @@ -996,8 +996,8 @@ bool DecodeVop2Sdwa(uint32_t pc, std::span code, uint32_t word_i return true; } - if (!DecodeVectorGpr(vdst, inst.dst, error) || - !DecodeScalarSource(fields.src0 + (fields.s0 == 0u ? 256u : 0u), pc, inst.src0, error) || + DecodeVectorGpr(vdst, inst.dst); + if (!DecodeScalarSource(fields.src0 + (fields.s0 == 0u ? 256u : 0u), pc, inst.src0, error) || !DecodeScalarSource(vsrc1 + (fields.s1 == 0u ? 256u : 0u), pc, inst.src1, error)) { return false; } @@ -1019,20 +1019,13 @@ bool DecodeVop2Sdwa(uint32_t pc, std::span code, uint32_t word_i bool DecodeVop2Dpp(uint32_t pc, std::span code, uint32_t word_index, uint32_t opcode, uint32_t vdst, uint32_t vsrc1, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOP2 DPP instruction at pc 0x{:08x}", pc); - } - return false; - } - const auto modifier = code[word_index + 1u]; const auto src0 = modifier & 0xffu; SetRawWords(inst, code, word_index, 2); - if (!DecodeVectorGpr(vdst, inst.dst, error) || - !DecodeScalarSource(src0 + 256u, pc, inst.src0, error) || - !DecodeVectorGpr(vsrc1, inst.src1, error)) { + DecodeVectorGpr(vdst, inst.dst); + DecodeVectorGpr(vsrc1, inst.src1); + if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) { return false; } ApplyDefaultVop2F16Destination(inst); @@ -1120,13 +1113,6 @@ bool SupportsVopcSdwa(Opcode opcode) { bool DecodeVopcSdwa(uint32_t pc, std::span code, uint32_t word_index, uint32_t opcode, uint32_t vsrc1, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOPC SDWA instruction at pc 0x{:08x}", pc); - } - return false; - } - const auto modifier = code[word_index + 1u]; const auto fields = DecodeVopcSdwaFields(modifier); SetRawWords(inst, code, word_index, 2); @@ -1160,18 +1146,12 @@ bool DecodeVopcSdwa(uint32_t pc, std::span code, uint32_t word_i inst.src1.negate = fields.src1_neg != 0u; inst.src1.absolute = fields.src1_abs != 0u; inst.src_count = 2; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } bool DecodeVopcDpp(uint32_t pc, std::span code, uint32_t word_index, uint32_t opcode, uint32_t vsrc1, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOPC DPP instruction at pc 0x{:08x}", pc); - } - return false; - } - const auto modifier = code[word_index + 1u]; const auto src0 = modifier & 0xffu; SetRawWords(inst, code, word_index, 2); @@ -1184,8 +1164,8 @@ bool DecodeVopcDpp(uint32_t pc, std::span code, uint32_t word_in SetUnsupported(inst, Family::VOPC, opcode, "VOPC DPP modifier is not supported for opcode"); return true; } - if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error) || - !DecodeVectorGpr(vsrc1, inst.src1, error)) { + DecodeVectorGpr(vsrc1, inst.src1); + if (!DecodeScalarSource(src0 + 256u, pc, inst.src0, error)) { return false; } inst.dst.kind = IsVopcCompareExec(inst.opcode) ? OperandKind::ExecLo : OperandKind::VccLo; @@ -1193,7 +1173,8 @@ bool DecodeVopcDpp(uint32_t pc, std::span code, uint32_t word_in inst.src1.negate = ((modifier >> 22u) & 0x1u) != 0u; inst.src1.absolute = ((modifier >> 23u) & 0x1u) != 0u; inst.src_count = 2; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } uint32_t NativeVop3SourceCount(Opcode opcode) { @@ -1480,11 +1461,6 @@ bool DecodeVop2(uint32_t pc, std::span code, uint32_t word_index inst.opcode = LookupVop2Opcode(opcode); SetRawWords(inst, code, word_index, 1); - switch (opcode) { - case 0x3eu: return DecodeVopc(pc, code, word_index, inst, error); - case 0x3fu: return DecodeVop1(pc, code, word_index, inst, error); - default: break; - } if (inst.opcode == Opcode::Unsupported) { SetUnsupported(inst, Family::VOP2, opcode, "VOP2 opcode is not implemented"); return true; @@ -1498,9 +1474,9 @@ bool DecodeVop2(uint32_t pc, std::span code, uint32_t word_index return true; } - if (!DecodeVectorGpr(vdst, inst.dst, error) || - !DecodeScalarSource(src0, pc, inst.src0, error) || - !DecodeVectorGpr(vsrc1, inst.src1, error)) { + DecodeVectorGpr(vdst, inst.dst); + DecodeVectorGpr(vsrc1, inst.src1); + if (!DecodeScalarSource(src0, pc, inst.src0, error)) { return false; } ApplyDefaultVop2F16Destination(inst); @@ -1539,13 +1515,19 @@ bool DecodeVop1(uint32_t pc, std::span code, uint32_t word_index return true; } const bool scalar_dst = UsesScalarDestination(inst.opcode); - if (!(scalar_dst ? DecodeScalarDestination(vdst, pc, inst.dst, error) - : DecodeVectorGpr(vdst, inst.dst, error)) || - !DecodeScalarSource(src0, pc, inst.src0, error)) { + if (scalar_dst) { + if (!DecodeScalarDestination(vdst, pc, inst.dst, error)) { + return false; + } + } else { + DecodeVectorGpr(vdst, inst.dst); + } + if (!DecodeScalarSource(src0, pc, inst.src0, error)) { return false; } inst.src_count = 1; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } bool DecodeVopc(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, @@ -1572,23 +1554,17 @@ bool DecodeVopc(uint32_t pc, std::span code, uint32_t word_index SetUnsupported(inst, Family::VOPC, opcode, "VOPC opcode is not implemented"); return true; } - if (!DecodeScalarSource(src0, pc, inst.src0, error) || - !DecodeVectorGpr(vsrc1, inst.src1, error)) { + DecodeVectorGpr(vsrc1, inst.src1); + if (!DecodeScalarSource(src0, pc, inst.src0, error)) { return false; } inst.src_count = 2; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOP3 instruction at pc 0x{:08x}", pc); - } - return false; - } - const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = (word0 >> 16u) & 0x3ffu; @@ -1655,7 +1631,7 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index // VOP3A uses VDST for VOPC and the scalar-destination lane-read opcodes. dst_ok = DecodeScalarDestination(vdst, pc, inst.dst, error); } else { - dst_ok = DecodeVectorGpr(vdst, inst.dst, error); + DecodeVectorGpr(vdst, inst.dst); } if (!dst_ok || !DecodeScalarSource(src0, pc, inst.src0, error)) { return false; @@ -1674,14 +1650,16 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index if (native_source_modifiers) { ApplyNativeVop3SourceModifiers(inst, abs, neg); } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } if (IsVop3EncodedVop1(opcode)) { inst.src_count = 1; if (native_source_modifiers) { ApplyNativeVop3SourceModifiers(inst, abs, neg); } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } if (carry_in_out) { if (!DecodeScalarDestination(sdst, pc, inst.dst2, error) || @@ -1690,7 +1668,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index return false; } inst.src_count = 3; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } if (vop3b_carry_out) { if (!DecodeScalarDestination(sdst, pc, inst.dst2, error) || @@ -1698,7 +1677,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index return false; } inst.src_count = 2; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } if (vop3b_mad_u64) { if (!DecodeScalarDestination(sdst, pc, inst.dst2, error) || @@ -1707,7 +1687,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index return false; } inst.src_count = 3; - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } if (IsVop3EncodedVop2(opcode)) { if (!DecodeScalarSource(src1, pc, inst.src1, error)) { @@ -1724,7 +1705,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index if (native_source_modifiers) { ApplyNativeVop3SourceModifiers(inst, abs, neg); } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } if (!DecodeScalarSource(src1, pc, inst.src1, error)) { return false; @@ -1744,31 +1726,26 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index } else if (native_source_modifiers) { ApplyNativeVop3SourceModifiers(inst, abs, neg); } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } bool DecodeVop3p(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, std::string* error) { - if (word_index + 1u >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VOP3P instruction at pc 0x{:08x}", pc); - } - return false; - } - const uint32_t word0 = code[word_index]; const uint32_t word1 = code[word_index + 1u]; const uint32_t opcode = (word0 >> 16u) & 0x7fu; const uint32_t vdst = word0 & 0xffu; const uint32_t neg_hi = (word0 >> 8u) & 0x7u; const uint32_t op_sel = (word0 >> 11u) & 0x7u; - const uint32_t op_sel_hi_2 = (word0 >> 14u) & 0x1u; + const uint32_t op_sel_hi_0 = (word0 >> 14u) & 0x1u; const uint32_t clamp = (word0 >> 15u) & 0x1u; const uint32_t src0 = word1 & 0x1ffu; const uint32_t src1 = (word1 >> 9u) & 0x1ffu; const uint32_t src2 = (word1 >> 18u) & 0x1ffu; - const uint32_t op_sel_hi = ((word1 >> 27u) & 0x3u) | (op_sel_hi_2 << 2u); - const uint32_t neg = (word1 >> 29u) & 0x7u; + const uint32_t op_sel_hi = + op_sel_hi_0 | (((word1 >> 28u) & 0x1u) << 1u) | (((word1 >> 27u) & 0x1u) << 2u); + const uint32_t neg = (word1 >> 29u) & 0x7u; inst.pc = pc; inst.word = word0; @@ -1782,8 +1759,8 @@ bool DecodeVop3p(uint32_t pc, std::span code, uint32_t word_inde return true; } inst.src_count = Vop3pSourceCount(inst.opcode); - if (!DecodeVectorGpr(vdst, inst.dst, error) || - !DecodeScalarSource(src0, pc, inst.src0, error) || + DecodeVectorGpr(vdst, inst.dst); + if (!DecodeScalarSource(src0, pc, inst.src0, error) || !DecodeScalarSource(src1, pc, inst.src1, error)) { return false; } @@ -1810,18 +1787,12 @@ bool DecodeVop3p(uint32_t pc, std::span code, uint32_t word_inde } else if (inst.opcode == Opcode::VFmaF32) { ApplyVop3pMixAbsModifiers(inst); } - return ReadLiteralOperands(code, word_index, inst, error); + ReadLiteralOperands(code, word_index, inst); + return true; } -bool DecodeVintrp(uint32_t pc, std::span code, uint32_t word_index, - Instruction& inst, std::string* error) { - if (word_index >= code.size()) { - if (error != nullptr) { - *error = fmt::format("truncated VINTRP instruction at pc 0x{:08x}", pc); - } - return false; - } - +void DecodeVintrp(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst) { const uint32_t word = code[word_index]; const uint32_t opcode = (word >> 16u) & 0x3u; const uint32_t vdst = (word >> 18u) & 0xffu; @@ -1838,18 +1809,16 @@ bool DecodeVintrp(uint32_t pc, std::span code, uint32_t word_ind SetRawWords(inst, code, word_index, 1); if (inst.opcode == Opcode::Unsupported) { SetUnsupported(inst, Family::VINTRP, opcode, "VINTRP opcode is not implemented"); - return true; + return; } - if (!DecodeVectorGpr(vdst, inst.dst, error)) { - return false; - } + DecodeVectorGpr(vdst, inst.dst); if (inst.opcode == Opcode::VInterpMovF32) { inst.src0.kind = OperandKind::IntegerInlineConstant; inst.src0.value = vsrc & 0x3u; inst.src0.signed_val = static_cast(inst.src0.value); - } else if (!DecodeVectorGpr(vsrc, inst.src0, error)) { - return false; + } else { + DecodeVectorGpr(vsrc, inst.src0); } inst.src1.kind = OperandKind::IntegerInlineConstant; inst.src1.value = attr; @@ -1858,7 +1827,6 @@ bool DecodeVintrp(uint32_t pc, std::span code, uint32_t word_ind inst.src2.value = chan; inst.src2.signed_val = static_cast(chan); inst.src_count = 3; - return true; } } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.h b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.h index 64cbe510f..796367fe9 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.h +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.h @@ -15,8 +15,8 @@ bool DecodeVop3(uint32_t pc, std::span code, uint32_t word_index std::string* error); bool DecodeVop3p(uint32_t pc, std::span code, uint32_t word_index, Instruction& inst, std::string* error); -bool DecodeVintrp(uint32_t pc, std::span code, uint32_t word_index, - Instruction& inst, std::string* error); +void DecodeVintrp(uint32_t pc, std::span code, uint32_t word_index, + Instruction& inst); } // namespace Libs::Graphics::ShaderRecompiler::Decoder diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index aecb36604..2ea7ac906 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -505,14 +505,15 @@ constexpr u32 EncodeVop3pWord0(u32 opcode, u32 dst, u32 op_sel_hi = 0, u32 op_sel = 0, u32 neg_hi = 0, bool clamp = false) { return (0x33u << 26u) | ((opcode & 0x7fu) << 16u) | ((neg_hi & 0x7u) << 8u) | - ((op_sel & 0x7u) << 11u) | ((op_sel_hi & 0x4u) << 12u) | + ((op_sel & 0x7u) << 11u) | ((op_sel_hi & 0x1u) << 14u) | (clamp ? (1u << 15u) : 0u) | (dst & 0xffu); } constexpr u32 EncodeVop3pWord1(u32 src0, u32 src1, u32 src2 = 0, u32 op_sel_hi = 0, u32 neg = 0) { return (src0 & 0x1ffu) | ((src1 & 0x1ffu) << 9u) | ((src2 & 0x1ffu) << 18u) | - ((op_sel_hi & 0x3u) << 27u) | ((neg & 0x7u) << 29u); + (((op_sel_hi >> 2u) & 0x1u) << 27u) | + (((op_sel_hi >> 1u) & 0x1u) << 28u) | ((neg & 0x7u) << 29u); } constexpr u32 EncodeVopc(u32 opcode, u32 src0, u32 src1) { @@ -11118,14 +11119,15 @@ TestCase ScalarBitfieldPack() { return {"ScalarBitfieldPack", code, {}, - {0xf0000000u, 8, 9, 1, 0, 0, 0x0000000fu, 0, 0x00000f00u, 0x0000000fu, - 0xddddbbbbu, 0xccccbbbbu, 0xccccaaaau, 1, 1, 40, 5, 0xffffffffu, 0, - 1}, + {0xf0000000u, 8, 9, 1, 0, + 0, 0x0000000fu, 0, 0x00000f00u, 0x0000000fu, + 0xddddbbbbu, 0xccccbbbbu, 0xccccaaaau, 1, 1, + 40, 5, 0xffffffffu, 0, 1}, {O::SMovB32, O::SBrevB32, O::SBcnt1I32B32, O::SBcnt1I32B64, O::SFf1I32B64, O::SBitreplicateB64B32, O::SBfmB32, O::SBfeU32, - O::SPackLlB32B16, - O::SPackLhB32B16, O::SPackHhB32B16, O::SBitcmp0B32, O::SBitcmp1B32, - O::SCselectB32, O::VMovB32, O::BufferStoreDword, O::SEndpgm}}; + O::SPackLlB32B16, O::SPackLhB32B16, O::SPackHhB32B16, O::SBitcmp0B32, + O::SBitcmp1B32, O::SCselectB32, O::VMovB32, O::BufferStoreDword, + O::SEndpgm}}; } TestCase ScalarBrevB32PreservesScc() { diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index c4dd84248..ee425b99c 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -11,20 +11,20 @@ #include "graphics/host_gpu/renderer/pipeline/shaderSubgroup.h" #include "graphics/shader/recompiler/ExecMask.h" #include "graphics/shader/recompiler/ShaderRecompiler.h" +#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h" +#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" #include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h" #include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h" #include "graphics/shader/recompiler/frontend/translate/Translate.h" -#include "graphics/shader/recompiler/backend/spirv/SpirvEmitter.h" -#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" +#include "graphics/shader/recompiler/ir/ShaderIR.h" +#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h" #include "graphics/shader/recompiler/ir/passes/DeadCodeElimination.h" #include "graphics/shader/recompiler/ir/passes/ReadLaneElimination.h" #include "graphics/shader/recompiler/ir/passes/ResourceTracking.h" -#include "graphics/shader/recompiler/ir/ShaderIR.h" #include "graphics/shader/recompiler/ir/passes/ShaderInfoCollection.h" #include "graphics/shader/recompiler/ir/passes/SrtWalker.h" #include "graphics/shader/recompiler/ir/passes/SsaRewrite.h" -#include "graphics/shader/recompiler/ir/ValueProgram.h" #include "graphics/shader/shader.h" #include "libs/agc.h" #include "spirv-tools/libspirv.hpp" @@ -163,8 +163,7 @@ uint32_t SpirvInstructionOpcodeCount(const std::vector &binary, } bool SpirvSourceHasInstructionOperand(const std::string &source, - const char *opcode, - const char *operand) { + const char *opcode, const char *operand) { std::istringstream lines(source); std::string line; while (std::getline(lines, line)) { @@ -835,13 +834,14 @@ constexpr uint32_t EncodeVop3pWord0(uint32_t opcode, uint32_t dst, uint32_t neg_hi = 0, bool clamp = false) { return (0x33u << 26u) | ((opcode & 0x7fu) << 16u) | (dst & 0xffu) | ((neg_hi & 0x7u) << 8u) | ((op_sel & 0x7u) << 11u) | - (((op_sel_hi >> 2u) & 0x1u) << 14u) | (clamp ? (1u << 15u) : 0u); + ((op_sel_hi & 0x1u) << 14u) | (clamp ? (1u << 15u) : 0u); } constexpr uint32_t EncodeVop3pWord1(uint32_t src0, uint32_t src1, uint32_t src2, uint32_t op_sel_hi = 0, uint32_t neg = 0) { return (src0 & 0x1ffu) | ((src1 & 0x1ffu) << 9u) | ((src2 & 0x1ffu) << 18u) | - ((op_sel_hi & 0x3u) << 27u) | ((neg & 0x7u) << 29u); + (((op_sel_hi >> 2u) & 0x1u) << 27u) | + (((op_sel_hi >> 1u) & 0x1u) << 28u) | ((neg & 0x7u) << 29u); } constexpr uint32_t EncodeSmem0(uint32_t opcode, uint32_t dst, uint32_t sbase) { @@ -3315,6 +3315,81 @@ void CheckNewDecoderUnsupported(const uint32_t *shader, uint32_t words, "unsupported lowering error was not explicit"); } +void TestNewShaderDecoderArchitecture() { + using namespace ShaderRecompiler::Decoder; + + Check(GetInstructionFamily(EncodeVop1(0x01, 2, 3)) == Family::VOP1, + "decoder did not classify compact VOP1 directly"); + Check(GetInstructionFamily(EncodeVopc(0x02, 2, 3)) == Family::VOPC, + "decoder did not classify compact VOPC directly"); + Check(GetInstructionFamily(EncodeDs0(0x36)) == Family::DS, + "decoder did not classify DS directly"); + + const uint32_t offset_code[] = {0u, EncodeVop1(0x01, 2, 3)}; + Instruction direct; + std::string error; + Check(DecodeInstruction(offset_code, 1u, direct, &error), error.c_str()); + Check(direct.pc == 4u && direct.family == Family::VOP1 && + direct.opcode == Opcode::VMovB32 && direct.dst.reg == 2u && + direct.src0.reg == 3u, + "single-instruction decoder failed at a nonzero offset"); + + const uint32_t program_code[] = {EncodeVop1(0x01, 2, 3), EncodeSopp(0x01, 0)}; + Instruction program_direct; + Check(DecodeInstruction(program_code, 0u, program_direct, &error), + error.c_str()); + Program program; + Check(DecodeProgram(program_code, program, &error), error.c_str()); + Check(program.instructions.size() == 2u && + program.instructions.front().family == program_direct.family && + program.instructions.front().opcode == program_direct.opcode && + program.instructions.front().word_count == + program_direct.word_count, + "program decoder diverged from the single-instruction decoder"); + + const uint32_t literal_code[] = {EncodeVop1(0x01, 2, 255u), 0x12345678u}; + Instruction literal; + Check(DecodeInstruction(literal_code, 0u, literal, &error), error.c_str()); + Check(literal.word_count == 2u && literal.src0.value == 0x12345678u, + "single-instruction decoder lost a compact literal extension"); + + const uint32_t mimg_nsa[] = {EncodeMimg0(0x20, 0xf) | (3u << 1u), + EncodeMimg1(4, 0, 1, 8), 0x03020100u, + 0x07060504u, 0x0b0a0908u}; + Instruction image; + Check(DecodeInstruction(mimg_nsa, 0u, image, &error), error.c_str()); + Check(image.family == Family::MIMG && image.word_count == 5u && + image.image_nsa_dwords == 3u, + "single-instruction decoder lost the MIMG NSA length"); + + const uint32_t ds_code[] = {EncodeDs0(0x36) | (1u << 17u), + EncodeDs1(2, 0, 1)}; + Instruction ds; + Check(DecodeInstruction(ds_code, 0u, ds, &error), error.c_str()); + Check(ds.opcode == Opcode::DsReadB32 && ds.gds, + "DS decoder lost the GFX10 opcode or GDS fields"); + + const uint32_t boot_ds[] = {0xd8d4c480u, 0x45000045u}; + Instruction boot; + Check(DecodeInstruction(boot_ds, 0u, boot, &error), error.c_str()); + Check(boot.opcode == Opcode::DsSwizzleB32 && boot.offset == 0xc480u, + "DS decoder rejected a captured boot-shader instruction"); + + for (uint32_t source = 0; source < 3u; source++) { + const uint32_t op_sel_hi = 1u << source; + const uint32_t packed[] = { + EncodeVop3pWord0(0x0e, 0, 0, op_sel_hi), + EncodeVop3pWord1(256, 257, 258, op_sel_hi), + }; + Instruction packed_inst; + Check(DecodeInstruction(packed, 0u, packed_inst, &error), error.c_str()); + Check(packed_inst.src0.op_sel_hi == (source == 0u) && + packed_inst.src1.op_sel_hi == (source == 1u) && + packed_inst.src2.op_sel_hi == (source == 2u), + "VOP3P OPSEL_HI source bit mapping is incorrect"); + } +} + void TestNewShaderRecompilerRejectsDppOn64BitCompares() { const uint32_t opcodes[] = {0xa2u, 0xe4u, 0xe5u}; // eq_i64, gt_u64, ne_u64 for (const auto opcode : opcodes) { @@ -6053,14 +6128,17 @@ void TestNewShaderRecompilerCfgLoopHeaderDynamicScalarBufferLoadStructured() { void TestNewShaderRecompilerCfgLoopHeaderBufferLoadDispatcher() { const uint32_t shader[] = { - EncodeSMovB32(0, 128), // preheader: s0 = 0 - EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // loop: - // buffer_load_dword - // v0 + EncodeSMovB32(0, 128), // preheader: s0 = 0 + EncodeMubuf0(0x0c), + EncodeMubuf1(0, 0, 1), // loop: + // buffer_load_dword + // v0 EncodeSop2(0x00, 0, 0, 129), // s_add_u32 s0, s0, 1 EncodeSopc(0x0a, 0, 130), // s_cmp_lt_u32 s0, 2 EncodeSopp(0x05, 0xfffbu), // s_cbranch_scc1 loop - EncodeMubuf0(0x1c, 0, false), EncodeMubuf1(0, 12, 0), 0xbf810000u, + EncodeMubuf0(0x1c, 0, false), + EncodeMubuf1(0, 12, 0), + 0xbf810000u, }; ShaderRecompiler::CompileOptions options; @@ -6458,16 +6536,16 @@ void TestNewShaderRecompilerCfgLoopEarlyContinuesNoSelection() { void TestNewShaderRecompilerCfgLoopGatewaySelection() { const uint32_t shader[] = { - EncodeSopc(0x0a, 0, 130), // loop: s_cmp_lt_u32 s0, 2 - EncodeSopp(0x04, 8), // loop exit -> end - EncodeSopc(0x06, 1, 1), // skip-body condition - EncodeSopp(0x05, 3), // skip body -> loop-control gateway - EncodeSopc(0x06, 2, 2), // body early-break condition - EncodeSopp(0x05, 4), // early break -> end - EncodeSMovB32(3, 129), // body work - EncodeSopc(0x0a, 0, 130), // loop-control gateway - EncodeSopp(0x04, 1), // exit -> end, else latch - EncodeSopp(0x02, 0xfff6u), // latch -> loop header + EncodeSopc(0x0a, 0, 130), // loop: s_cmp_lt_u32 s0, 2 + EncodeSopp(0x04, 8), // loop exit -> end + EncodeSopc(0x06, 1, 1), // skip-body condition + EncodeSopp(0x05, 3), // skip body -> loop-control gateway + EncodeSopc(0x06, 2, 2), // body early-break condition + EncodeSopp(0x05, 4), // early break -> end + EncodeSMovB32(3, 129), // body work + EncodeSopc(0x0a, 0, 130), // loop-control gateway + EncodeSopp(0x04, 1), // exit -> end, else latch + EncodeSopp(0x02, 0xfff6u), // latch -> loop header 0xbf810000u, }; @@ -7017,9 +7095,12 @@ void TestNewShaderRecompilerCompareMaskIsFullWaveBallot() { void TestNewShaderRecompilerBufferLoadsGuardedByExec() { const uint32_t shader[] = { - EncodeMubuf0(0x0c), EncodeMubuf1(0, 0, 1), // buffer_load_dword - // v0 - EncodeMubuf0(0x1c, 0, false), EncodeMubuf1(0, 12, 0), EncodeSopp(0x01), + EncodeMubuf0(0x0c), + EncodeMubuf1(0, 0, 1), // buffer_load_dword + // v0 + EncodeMubuf0(0x1c, 0, false), + EncodeMubuf1(0, 12, 0), + EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; @@ -7430,9 +7511,7 @@ void TestNewShaderRecompilerPerInvocationU64Complement() { EncodeSop2(0x0f, 2, 126, 106), // s_and_b64 s[2:3], exec, vcc EncodeSop1(0x08, 4, 2), // s_not_b64 s[4:5], s[2:3] EncodeSop2(0x0f, 126, 126, 4), // s_and_b64 exec, exec, s[4:5] - EncodeExp0(0x0c, 0xf), - EncodeExp1(0, 1, 2, 3), - EncodeSopp(0x01), + EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3), EncodeSopp(0x01), }; ShaderRecompiler::CompileOptions options; @@ -7542,7 +7621,7 @@ void TestNewShaderRecompilerExpPixelOutputs() { const auto compressed_ba_source = DisassembleSpirvBinary(compressed_ba_result.spirv); Check(CountSourceOccurrences(compressed_ba_source, "OpCompositeExtract") == - 1u && + 1u && CountSourceOccurrences(compressed_ba_source, "OpBitFieldUExtract") == 2u, "compressed UINT16 BA-only export did not read and unpack VSRC1"); @@ -7796,7 +7875,7 @@ void TestNewShaderRecompilerEarlyZDisabledWhenPixelKillEnabled() { Check(!Common::ContainsStr(ordinary_source, "pixel_valid_mask_active"), "ordinary pixel shader allocated pixel-valid state"); Check(SpirvContainsExecutionMode(ordinary_result.spirv, - ExecutionModeEarlyFragmentTests), + ExecutionModeEarlyFragmentTests), "ordinary early-Z pixel shader lost EarlyFragmentTests"); CheckSpirvBinaryValidates(ordinary_result.spirv); } @@ -8294,9 +8373,9 @@ void TestSrtWalkerRealSmemLowering() { void TestSrtWalkerVccBaseLowering() { const uint32_t shader[] = { - EncodeSMovB32(106, 27), EncodeSMovB32(107, 28), + EncodeSMovB32(106, 27), EncodeSMovB32(107, 28), EncodeSmem0(0x02, 0, 53), 125u << 25u, - EncodeMubuf0(0x1c), EncodeMubuf1(0, 0, 1), + EncodeMubuf0(0x1c), EncodeMubuf1(0, 0, 1), EncodeSopp(0x01), }; std::string error; @@ -8307,8 +8386,8 @@ void TestSrtWalkerVccBaseLowering() { Check(ir.values->srt_reads.size() == 4, "VCC-based SMEM lowering did not build four SRT reads"); - const std::array table = {0x11111111u, 0x22222222u, - 0x33333333u, 0x44444444u}; + const std::array table = {0x11111111u, 0x22222222u, 0x33333333u, + 0x44444444u}; std::array user_data{}; const auto address = reinterpret_cast(table.data()); user_data[27] = static_cast(address); @@ -8461,9 +8540,8 @@ void TestScalarMemoryLoadCrossesIntoVcc() { "wide SMEM destination crossing into VCC lost its descriptor source"); for (uint32_t dword = 0; dword < 4; dword++) { const auto *value = source->dwords[dword].ResolveInstruction(); - Check(value != nullptr && - value->GetOpcode() == - ShaderRecompiler::IR::ValueOpcode::ReadConst, + Check(value != nullptr && value->GetOpcode() == + ShaderRecompiler::IR::ValueOpcode::ReadConst, "wide SMEM descriptor lost a dword crossing into VCC"); } } @@ -8698,11 +8776,9 @@ void TestNewShaderRecompilerPixelPipelineEntry() { CheckSpirvBinaryValidates(spirv); const uint32_t vcc_load_shader[] = { - EncodeSMovB32(106, 27), EncodeSMovB32(107, 28), - EncodeSmem0(0x02, 44, 53), (0x7du << 25u) | 160u, - EncodeMubuf0(0x0c), EncodeMubuf1(0, 11, 1), - EncodeExp0(0x00, 0x1), EncodeExp1(0, 0, 0, 0), - EncodeSopp(0x01), + EncodeSMovB32(106, 27), EncodeSMovB32(107, 28), EncodeSmem0(0x02, 44, 53), + (0x7du << 25u) | 160u, EncodeMubuf0(0x0c), EncodeMubuf1(0, 11, 1), + EncodeExp0(0x00, 0x1), EncodeExp1(0, 0, 0, 0), EncodeSopp(0x01), }; std::array table{}; std::array buffer{}; @@ -8899,6 +8975,7 @@ int main() { // Opcode semantics and optimized direct SPIR-V are exercised by // ShaderRecompilerComputeTests. The pre-SSA register-IR shape checks above // remain as historical decoder fixtures only. + TestNewShaderDecoderArchitecture(); TestNewShaderRecompilerRejectsDppOn64BitCompares(); TestNewShaderRecompilerIrLookupMissFailsExplicitly(); TestPsInputCountRegisterDecode();