Optimize decoder

This commit is contained in:
nmzik
2026-08-18 03:53:42 +02:00
parent f3cdf217ea
commit 5bfdf4267e
15 changed files with 471 additions and 485 deletions
@@ -1,18 +1,9 @@
#include "graphics/shader/recompiler/frontend/decode/ExportOps.h"
#include <fmt/format.h>
namespace Libs::Graphics::ShaderRecompiler::Decoder {
bool DecodeExp(uint32_t pc, std::span<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> code, uint32_t word_index,
if (en == 0u) {
inst.src_count = 0;
}
return true;
}
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
@@ -5,8 +5,7 @@
namespace Libs::Graphics::ShaderRecompiler::Decoder {
bool DecodeExp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
void DecodeExp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
@@ -1,21 +1,21 @@
#include "graphics/shader/recompiler/frontend/decode/ImageOps.h"
#include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h"
#include <algorithm>
#include <fmt/format.h>
#include <iterator>
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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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) {
@@ -5,8 +5,8 @@
namespace Libs::Graphics::ShaderRecompiler::Decoder {
bool DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
void DecodeMimg(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
const char* MimgSampleOpcodeName(uint32_t opcode);
@@ -1,13 +1,12 @@
#include "graphics/shader/recompiler/frontend/decode/MemoryOps.h"
#include <fmt/format.h>
#include <iterator>
#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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<uint32_t>(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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<uint32_t>(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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<uint32_t>(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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<uint32_t>(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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<uint32_t>(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<const uint32_t> 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
@@ -5,16 +5,15 @@
namespace Libs::Graphics::ShaderRecompiler::Decoder {
bool DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error);
bool DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error);
bool DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
void DecodeSmem(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeMubuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeMtbuf(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeFlat(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
void DecodeDs(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
@@ -3,7 +3,9 @@
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
#include <array>
#include <cstddef>
#include <cstdint>
namespace Libs::Graphics::ShaderRecompiler::Decoder::Detail {
@@ -12,34 +14,43 @@ struct OpcodeMap {
Opcode decoded = Opcode::Unknown;
};
template <typename Entry, size_t N>
constexpr const Entry* FindOpcode(const Entry (&table)[N], uint32_t encoding) {
for (const auto& entry: table) {
if (entry.encoding == encoding) {
return &entry;
template <typename Entry, size_t EncodingCount, size_t EntryCount>
struct OpcodeTable {
std::array<Entry, EntryCount> entries = {};
std::array<uint16_t, EncodingCount> indices = {};
};
template <size_t EncodingCount, typename Entry, size_t EntryCount>
consteval auto MakeOpcodeTable(const Entry (&entries)[EntryCount]) {
static_assert(EntryCount < UINT16_MAX);
OpcodeTable<Entry, EncodingCount, EntryCount> 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<uint16_t>(i + 1);
}
return nullptr;
return table;
}
template <typename Entry, size_t N>
constexpr Opcode LookupOpcode(const Entry (&table)[N], uint32_t encoding) {
template <typename Entry, size_t EncodingCount, size_t EntryCount>
constexpr const Entry* FindOpcode(const OpcodeTable<Entry, EncodingCount, EntryCount>& table,
uint32_t encoding) {
if (table.indices[encoding] == 0) {
return nullptr;
}
return &table.entries[table.indices[encoding] - 1];
}
template <typename Entry, size_t EncodingCount, size_t EntryCount>
constexpr Opcode LookupOpcode(const OpcodeTable<Entry, EncodingCount, EntryCount>& table,
uint32_t encoding) {
const auto* entry = FindOpcode(table, encoding);
return entry != nullptr ? entry->decoded : Opcode::Unsupported;
}
template <typename Entry, size_t N>
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_
@@ -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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> code, uint32_t word_index, Instruction& inst,
@@ -159,8 +163,6 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
const uint32_t sdst = (word >> 16u) & 0x7fu;
const auto imm = static_cast<int16_t>(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<const uint32_t> code, uint32_t word_index
return DecodeBinarySources(pc, code, word_index, inst, ssrc0, ssrc1, error);
}
bool DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void DecodeSopp(uint32_t pc, std::span<const uint32_t> 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<const uint32_t> 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
@@ -13,8 +13,8 @@ bool DecodeSopk(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
std::string* error);
bool DecodeSopc(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
void DecodeSopp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
@@ -9,7 +9,6 @@
#include <algorithm>
#include <bit>
#include <fmt/format.h>
#include <set>
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<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error) {
void ReadLiteralOperands(std::span<const uint32_t> 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<const uint32_t> 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<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);
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<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;
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<const uint32_t> 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<const uint32_t> code, Program& program, std::string* error) {
program.instructions.clear();
program.instructions.reserve(code.size());
program.code = code;
std::set<uint32_t> branch_targets;
std::vector<bool> 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));
@@ -5,6 +5,7 @@
#include "common/stringUtils.h"
#include <span>
#include <string_view>
#include <vector>
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<Instruction> 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<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
bool DecodeProgram(std::span<const uint32_t> 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<const uint32_t> code, uint32_t word_index, Instruction& inst,
std::string* error);
void DecodeVectorGpr(uint32_t reg, Operand& operand);
void ReadLiteralOperands(std::span<const uint32_t> code, uint32_t word_index, Instruction& inst);
void SetRawWords(Instruction& inst, std::span<const uint32_t> code, uint32_t word_index,
uint32_t word_count);
void SetUnsupported(Instruction& inst, Family family, uint32_t opcode_id, const char* reason);
@@ -2,8 +2,6 @@
#include "graphics/shader/recompiler/frontend/decode/OpcodeTable.h"
#include <fmt/format.h>
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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> code,
@@ -976,19 +982,13 @@ bool FinalizeVop2Instruction(uint32_t pc, std::span<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> code, uint32_t word_i
bool DecodeVop2Dpp(uint32_t pc, std::span<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> code, uint32_t word_index, Instruction& inst,
@@ -1572,23 +1554,17 @@ bool DecodeVopc(uint32_t pc, std::span<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<const uint32_t> 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<int32_t>(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<const uint32_t> code, uint32_t word_ind
inst.src2.value = chan;
inst.src2.signed_val = static_cast<int32_t>(chan);
inst.src_count = 3;
return true;
}
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
@@ -15,8 +15,8 @@ bool DecodeVop3(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index
std::string* error);
bool DecodeVop3p(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error);
bool DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst, std::string* error);
void DecodeVintrp(uint32_t pc, std::span<const uint32_t> code, uint32_t word_index,
Instruction& inst);
} // namespace Libs::Graphics::ShaderRecompiler::Decoder
+10 -8
View File
@@ -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() {
+120 -43
View File
@@ -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<uint32_t> &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<uint32_t, 4> table = {0x11111111u, 0x22222222u,
0x33333333u, 0x44444444u};
const std::array<uint32_t, 4> table = {0x11111111u, 0x22222222u, 0x33333333u,
0x44444444u};
std::array<uint32_t, 32> user_data{};
const auto address = reinterpret_cast<uint64_t>(table.data());
user_data[27] = static_cast<uint32_t>(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<uint32_t, 44> table{};
std::array<uint32_t, 1> 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();