diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index b3530caa3..534f92ec8 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -20,10 +20,10 @@ constexpr OpcodeMap SOP2_OPCODE_LIST[] = { {0x1du, Opcode::SXnorB64}, {0x1eu, Opcode::SLshlB32}, {0x1fu, Opcode::SLshlB64}, {0x20u, Opcode::SLshrB32}, {0x21u, Opcode::SLshrB64}, {0x22u, Opcode::SAshrI32}, {0x24u, Opcode::SBfmB32}, {0x25u, Opcode::SBfmB64}, {0x26u, Opcode::SMulI32}, - {0x27u, Opcode::SBfeU32}, {0x29u, Opcode::SBfeU64}, {0x2eu, Opcode::SLshl1AddU32}, - {0x2fu, Opcode::SLshl2AddU32}, {0x30u, Opcode::SLshl3AddU32}, {0x31u, Opcode::SLshl4AddU32}, - {0x32u, Opcode::SPackLlB32B16}, {0x33u, Opcode::SPackLhB32B16}, {0x34u, Opcode::SPackHhB32B16}, - {0x35u, Opcode::SMulHiU32}, + {0x27u, Opcode::SBfeU32}, {0x28u, Opcode::SBfeI32}, {0x29u, Opcode::SBfeU64}, + {0x2eu, Opcode::SLshl1AddU32}, {0x2fu, Opcode::SLshl2AddU32}, {0x30u, Opcode::SLshl3AddU32}, + {0x31u, Opcode::SLshl4AddU32}, {0x32u, Opcode::SPackLlB32B16}, {0x33u, Opcode::SPackLhB32B16}, + {0x34u, Opcode::SPackHhB32B16}, {0x35u, Opcode::SMulHiU32}, }; constexpr OpcodeMap SOP1_OPCODE_LIST[] = { diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index d0b1cf8ba..14df6cd39 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -516,6 +516,7 @@ std::string OpcodeToString(Opcode opcode) { case Opcode::SMulHiU32: return "s_mul_hi_u32"; case Opcode::SMulkI32: return "s_mulk_i32"; case Opcode::SBfeU32: return "s_bfe_u32"; + case Opcode::SBfeI32: return "s_bfe_i32"; case Opcode::SBfeU64: return "s_bfe_u64"; case Opcode::SBfmB32: return "s_bfm_b32"; case Opcode::SBfmB64: return "s_bfm_b64"; diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index f5c0c9878..f93532db5 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -100,6 +100,7 @@ enum class Opcode { SMulHiU32, SMulkI32, SBfeU32, + SBfeI32, SBfeU64, SBfmB32, SBfmB64, diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index 8a14ac055..bfc42afec 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -515,14 +515,18 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) { result = ir.Emit(IR::ValueOpcode::ShiftLeftLogical64, {right_mask64(count), offset}); break; } - case IR::Opcode::BitFieldExtractU32: { + case IR::Opcode::BitFieldExtractU32: + case IR::Opcode::BitFieldExtractI32: { const auto source = ReadU32(inst.src[0]); const auto field = ReadU32(inst.src[1]); const auto offset = extract(field, imm(0), imm(5), false); const auto count = IR::U32(ir.Emit(IR::ValueOpcode::UMin32, {extract(field, imm(16), imm(7), false), ir.ISub(imm(32), offset)})); - result = ir.Emit(IR::ValueOpcode::BitFieldUExtract, {source, offset, count}); + const auto opcode = inst.op == IR::Opcode::BitFieldExtractI32 + ? IR::ValueOpcode::BitFieldSExtract + : IR::ValueOpcode::BitFieldUExtract; + result = ir.Emit(opcode, {source, offset, count}); break; } case IR::Opcode::BitFieldExtractU64: { diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp index c6d189d26..f4d2b3941 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp @@ -63,6 +63,7 @@ constexpr LowerMap LOWER_OPS[] = { {Decoder::Opcode::SMulHiU32, Opcode::UMulHighU32}, {Decoder::Opcode::SMulkI32, Opcode::IMulU32}, {Decoder::Opcode::SBfeU32, Opcode::BitFieldExtractU32}, + {Decoder::Opcode::SBfeI32, Opcode::BitFieldExtractI32}, {Decoder::Opcode::SBfeU64, Opcode::BitFieldExtractU64}, {Decoder::Opcode::SBfmB32, Opcode::BitFieldMaskU32}, {Decoder::Opcode::SBfmB64, Opcode::BitFieldMaskU64}, @@ -492,6 +493,7 @@ bool ScalarResultWritesSccNonZero(Decoder::Opcode opcode) { case Decoder::Opcode::SLshrB32: case Decoder::Opcode::SAshrI32: case Decoder::Opcode::SBfeU32: + case Decoder::Opcode::SBfeI32: case Decoder::Opcode::SAndB64: case Decoder::Opcode::SAndn2B64: case Decoder::Opcode::SNotB64: diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc index 446c976b7..83f80ad1d 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc @@ -88,6 +88,7 @@ IR_OPCODE(FindMsbFromHighU64, General, ComposedInteger) IR_OPCODE(BitFieldMaskU32, General, ExtendedInteger) IR_OPCODE(BitFieldMaskU64, General, ExtendedInteger) IR_OPCODE(BitFieldExtractU32, General, ExtendedInteger) +IR_OPCODE(BitFieldExtractI32, General, ExtendedInteger) IR_OPCODE(BitFieldExtractU64, General, ExtendedInteger) IR_OPCODE(BitFieldExtract3U32, General, ExtendedInteger) IR_OPCODE(BitFieldExtract3I32, General, ExtendedInteger) diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index be8240017..333f4143d 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -11164,6 +11164,36 @@ TestCase ScalarBrevB32PreservesScc() { O::BufferStoreDword, O::SEndpgm}}; } +TestCase ScalarBfeI32CapturedRawSignExtends() { + using O = ShaderOpcode; + + std::vector code; + AppendBufferLoadDword(&code, 0, 30); + code.push_back(EncodeVop1(0x02, 20, Vgpr(0))); + code.push_back(0x946aff14u); // s_bfe_i32 vcc_lo, s20, literal + code.push_back(0x0004001cu); // offset 28, width 4 + code.push_back(EncodeSop2(0x0a, 21, InlineU32(1), InlineU32(0))); + code.push_back(EncodeSop2(0x28, 22, 20, 255u)); + code.push_back(0u); // zero width produces zero and clears SCC + code.push_back(EncodeSop2(0x0a, 23, InlineU32(1), InlineU32(0))); + AppendStoreSgpr(&code, 106, 1); + AppendStoreSgpr(&code, 21, 2); + AppendStoreSgpr(&code, 22, 3); + AppendStoreSgpr(&code, 23, 4); + AppendEnd(&code); + + TestCase test; + test.name = "ScalarBfeI32CapturedRawSignExtends"; + test.code = std::move(code); + test.initial = {0xf0000000u, 0, 0, 0, 0}; + test.expected = {0xf0000000u, 0xffffffffu, 1, 0, 0}; + test.opcodes = {O::BufferLoadDword, O::VReadfirstlaneB32, O::SBfeI32, + O::SCselectB32, O::VMovB32, O::BufferStoreDword, + O::SEndpgm}; + test.required_spirv = {"OpBitFieldSExtract"}; + return test; +} + TestCase BitfieldExtractWidthPastEndEdges() { using O = ShaderOpcode; @@ -16981,6 +17011,7 @@ std::vector MakeCases() { AddCase(ScalarGetpcWritesNextInstructionPc); AddCase(ScalarBitfieldPack); AddCase(ScalarBrevB32PreservesScc); + AddCase(ScalarBfeI32CapturedRawSignExtends); AddCase(BitfieldExtractWidthPastEndEdges); AddCase(Scalar64BitOps); AddCase(ScalarAndn2B64SccBranch); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 3811164a5..6dce0ee0c 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -3356,6 +3356,20 @@ void TestNewShaderDecoderArchitecture() { Check(literal.word_count == 2u && literal.src0.value == 0x12345678u, "single-instruction decoder lost a compact literal extension"); + const uint32_t scalar_signed_bfe[] = { + 0x946aff14u, // s_bfe_i32 vcc_lo, s20, 0x0004001c + 0x0004001cu, + }; + Instruction signed_bfe; + Check(DecodeInstruction(scalar_signed_bfe, 0u, signed_bfe, &error), + error.c_str()); + Check(signed_bfe.family == Family::SOP2 && + signed_bfe.opcode == Opcode::SBfeI32 && + signed_bfe.word_count == 2u && + signed_bfe.dst.kind == OperandKind::VccLo && + signed_bfe.src0.reg == 20u && signed_bfe.src1.value == 0x0004001cu, + "decoder rejected or misdecoded captured S_BFE_I32 instruction"); + const uint32_t mimg_nsa[] = {EncodeMimg0(0x20, 0xf) | (3u << 1u), EncodeMimg1(4, 0, 1, 8), 0x03020100u, 0x07060504u, 0x0b0a0908u};