From 280b87adeccbd1bbbe158d045c5c908fe69040ec Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 14 Oct 2026 05:20:23 +0200 Subject: [PATCH] shader: allow reverse left shifts to use shared SDWA partial writes --- .../frontend/decode/VectorAluOps.cpp | 4 +- tests/ShaderRecompilerComputeTests.cpp | 50 +++++++++++++++++++ tests/shaderCfgTests.cpp | 20 ++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index abc255194..19f836a49 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -17,7 +17,6 @@ enum class Vop2SdwaProfile { IntegerFullDestination, IntegerPartialDestination, ReverseRightShift, - ReverseLogicalLeft, Bitwise, Count, }; @@ -48,7 +47,7 @@ constexpr Vop2OpcodeInfo VOP2_OPCODE_LIST[] = { {0x17u, Opcode::V_ASHR_I32}, {0x18u, Opcode::V_ASHRREV_I32, Vop2SdwaProfile::ReverseRightShift}, {0x19u, Opcode::V_LSHL_B32}, - {0x1au, Opcode::V_LSHLREV_B32, Vop2SdwaProfile::ReverseLogicalLeft}, + {0x1au, Opcode::V_LSHLREV_B32, Vop2SdwaProfile::IntegerPartialDestination}, {0x1bu, Opcode::V_AND_B32, Vop2SdwaProfile::Bitwise}, {0x1cu, Opcode::V_OR_B32, Vop2SdwaProfile::Bitwise}, {0x1du, Opcode::V_XOR_B32, Vop2SdwaProfile::Bitwise}, @@ -1041,7 +1040,6 @@ constexpr Vop2SdwaRule VOP2_SDWA_RULES[] = { {SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false}, {SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false}, {SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false}, - {SdwaSelFull(), SdwaSelAll(), SdwaSelAll(), false, false}, // Bitwise: WriteRawU32 inserts a byte or word destination like any other partial write. {SdwaSelAll(), SdwaSelAll(), SdwaSelAll(), true, false}, }; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 81defcd48..b45a5c012 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -23215,6 +23215,52 @@ TestCase Vop2SdwaBitwiseByteDestinations(u32 wave_size) { return test; } +TestCase Vop2SdwaLshlrevByteDestinations(u32 wave_size) { + using O = ShaderOpcode; + constexpr u32 sentinel = 0xa1b2c3d4u; + TestCase test; + test.name = wave_size == 64 ? "Vop2SdwaLshlrevByteDestinations64" : "Vop2SdwaLshlrevByteDestinations32"; + test.initial = {0x44668122u}; + test.expected = test.initial; + auto &code = test.code; + const auto store = [&](u32 reg, u32 expected) { + AppendStoreVgpr(&code, reg, static_cast(test.expected.size())); + test.expected.push_back(expected); + }; + AppendBufferLoadDword(&code, 4, 30); + for (const u32 count : {6u, 7u, 39u}) { + const u32 byte = count == 6u ? 0x40u : 0x80u; + for (u32 selector = 0; selector < 4u; ++selector) { + for (u32 unused = 0; unused < 3u; ++unused) { + AppendVMovLiteral(&code, 5, sentinel); + code.push_back(EncodeVop2(0x1a, 5, 249, 4)); + // count7/byte1/preserve is captured 340a08f9 01861187 at PC4e98. + code.push_back(EncodeVop2Sdwa(InlineU32(count), selector, unused, 6, 1, + 0, 0, 0, 0, 0, 0, 1)); + const u32 shift = selector * 8u; + const u32 fill = unused == 1u && byte == 0x80u ? 0xffffff00u : 0u; + store(5, ((byte | fill) << shift) | + (unused == 2u ? sentinel & ~(0xffu << shift) : 0u)); + } + } + } + code.insert(code.end(), {EncodeVop2(0x1a, 4, 249, 4), 0x01861187u}); + store(4, 0x44668022u); // Source1 aliases the preserved destination. + AppendVMovLiteral(&code, 5, sentinel); + code.push_back(EncodeSMovB32(126, InlineU32(0))); + code.insert(code.end(), {0x340a08f9u, 0x01861187u}); + code.push_back(EncodeSMovB32(126, InlineU32(1))); + store(5, sentinel); + AppendEnd(&code); + test.initial.resize(test.expected.size()); + test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_MOV_B32, O::S_MOV_B32, + O::V_LSHLREV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {"OpShiftLeftLogical", "OpBitFieldInsert", "OpBitFieldSExtract"}; + test.compute_info.wave_size = wave_size; + test.has_compute_info = true; + return test; +} + TestCase Vop1SdwaMovByteDestinations() { using O = ShaderOpcode; @@ -39393,6 +39439,8 @@ std::vector MakeCases() { AddCase(Vop1SdwaNotPartialSourcesAndDestinations); cases.push_back(Vop2SdwaBitwiseByteDestinations(32)); cases.push_back(Vop2SdwaBitwiseByteDestinations(64)); + cases.push_back(Vop2SdwaLshlrevByteDestinations(32)); + cases.push_back(Vop2SdwaLshlrevByteDestinations(64)); AddCase(Vop1SdwaMovByteDestinations); cases.push_back(Vop1SdwaMovByteSources(32)); cases.push_back(Vop1SdwaMovByteSources(64)); @@ -45801,6 +45849,8 @@ int main(int argc, char **argv) { VulkanHarness vulkan; RunCase(&vulkan, Vop2SdwaBitwiseByteDestinations(32)); RunCase(&vulkan, Vop2SdwaBitwiseByteDestinations(64)); + RunCase(&vulkan, Vop2SdwaLshlrevByteDestinations(32)); + RunCase(&vulkan, Vop2SdwaLshlrevByteDestinations(64)); RunCase(&vulkan, Vop1SdwaNotPartialSourcesAndDestinations()); return 0; } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 2bf30993b..c66258d55 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -4705,6 +4705,26 @@ void TestNewShaderDecoderArchitecture() { byte_left_shift.src1.value == 1u, "decoder rejected or misdecoded captured V_LSHLREV_B32 byte shift"); + const uint32_t vop2_sdwa_byte_left_destination[] = { + 0x340a08f9u, 0x01861187u, // v_lshlrev_b32 v5.byte1, 7, v4.byte1; preserve + }; + Instruction byte_left_destination; + ShaderRecompiler::Decoder::DecodeInstruction(vop2_sdwa_byte_left_destination, 0u, + byte_left_destination); + Check(byte_left_destination.family == Family::VOP2 && + byte_left_destination.opcode == Opcode::V_LSHLREV_B32 && + byte_left_destination.word_count == 2u && + byte_left_destination.dst.reg == 5u && + byte_left_destination.dst.sdwa_sel == 1u && + byte_left_destination.dst.sdwa_dst_unused == 2u && + byte_left_destination.dst.explicit_sdwa_dst && + byte_left_destination.src0.kind == OperandKind::IntegerInlineConstant && + byte_left_destination.src0.value == 7u && + byte_left_destination.src0.sdwa_sel == 6u && + byte_left_destination.src1.kind == OperandKind::Vgpr && + byte_left_destination.src1.reg == 4u && byte_left_destination.src1.sdwa_sel == 1u, + "decoder rejected or misdecoded captured V_LSHLREV_B32 byte destination"); + const uint32_t mimg_nsa[] = {EncodeMimg0(0x20, 0xf) | (3u << 1u), EncodeMimg1(4, 0, 1, 8), 0x03020100u, 0x07060504u, 0x0b0a0908u};