shader: implement RDNA2 scalar 16-bit sign extension

This commit is contained in:
Claxten 2026-10-06 04:28:20 +04:00 committed by nmzik
parent cd06601022
commit 5601861d67
5 changed files with 64 additions and 0 deletions

View File

@ -53,6 +53,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = {
{0x14u, Opcode::S_FF1_I32_B64},
{0x15u, Opcode::S_FLBIT_I32_B32},
{0x16u, Opcode::S_FLBIT_I32_B64},
{0x1au, Opcode::S_SEXT_I32_I16},
{0x1bu, Opcode::S_BITSET0_B32},
{0x1cu, Opcode::S_BITSET0_B64},
{0x1du, Opcode::S_BITSET1_B32},

View File

@ -503,6 +503,7 @@ std::string InstructionToString(const Instruction& inst) {
OperandToString(inst.dst).c_str(),
OperandToString(inst.src0).c_str()));
case Opcode::S_ABS_I32:
case Opcode::S_SEXT_I32_I16:
case Opcode::S_BREV_B32:
case Opcode::S_BREV_B64:
case Opcode::S_BCNT1_I32_B32:

View File

@ -45,6 +45,7 @@ enum class Opcode {
S_CMOV_B64,
S_MOVK_I32,
S_ABS_I32,
S_SEXT_I32_I16,
S_ABSDIFF_I32,
S_BREV_B32,
S_BREV_B64,

View File

@ -134,6 +134,10 @@ void Translator::EmitScalar(const Decoder::Instruction& inst) {
case O::S_ABS_I32:
return SimpleInteger(inst, IR::ValueOpcode::IAbs32, IR::Type::U32, false, false, true);
case O::S_SEXT_I32_I16:
WriteOperand(inst.dst, IR::U32(ir.Emit(IR::ValueOpcode::BitFieldSExtract,
{ReadU32(inst.src0), IR::Value(0u), IR::Value(16u)})));
return;
case O::S_MUL_I32:
case O::S_MULK_I32:
return SimpleInteger(inst, IR::ValueOpcode::IMul32, IR::Type::U32, false, false, false);

View File

@ -20328,6 +20328,57 @@ TestCase ScalarBrevB32PreservesScc() {
O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}};
}
TestCase ScalarSextI16Captured() {
using O = ShaderOpcode;
TestCase test;
test.name = "ScalarSextI16Captured";
constexpr u32 wave_size = 64, high = 0x80000001u;
test.initial = {0xdead0000u, 0xabcd7fffu, 0x12348000u, 0xabcfffffu};
test.expected = test.initial;
auto &code = test.code;
for (u32 i = 0; i < 4u; ++i) {
const auto extended = static_cast<u32>(static_cast<int32_t>(static_cast<int16_t>(test.initial[i])));
for (const bool scc : {false, true}) {
AppendVMovU32(&code, 30, i * 4u);
AppendBufferLoadDword(&code, 3, 30);
code.push_back(EncodeVop1(0x02, 11, Vgpr(3)));
AppendSMovLiteral(&code, 107, high);
code.push_back(EncodeSopc(0x06, InlineU32(scc ? 1u : 0u), InlineU32(1)));
code.push_back(EncodeSop1(0x04, 126, 128)); // Scalar operations execute with empty EXEC.
code.push_back(0xbeea1a0bu); // Captured s_sext_i32_i16 vcc_lo, s11.
code.push_back(EncodeSMovB32(20, 106));
code.push_back(EncodeSMovB32(21, 107));
code.push_back(EncodeSMovB32(22, 253));
code.push_back(EncodeSop1(0x04, 126, 193));
AppendVMovU32(&code, 1, 1);
code.push_back(EncodeVop2(0x01, 2, InlineU32(0), 1));
const u32 expected[] = {extended, high, scc ? 1u : 0u};
for (u32 word = 0; word < 3u; ++word) {
AppendStoreSgprAtLaneDwordOffset(&code, 20u + word, 0, test.expected.size());
test.expected.insert(test.expected.end(), wave_size, expected[word]);
}
AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, test.expected.size());
const uint64_t mask = (uint64_t{high} << 32u) | extended;
for (u32 lane = 0; lane < wave_size; ++lane)
test.expected.push_back(static_cast<u32>((mask >> lane) & 1u));
}
}
AppendEnd(&code);
test.initial.resize(test.expected.size());
test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_READFIRSTLANE_B32, O::S_MOV_B32,
O::S_MOV_B64, O::S_CMP_EQ_U32, O::S_SEXT_I32_I16,
O::V_CNDMASK_B32, O::V_MOV_B32, O::V_ADD_NC_U32,
O::V_LSHLREV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.decoded_counts = {{"S_SEXT_I32_I16", 8u}};
test.required_spirv = {"OpBitFieldSExtract"};
test.compute_info.threads_num[0] = wave_size;
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1;
test.compute_info.thread_ids_num = 1;
test.compute_info.wave_size = wave_size;
test.has_compute_info = true;
return test;
}
TestCase ScalarBrevB64OperandsAndMasks(u32 wave_size) {
using O = ShaderOpcode;
TestCase test;
@ -35081,6 +35132,7 @@ std::vector<TestCase> MakeCases() {
AddCase(ScalarBitcmpB64DynamicOperands);
AddCase(ScalarBitcmpB64IntegerConstants);
AddCase(ScalarBrevB32PreservesScc);
AddCase(ScalarSextI16Captured);
cases.push_back(ScalarBrevB64OperandsAndMasks(32));
cases.push_back(ScalarBrevB64OperandsAndMasks(64));
AddCase(ScalarBfeI32CapturedRawSignExtends);
@ -40720,6 +40772,11 @@ int main(int argc, char **argv) {
}
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--scalar-sext-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, ScalarSextI16Captured());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--flat-d16-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, FlatLoadDlcCaptured(32));