From 138d5b86c40cb3adc47acde4f1cffa0ee21357f4 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 28 Oct 2026 22:31:16 +0100 Subject: [PATCH] shader: support S_QUADMASK_B32 --- CMakeLists.txt | 2 + .../frontend/decode/ScalarAluOps.cpp | 1 + .../frontend/decode/ShaderDecoder.cpp | 1 + .../frontend/decode/ShaderDecoder.h | 1 + .../recompiler/frontend/translate/Integer.cpp | 19 ++++--- .../recompiler/frontend/translate/Scalar.cpp | 3 +- .../frontend/translate/Translator.h | 2 +- tests/ShaderRecompilerComputeTests.cpp | 49 +++++++++++++++++++ 8 files changed, 69 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5c547bc1..b3366e2b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -694,6 +694,8 @@ if(BUILD_TESTING) COMMAND $ --float-min-max-only) add_test(NAME shader_recompiler_vop3_conversions COMMAND $ --vop3-conversions-only) + add_test(NAME shader_recompiler_quadmask + COMMAND $ --quadmask-only) add_test(NAME shader_recompiler_alignbyte COMMAND $ --alignbyte-only) add_test(NAME graphics_pipeline_rasterization diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index cf8d81ea6..a3400b7b7 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -70,6 +70,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = { {0x21u, Opcode::S_SWAPPC_B64}, {0x24u, Opcode::S_AND_SAVEEXEC_B64}, {0x28u, Opcode::S_ORN2_SAVEEXEC_B64}, + {0x2cu, Opcode::S_QUADMASK_B32}, {0x2du, Opcode::S_QUADMASK_B64}, {0x34u, Opcode::S_ABS_I32}, {0x37u, Opcode::S_ANDN1_SAVEEXEC_B64}, diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 078e41f5b..e00b5731e 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -517,6 +517,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::S_NOT_B64: case Opcode::S_WQM_B32: case Opcode::S_WQM_B64: + case Opcode::S_QUADMASK_B32: case Opcode::S_QUADMASK_B64: case Opcode::S_AND_SAVEEXEC_B32: case Opcode::S_ORN2_SAVEEXEC_B32: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index faf563fcb..e4450c093 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -63,6 +63,7 @@ enum class Opcode { S_FLBIT_I32, S_FLBIT_I32_I64, S_BITREPLICATE_B64_B32, + S_QUADMASK_B32, S_QUADMASK_B64, S_GETPC_B64, S_SETPC_B64, diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index 5493242e3..7aa54212a 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -375,7 +375,7 @@ void Translator::S_BITREPLICATE_B64_B32(const Decoder::Instruction& inst) { WriteOperand(DestinationOperand(inst), ir.ConstructU64(replicate(low), replicate(high))); } -void Translator::S_QUADMASK_B64(const Decoder::Instruction& inst) { +void Translator::S_QUADMASK(const Decoder::Instruction& inst, bool is64) { const auto compact = [&](IR::U32 value) { auto bits = ir.BitwiseOr(value, ir.ShiftRightLogical(value, IR::U32(IR::Value(1u)))); bits = ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, IR::U32(IR::Value(2u)))); @@ -388,12 +388,17 @@ void Translator::S_QUADMASK_B64(const Decoder::Instruction& inst) { ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, IR::U32(IR::Value(12u)))), IR::U32(IR::Value(0xffu))); }; - const auto source = ReadU32Pair(inst.src0); - const auto quads = ir.BitwiseOr( - compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), IR::U32(IR::Value(8u)))); - const auto result = ir.ConstructU64(quads, IR::U32(IR::Value(0u))); - WriteOperand(DestinationOperand(inst), result); - ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}))); + IR::U32 quads; + if (is64) { + const auto source = ReadU32Pair(inst.src0); + quads = ir.BitwiseOr( + compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), IR::U32(IR::Value(8u)))); + WriteOperand(DestinationOperand(inst), ir.ConstructU64(quads, IR::U32(IR::Value(0u)))); + } else { + quads = compact(ReadU32(inst.src0)); + WriteOperand(DestinationOperand(inst), quads); + } + ir.SetScc(ir.INotEqual(quads, IR::U32(IR::Value(0u)))); } void Translator::BFM_B32(const Decoder::Instruction& inst) { diff --git a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp index 53bb07bb7..73d4324ab 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp @@ -248,7 +248,8 @@ void Translator::EmitScalar(const Decoder::Instruction& inst) { case O::S_BITSET0_B64: return S_BITSET_B64(inst, false); case O::S_BITSET1_B64: return S_BITSET_B64(inst, true); case O::S_BITREPLICATE_B64_B32: return S_BITREPLICATE_B64_B32(inst); - case O::S_QUADMASK_B64: return S_QUADMASK_B64(inst); + case O::S_QUADMASK_B32: return S_QUADMASK(inst, false); + case O::S_QUADMASK_B64: return S_QUADMASK(inst, true); case O::S_BFM_B32: return BFM_B32(inst); case O::S_BFM_B64: return S_BFM_B64(inst); case O::S_BFE_U32: return S_BFE_U32(inst, false); diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 349df316b..89eb0c4d0 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -196,7 +196,7 @@ private: void V_BCNT_U32_B32(const Decoder::Instruction& inst); void V_MBCNT_U32_B32(const Decoder::Instruction& inst, bool low); void S_BITREPLICATE_B64_B32(const Decoder::Instruction& inst); - void S_QUADMASK_B64(const Decoder::Instruction& inst); + void S_QUADMASK(const Decoder::Instruction& inst, bool is64); void BFM_B32(const Decoder::Instruction& inst); IR::U32 RightMask32(IR::U32 count); void S_BFM_B64(const Decoder::Instruction& inst); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 869af69e1..33868d5b9 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -23748,6 +23748,46 @@ TestCase ScalarNotB64UpdatesScc() { O::S_ENDPGM}}; } +TestCase ScalarQuadmaskB32(u32 wave_size) { + using O = ShaderOpcode; + TestCase test; + test.name = wave_size == 32 ? "ScalarQuadmaskB32Wave32" : "ScalarQuadmaskB32Wave64"; + test.initial = {0, 0xffffffffu, 0x80000011u, 0x01010101u}; + for (u32 bit = 0; bit < 32; ++bit) test.initial.push_back(1u << bit); + test.expected = test.initial; + auto &code = test.code; + for (u32 i = 0; i < test.initial.size(); ++i) { + u32 quads = 0; + for (u32 q = 0; q < 8; ++q) { + if ((test.initial[i] >> (4 * q)) & 15u) quads |= 1u << q; + } + AppendVMovU32(&code, 30, i * sizeof(u32)); + AppendBufferLoadDword(&code, 2, 30); + for (u32 dst : {12u, 106u}) { + code.push_back(EncodeVop1(0x02, dst, Vgpr(2))); + AppendSMovLiteral(&code, dst + 1, 0xdeadbeefu); + code.push_back(EncodeSopc(0x06, InlineU32(quads == 0), InlineU32(1))); + code.push_back(EncodeSop1(0x2c, dst, dst)); + code.push_back(EncodeSop2(0x0a, 14, InlineU32(1), InlineU32(0))); + AppendStoreSgpr(&code, dst, static_cast(test.expected.size())); + AppendStoreSgpr(&code, dst + 1, static_cast(test.expected.size()) + 1); + AppendStoreSgpr(&code, 14, static_cast(test.expected.size()) + 2); + test.expected.insert(test.expected.end(), {quads, 0xdeadbeefu, quads != 0}); + } + } + code.push_back(EncodeSop1(0x2c, 12, 255)); + code.push_back(0x000f000fu); + AppendStoreSgpr(&code, 12, static_cast(test.expected.size())); + test.expected.push_back(0x11u); + AppendEnd(&code); + test.opcodes = {O::S_QUADMASK_B32, O::S_CMP_EQ_U32, O::S_CSELECT_B32, O::S_MOV_B32, + O::V_READFIRSTLANE_B32, O::V_MOV_B32, O::BUFFER_LOAD_DWORD, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.compute_info.wave_size = wave_size; + test.has_compute_info = true; + return test; +} + TestCase ScalarQuadmaskB64() { using O = ShaderOpcode; @@ -46047,6 +46087,8 @@ std::vector MakeCases() { AddCase(ScalarCompareOps); AddCase(ScalarShiftAddAndMaskOps); AddCase(ScalarNotB64UpdatesScc); + cases.push_back(ScalarQuadmaskB32(32)); + cases.push_back(ScalarQuadmaskB32(64)); AddCase(ScalarQuadmaskB64); AddCase(ScalarFlbitI32B64Gpu); AddCase(ScalarFlbitI32B32Gpu); @@ -52562,6 +52604,13 @@ int main(int argc, char **argv) { std::setvbuf(stdout, nullptr, _IONBF, 0); EnsureConfigInitialized(); CheckLeastRecentlyUsedCacheOrdering(); + if (argc == 2 && std::strcmp(argv[1], "--quadmask-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, ScalarQuadmaskB32(32)); + RunCase(&vulkan, ScalarQuadmaskB32(64)); + RunCase(&vulkan, ScalarQuadmaskB64()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--vop3-conversions-only") == 0) { VulkanHarness vulkan; RunCase(&vulkan, Vop3FloatConversions(32));