From cfb110e0aabbddeb29e40a4aa8e6ff90cbc845ff Mon Sep 17 00:00:00 2001 From: Elija <143048359+ElijaOwO@users.noreply.github.com> Date: Sun, 20 Sep 2026 16:21:40 +0200 Subject: [PATCH] shader: allow partial SDWA destinations for V_NOT_B32 (#684) --- .../frontend/decode/VectorAluOps.cpp | 3 +- tests/ShaderRecompilerComputeTests.cpp | 24 ++++++++ tests/shaderCfgTests.cpp | 57 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 1cf34224..f4215141 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -521,7 +521,8 @@ constexpr Vop1SdwaRule VOP1_SDWA_RULES[] = { {Opcode::V_CVT_F32_U32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, {Opcode::V_CVT_F32_I32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, {Opcode::V_CVT_F32_UBYTE0, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, - {Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, + {Opcode::V_NOT_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelBytes() | SdwaSelWords(), + SdwaSelFull(), false}, {Opcode::V_FFBL_B32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), 0, 0, false}, {Opcode::V_CVT_F32_F16, SdwaSelWords() | SdwaSelFull(), 0, 0, true}, {Opcode::V_CVT_F16_F32, SdwaSelBytes() | SdwaSelWords() | SdwaSelFull(), SdwaSelWords(), diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 92f7e47f..8bfab528 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -18065,6 +18065,29 @@ TestCase Vop1SdwaNotCapturedByte0Source() { return test; } +TestCase Vop1SdwaNotPreservesHighWordDestination() { + using O = ShaderOpcode; + + std::vector code; + AppendBufferLoadDword(&code, 0, 30); + AppendVMovLiteral(&code, 3, 0xabcd5555u); + code.push_back(0x7e066ef9u); + code.push_back(0x00061400u); + AppendStoreVgpr(&code, 3, 0); + AppendEnd(&code); + + TestCase test; + test.name = "Vop1SdwaNotPreservesHighWordDestination"; + test.code = std::move(code); + test.initial = {0x12345678u}; + test.expected = {0xabcda987u}; + test.opcodes = {O::BUFFER_LOAD_DWORD, O::V_MOV_B32, O::V_NOT_B32, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.ir_counts = {{" = BitwiseNot32 ", 1}}; + test.required_spirv = {"OpNot"}; + return test; +} + TestCase Vop1SdwaMovByteDestinations() { using O = ShaderOpcode; @@ -27176,6 +27199,7 @@ std::vector MakeCases() { AddCase(VectorFfbhI32NativeAndVop3OnGpu); AddCase(Vop1SdwaFfblCapturedHighWordSource); AddCase(Vop1SdwaNotCapturedByte0Source); + AddCase(Vop1SdwaNotPreservesHighWordDestination); AddCase(Vop1SdwaMovByteDestinations); AddCase(Vop2SdwaSubNcExactByte2Destination); AddCase(Vop2SdwaAddNcCapturedHighWordDestination); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 1182859b..c687799c 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -3435,6 +3435,62 @@ void TestNewShaderRecompilerCapturedVop1SdwaByteConvert() { CheckSpirvBinaryValidates(result.spirv); } +void TestNewShaderRecompilerVop1SdwaNotDestination() { + auto options = MakeCompileOptions(ShaderType::Pixel); + + const auto check_destination = [&](uint32_t modifier, uint32_t dst_sel, + uint32_t dst_unused) { + const uint32_t shader[] = { + 0x7e066ef9u, modifier, EncodeExp0(0x00, 0x1), + EncodeExp1(3, 0, 0, 0), EncodeSopp(0x01), + }; + + ShaderRecompiler::Decoder::Instruction decoded; + ShaderRecompiler::Decoder::DecodeInstruction(shader, 0, decoded); + Check(decoded.opcode == ShaderRecompiler::Decoder::Opcode::V_NOT_B32, + "V_NOT_B32 SDWA opcode did not decode"); + Check(decoded.word_count == 2u, + "V_NOT_B32 SDWA instruction has the wrong word count"); + Check(decoded.dst.kind == ShaderRecompiler::Decoder::OperandKind::Vgpr && + decoded.dst.reg == 3u && decoded.dst.sdwa_sel == dst_sel && + decoded.dst.sdwa_dst_unused == dst_unused && + decoded.dst.explicit_sdwa_dst, + "V_NOT_B32 SDWA destination metadata is incorrect"); + Check(decoded.src0.kind == ShaderRecompiler::Decoder::OperandKind::Vgpr && + decoded.src0.reg == 0u && decoded.src0.sdwa_sel == 6u && + !decoded.src0.negate && !decoded.src0.absolute, + "V_NOT_B32 SDWA source metadata is incorrect"); + + auto result = RecompileForTest(shader, options); + CheckSpirvBinaryValidates(result.spirv); + }; + + check_destination(0x00061400u, 4u, 2u); + check_destination(0x00060500u, 5u, 0u); + check_destination(0x00060000u, 0u, 0u); + + const auto check_rejected = [&](uint32_t modifier, const char *message) { + const uint32_t instruction[] = {0x7e066ef9u, modifier}; + + ShaderRecompiler::Decoder::Instruction decoded; + ShaderRecompiler::Decoder::DecodeInstruction(instruction, 0, decoded); + Check(decoded.word_count == 2u && + decoded.opcode == ShaderRecompiler::Decoder::Opcode::UNSUPPORTED, + message); + }; + + check_rejected(0x00060700u, "V_NOT_B32 SDWA accepted destination selector 7"); + check_rejected(0x00070600u, "V_NOT_B32 SDWA accepted source selector 7"); + check_rejected(0x00061c00u, + "V_NOT_B32 SDWA accepted destination unused value 3"); + check_rejected(0x00160400u, "V_NOT_B32 SDWA accepted source negation"); + check_rejected(0x00260400u, "V_NOT_B32 SDWA accepted source absolute"); + check_rejected(0x00062400u, "V_NOT_B32 SDWA accepted clamp"); + check_rejected(0x00064400u, "V_NOT_B32 SDWA accepted output modifier"); + check_rejected(0x00000400u, + "V_NOT_B32 SDWA partial destination accepted a byte source"); +} + void TestNewShaderRecompilerBootB16PackedAndSdwaOpcodes() { const uint32_t shader[] = { 0xd7070001u, @@ -13397,6 +13453,7 @@ int main() { TestNewShaderRecompilerDsReadWrite2Translation(); TestNewShaderRecompilerDsWideAndAtomicTranslation(); TestNewShaderRecompilerCapturedVop1SdwaByteConvert(); + TestNewShaderRecompilerVop1SdwaNotDestination(); TestNewShaderRecompilerScalarMemoryBindingDomains(); // Opcode semantics and optimized SPIR-V are exercised by // ShaderRecompilerComputeTests; keep the distinct decoder contract checks