From fbdc1e8714c1c321dd6f10216f4827cf18b1cf1c Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 20 Aug 2026 07:39:56 +0200 Subject: [PATCH] shader: implement RDNA2 scalar quad-mask reduction --- .../frontend/decode/ScalarAluOps.cpp | 1 + .../frontend/decode/ShaderDecoder.cpp | 1 + .../frontend/decode/ShaderDecoder.h | 1 + .../recompiler/frontend/translate/Integer.cpp | 15 +++++++++ .../recompiler/ir/opcodes/ShaderIROpcodes.cpp | 7 ++-- .../recompiler/ir/opcodes/ShaderIROpcodes.inc | 1 + tests/ShaderRecompilerComputeTests.cpp | 32 +++++++++++++++++++ 7 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index 8a2fcdb89..9a729d4a6 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -52,6 +52,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = { {0x20u, Opcode::S_SETPC_B64}, {0x24u, Opcode::S_AND_SAVEEXEC_B64}, {0x28u, Opcode::S_ORN2_SAVEEXEC_B64}, + {0x2du, Opcode::S_QUADMASK_B64}, {0x34u, Opcode::S_ABS_I32}, {0x37u, Opcode::S_ANDN1_SAVEEXEC_B64}, {0x3bu, Opcode::S_BITREPLICATE_B64_B32}, diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 999e2c69b..d61d26446 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -498,6 +498,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::S_FF1_I32_B64: case Opcode::S_NOT_B64: case Opcode::S_WQM_B64: + case Opcode::S_QUADMASK_B64: case Opcode::S_AND_SAVEEXEC_B32: case Opcode::S_ANDN1_SAVEEXEC_B32: case Opcode::S_AND_SAVEEXEC_B64: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 21d3b1795..63ac2f405 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -47,6 +47,7 @@ enum class Opcode { S_FF1_I32_B64, S_FLBIT_I32_B64, S_BITREPLICATE_B64_B32, + S_QUADMASK_B64, S_GETPC_B64, S_SETPC_B64, S_AND_SAVEEXEC_B32, diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index 4298db922..f66591fc1 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -469,6 +469,21 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) { replicate(ir.ShiftRightLogical(source, imm(16)))); break; } + case IR::Opcode::QuadmaskB64: { + const auto compact = [&](IR::U32 value) { + auto bits = ir.BitwiseOr(value, ir.ShiftRightLogical(value, imm(1))); + bits = ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(2))); + bits = mask(bits, 0x11111111u); + bits = mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(3))), 0x03030303u); + bits = mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(6))), 0x000f000fu); + return mask(ir.BitwiseOr(bits, ir.ShiftRightLogical(bits, imm(12))), 0xffu); + }; + const auto source = ReadU32Pair(inst.src[0]); + const auto quads = + ir.BitwiseOr(compact(source[0]), ir.ShiftLeftLogical(compact(source[1]), imm(8))); + result = ir.ConstructU64(quads, imm(0)); + break; + } case IR::Opcode::BitFieldMaskU32: { const auto count = mask(ReadU32(inst.src[0]), 31u); const auto offset = mask(ReadU32(inst.src[1]), 31u); diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp index 6798e8bb5..e968de593 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp @@ -28,6 +28,7 @@ constexpr LowerMap LOWER_OPS[] = { {Decoder::Opcode::S_FF1_I32_B64, Opcode::FindLsbU64}, {Decoder::Opcode::S_FLBIT_I32_B64, Opcode::FindMsbFromHighU64}, {Decoder::Opcode::S_BITREPLICATE_B64_B32, Opcode::BitReplicateB64B32}, + {Decoder::Opcode::S_QUADMASK_B64, Opcode::QuadmaskB64}, {Decoder::Opcode::S_NOT_B32, Opcode::BitwiseNotU32}, {Decoder::Opcode::S_NOT_B64, Opcode::BitwiseNotU64}, {Decoder::Opcode::S_ADD_U32, Opcode::IAddU32}, @@ -511,7 +512,8 @@ bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode) { case Decoder::Opcode::S_LSHL_B64: case Decoder::Opcode::S_LSHR_B64: case Decoder::Opcode::S_BFE_U64: - case Decoder::Opcode::S_WQM_B64: return true; + case Decoder::Opcode::S_WQM_B64: + case Decoder::Opcode::S_QUADMASK_B64: return true; default: return false; } } @@ -530,7 +532,8 @@ bool ScalarResultIs64Bit(Decoder::Opcode opcode) { case Decoder::Opcode::S_LSHL_B64: case Decoder::Opcode::S_LSHR_B64: case Decoder::Opcode::S_BFE_U64: - case Decoder::Opcode::S_WQM_B64: return true; + case Decoder::Opcode::S_WQM_B64: + case Decoder::Opcode::S_QUADMASK_B64: return true; default: return false; } } diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc index bba995922..a48f88155 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc @@ -78,6 +78,7 @@ IR_OPCODE(BitReverseU32, General, SimpleInteger) IR_OPCODE(BitCountU32, General, SimpleInteger) IR_OPCODE(BitCountU64, General, SimpleInteger) IR_OPCODE(BitReplicateB64B32, General, ExtendedInteger) +IR_OPCODE(QuadmaskB64, General, ExtendedInteger) IR_OPCODE(BitCountAddU32, General, ExtendedInteger) IR_OPCODE(MaskedBitCountLowU32, General, ExtendedInteger) IR_OPCODE(MaskedBitCountHighU32, General, ExtendedInteger) diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 718996fe2..1a8ff3bad 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -11711,6 +11711,37 @@ TestCase ScalarNotB64UpdatesScc() { O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase ScalarQuadmaskB64() { + using O = ShaderOpcode; + + std::vector code; + auto capture_scc = [&](u32 dst_sgpr) { + code.push_back(EncodeSop2(0x0a, dst_sgpr, InlineU32(1), InlineU32(0))); + }; + + AppendSMovLiteral(&code, 106, 0x80000011u); + AppendSMovLiteral(&code, 107, 0x000000f0u); + code.push_back(EncodeSop1(0x2d, 106, 106)); + capture_scc(0); + AppendStoreSgprPair(&code, 106, 0); + AppendStoreSgpr(&code, 0, 2); + + code.push_back(EncodeSMovB32(106, InlineU32(0))); + code.push_back(EncodeSMovB32(107, InlineU32(0))); + code.push_back(EncodeSop1(0x2d, 106, 106)); + capture_scc(0); + AppendStoreSgprPair(&code, 106, 3); + AppendStoreSgpr(&code, 0, 5); + AppendEnd(&code); + + return {"ScalarQuadmaskB64", + code, + {}, + {0x00000283u, 0, 1, 0, 0, 0}, + {O::S_MOV_B32, O::S_QUADMASK_B64, O::S_CSELECT_B32, O::V_MOV_B32, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; +} + TestCase ScalarFlbitI32B64Gpu() { using O = ShaderOpcode; @@ -18564,6 +18595,7 @@ std::vector MakeCases() { AddCase(ScalarCompareOps); AddCase(ScalarShiftAddAndMaskOps); AddCase(ScalarNotB64UpdatesScc); + AddCase(ScalarQuadmaskB64); AddCase(ScalarFlbitI32B64Gpu); AddCase(ScalarSaveExecOps); AddCase(ScalarOrn2SaveexecUsesSourceOrNotExec);