From ecc30a1f2d269254c934ba12247936d611229493 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 23 Oct 2026 13:22:50 +0200 Subject: [PATCH] shader: decode S_FLBIT_I32 and S_FLBIT_I32_I64 with a 4-op signed leading-bit search --- .../frontend/decode/ScalarAluOps.cpp | 2 + .../frontend/decode/ShaderDecoder.h | 2 + .../recompiler/frontend/translate/Integer.cpp | 28 ++++++++++--- .../recompiler/frontend/translate/Scalar.cpp | 8 ++-- .../frontend/translate/Translator.h | 2 +- tests/ShaderRecompilerComputeTests.cpp | 39 +++++++++++++++++++ 6 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index ffdbae4f6..cf8d81ea6 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -57,6 +57,8 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = { {0x14u, Opcode::S_FF1_I32_B64}, {0x15u, Opcode::S_FLBIT_I32_B32}, {0x16u, Opcode::S_FLBIT_I32_B64}, + {0x17u, Opcode::S_FLBIT_I32}, + {0x18u, Opcode::S_FLBIT_I32_I64}, {0x19u, Opcode::S_SEXT_I32_I8}, {0x1au, Opcode::S_SEXT_I32_I16}, {0x1bu, Opcode::S_BITSET0_B32}, diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index e18b89b6c..e6109136f 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -60,6 +60,8 @@ enum class Opcode { S_FF1_I32_B64, S_FLBIT_I32_B32, S_FLBIT_I32_B64, + S_FLBIT_I32, + S_FLBIT_I32_I64, S_BITREPLICATE_B64_B32, S_QUADMASK_B64, S_GETPC_B64, diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index c035f83d5..1c4ef675c 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -221,8 +221,17 @@ void Translator::V_XOR3_B32(const Decoder::Instruction& inst) { WriteOperand(DestinationOperand(inst), result); } -void Translator::S_FF_I32_B64(const Decoder::Instruction& inst, bool find_zero, bool from_msb) { +void Translator::S_FF_I32_B64(const Decoder::Instruction& inst, bool find_zero, bool from_msb, + bool sign) { auto source = ReadU32Pair(inst.src0); + if (sign) { + // A signed source sign-extends a literal; then bits equal to the sign become zero. + if (inst.src0.kind == Decoder::OperandKind::LiteralConstant) { + source[1] = ir.ShiftRightArithmetic(source[0], IR::U32(IR::Value(31u))); + } + const auto mask = ir.ShiftRightArithmetic(source[1], IR::U32(IR::Value(31u))); + source = {ir.BitwiseXor(source[0], mask), ir.BitwiseXor(source[1], mask)}; + } for (auto& word: source) { word = find_zero ? ir.BitwiseNot(word) : word; } @@ -240,12 +249,19 @@ void Translator::S_FF_I32_B64(const Decoder::Instruction& inst, bool find_zero, void Translator::V_FFBH_32(const Decoder::Instruction& inst, bool sign) { const auto source = ReadU32(inst.src0); - const auto value = sign ? ir.BitwiseXor( - source, ir.ShiftRightArithmetic(source, IR::U32(IR::Value(31u)))) - : source; - const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {value})); + if (sign) { + // Bits equal to the sign become zero; FindILsb of the reversed bits is ~0 when none. + const auto bits = + ir.BitwiseXor(source, ir.ShiftRightArithmetic(source, IR::U32(IR::Value(31u)))); + const auto reversed = ir.Emit(IR::ValueOpcode::BitReverse32, {bits}); + WriteOperand(DestinationOperand(inst), + IR::U32(ir.Emit(IR::ValueOpcode::FindILsb32, {reversed}))); + return; + } + // ResourceTracking matches this 31 - FindUMsb32 form. + const auto msb = IR::U32(ir.Emit(IR::ValueOpcode::FindUMsb32, {source})); const auto position = ir.ISub(IR::U32(IR::Value(31u)), msb); - const auto result = ir.Select(ir.INotEqual(value, IR::U32(IR::Value(0u))), position, + const auto result = ir.Select(ir.INotEqual(source, IR::U32(IR::Value(0u))), position, IR::U32(IR::Value(0xffffffffu))); WriteOperand(DestinationOperand(inst), result); } diff --git a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp index bb89d1c92..ba513f604 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp @@ -238,10 +238,12 @@ void Translator::EmitScalar(const Decoder::Instruction& inst) { return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseOr32, false, true, true); case O::S_XNOR_B32: return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseXor32, false, true, true); - case O::S_FF0_I32_B64: return S_FF_I32_B64(inst, true, false); - case O::S_FF1_I32_B64: return S_FF_I32_B64(inst, false, false); + case O::S_FF0_I32_B64: return S_FF_I32_B64(inst, true, false, false); + case O::S_FF1_I32_B64: return S_FF_I32_B64(inst, false, false, false); case O::S_FLBIT_I32_B32: return V_FFBH_32(inst, false); - case O::S_FLBIT_I32_B64: return S_FF_I32_B64(inst, false, true); + case O::S_FLBIT_I32_B64: return S_FF_I32_B64(inst, false, true, false); + case O::S_FLBIT_I32: return V_FFBH_32(inst, true); + case O::S_FLBIT_I32_I64: return S_FF_I32_B64(inst, false, true, true); case O::S_BITSET0_B32: return S_BITSET_B32(inst, false); case O::S_BITSET1_B32: return S_BITSET_B32(inst, true); diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 50e37c030..04829894c 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -182,7 +182,7 @@ private: void V_AND_OR_B32(const Decoder::Instruction& inst); void V_OR3_B32(const Decoder::Instruction& inst); void V_XOR3_B32(const Decoder::Instruction& inst); - void S_FF_I32_B64(const Decoder::Instruction& inst, bool find_zero, bool from_msb); + void S_FF_I32_B64(const Decoder::Instruction& inst, bool find_zero, bool from_msb, bool sign); void V_FFBH_32(const Decoder::Instruction& inst, bool sign); void Integer24(const Decoder::Instruction& inst, bool sign, bool addend); void V_MAD_U64_U32(const Decoder::Instruction& inst); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 351ea6e62..aa04df869 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -23119,6 +23119,43 @@ TestCase ScalarFlbitI32B32Gpu() { O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase ScalarFlbitSignedGpu() { + using O = ShaderOpcode; + + // RDNA2's S_FLBIT_I32 examples; 64-bit signs come from the high word. + constexpr u32 values32[] = {0x00000000u, 0x0000ccccu, 0xffff3333u, + 0x7fffffffu, 0x80000000u, 0xffffffffu}; + constexpr uint64_t values64[] = {0x0000000000000000ull, 0xffffffffffffffffull, + 0x0000000100000000ull, 0xffffffff7fffffffull, + 0xc000000000000000ull, 0x0000000000008000ull}; + std::vector code; + u32 out = 0; + for (const auto value : values32) { + AppendSMovLiteral(&code, 0, value); + code.push_back(EncodeSop1(0x17, 20, 0)); + AppendStoreSgpr(&code, 20, out++); + } + for (const auto value : values64) { + AppendSMovLiteral(&code, 0, static_cast(value)); + AppendSMovLiteral(&code, 1, static_cast(value >> 32u)); + code.push_back(EncodeSop1(0x18, 20, 0)); + AppendStoreSgpr(&code, 20, out++); + } + // A signed 64-bit source sign-extends a literal. + code.push_back(EncodeSop1(0x18, 20, 255)); + code.push_back(0x80000000u); + AppendStoreSgpr(&code, 20, out); + AppendEnd(&code); + + return {"ScalarFlbitSignedGpu", + code, + {}, + {0xffffffffu, 16, 16, 1, 1, 0xffffffffu, 0xffffffffu, 0xffffffffu, + 31, 32, 2, 48, 33}, + {O::S_MOV_B32, O::S_FLBIT_I32, O::S_FLBIT_I32_I64, O::V_MOV_B32, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; +} + TestCase ScalarSaveExecOps() { using O = ShaderOpcode; @@ -43766,6 +43803,7 @@ std::vector MakeCases() { AddCase(ScalarQuadmaskB64); AddCase(ScalarFlbitI32B64Gpu); AddCase(ScalarFlbitI32B32Gpu); + AddCase(ScalarFlbitSignedGpu); AddCase(ScalarSaveExecOps); AddCase(ScalarOrn2SaveexecUsesSourceOrNotExec); cases.push_back(ScalarOrn2SaveexecB32(32, 32)); @@ -50070,6 +50108,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorCompareF16Ops()); RunCase(&vulkan, ScalarBrevB64OperandsAndMasks(32)); RunCase(&vulkan, ScalarBrevB64OperandsAndMasks(64)); + RunCase(&vulkan, ScalarFlbitSignedGpu()); for (bool add : {false, true}) { RunCase(&vulkan, DsAtomic64Bounds(add)); for (u32 wave_size : {32, 64}) {