From 13e87a8e581fab566bbed7a759b12c1f407d1447 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 15 Aug 2026 08:44:20 +0200 Subject: [PATCH] graphics: implement RDNA2 alignbyte shader opcode --- CMakeLists.txt | 2 ++ .../frontend/decode/ShaderDecoder.cpp | 1 + .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 1 + .../recompiler/frontend/translate/Integer.cpp | 13 +++++++++ .../recompiler/ir/opcodes/ShaderIROpcodes.cpp | 1 + .../recompiler/ir/opcodes/ShaderIROpcodes.inc | 1 + tests/ShaderRecompilerComputeTests.cpp | 28 +++++++++++++++++++ tests/shaderCfgTests.cpp | 6 ++++ 9 files changed, 54 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60aeae8b5..e80e24f12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -517,6 +517,8 @@ if(BUILD_TESTING) add_test(NAME sync_on_address COMMAND $) add_test(NAME audio_out2_port COMMAND $) add_test(NAME shader_recompiler_compute COMMAND $) + add_test(NAME shader_recompiler_alignbyte + COMMAND $ --alignbyte-only) add_test(NAME virtual_memory_allocation COMMAND $) add_test(NAME guest_red_zone_patcher diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 712092a4c..7d41f8cc6 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -618,6 +618,7 @@ std::string OpcodeToString(Opcode opcode) { case Opcode::VBfeI32: return "v_bfe_i32"; case Opcode::VBfiB32: return "v_bfi_b32"; case Opcode::VAlignbitB32: return "v_alignbit_b32"; + case Opcode::VAlignbyteB32: return "v_alignbyte_b32"; case Opcode::VMin3F32: return "v_min3_f32"; case Opcode::VMin3I32: return "v_min3_i32"; case Opcode::VMin3U32: return "v_min3_u32"; diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index d56c4b562..98ce6b985 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -216,6 +216,7 @@ enum class Opcode { VBfeI32, VBfiB32, VAlignbitB32, + VAlignbyteB32, VMin3F32, VMin3I32, VMin3U32, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 3eb8bde0d..480d66d87 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -272,6 +272,7 @@ constexpr OpcodeMap VOP3_OPS[] = { {0x371u, Opcode::VAndOrB32}, {0x372u, Opcode::VOr3B32}, {0x377u, Opcode::VPermlane16B32}, {0x378u, Opcode::VPermlanex16B32}, {0x34bu, Opcode::VFmaF16}, {0x36du, Opcode::VAdd3U32}, + {0x14fu, Opcode::VAlignbyteB32}, }; constexpr OpcodeMap VOP3P_OPS[] = { diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index d0d9a25fe..c1a88958c 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -568,6 +568,19 @@ bool Translator::TranslateExtendedInteger(const IR::Instruction& inst) { result = ir.BitwiseOr(lo_part, hi_part); break; } + case IR::Opcode::AlignByteU32: { + const auto hi = ReadU32(inst.src[0]); + const auto lo = ReadU32(inst.src[1]); + const auto byte_offset = mask(ReadU32(inst.src[2]), 31u); + const auto bit_offset = ir.ShiftLeftLogical(byte_offset, imm(3)); + const auto concatenated = ir.PackUint2x32(lo, hi); + const auto shifted = IR::U64(ir.Emit(IR::ValueOpcode::ShiftRightLogical64, + {concatenated, mask(bit_offset, 63u)})); + const auto in_range = + IR::U1(ir.Emit(IR::ValueOpcode::ULessThan32, {byte_offset, imm(8)})); + result = ir.Select(in_range, UnpackU64(shifted)[0], imm(0)); + break; + } case IR::Opcode::ShiftLeftAddU32: result = ir.IAdd(ir.ShiftLeftLogical(ReadU32(inst.src[0]), 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 ff1328bf0..2dabf41d2 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.cpp @@ -151,6 +151,7 @@ constexpr LowerMap LOWER_OPS[] = { {Decoder::Opcode::VBfeI32, Opcode::BitFieldExtract3I32}, {Decoder::Opcode::VBfiB32, Opcode::BitFieldInsertSelectU32}, {Decoder::Opcode::VAlignbitB32, Opcode::AlignBitU32}, + {Decoder::Opcode::VAlignbyteB32, Opcode::AlignByteU32}, {Decoder::Opcode::VMin3F32, Opcode::FMin3F32}, {Decoder::Opcode::VMin3I32, Opcode::IMin3I32}, {Decoder::Opcode::VMin3U32, Opcode::UMin3U32}, diff --git a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc index b60b6e31d..a7fa629d4 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ShaderIROpcodes.inc @@ -94,6 +94,7 @@ IR_OPCODE(BitFieldInsertSelectU32, General, ExtendedInteger) IR_OPCODE(BitCompare0B32, General, ExtendedInteger) IR_OPCODE(BitCompare1B32, General, ExtendedInteger) IR_OPCODE(AlignBitU32, General, ExtendedInteger) +IR_OPCODE(AlignByteU32, General, ExtendedInteger) IR_OPCODE(ShiftLeftAddU32, General, ExtendedInteger) IR_OPCODE(AddShiftLeftU32, General, ExtendedInteger) IR_OPCODE(XorAddU32, General, ExtendedInteger) diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index eb65ab57a..d5b2701cf 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -11584,6 +11584,28 @@ TestCase VectorBfeI32ArithmeticShiftMasksField() { {O::VMovB32, O::VBfeI32, O::BufferStoreDword, O::SEndpgm}}; } +TestCase VectorAlignByteUsesFiveBitByteOffset() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovLiteral(&code, 0, 0x11223344u); + AppendVMovLiteral(&code, 1, 0x55667788u); + constexpr u32 offsets[] = {0, 1, 3, 4, 5, 7, 8, 31}; + for (u32 i = 0; i < static_cast(std::size(offsets)); i++) { + AppendVMovU32(&code, 2, offsets[i]); + AppendVop3(&code, 0x14f, 10u + i, Vgpr(0), Vgpr(1), Vgpr(2)); + AppendStoreVgpr(&code, 10u + i, i); + } + AppendEnd(&code); + + return {"VectorAlignByteUsesFiveBitByteOffset", + code, + {}, + {0x55667788u, 0x44556677u, 0x22334455u, 0x11223344u, 0x00112233u, + 0x00000011u, 0u, 0u}, + {O::VMovB32, O::VAlignbyteB32, O::BufferStoreDword, O::SEndpgm}}; +} + TestCase VectorCarryAndBitCountOps() { using O = ShaderOpcode; @@ -16901,6 +16923,7 @@ std::vector MakeCases() { AddCase(VectorShiftCountsMaskLowBits); AddCase(VectorVop3IntegerOps); AddCase(VectorBfeI32ArithmeticShiftMasksField); + AddCase(VectorAlignByteUsesFiveBitByteOffset); AddCase(VectorCarryAndBitCountOps); AddCase(VectorMbcntUsesThreadMask); AddCase(VectorAddcWritesPerLaneCarryOut); @@ -20241,6 +20264,11 @@ int main(int argc, char **argv) { vulkan.CheckGpuCommandLane(); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--alignbyte-only") == 0) { + VulkanHarness vulkan; + RunCase(&vulkan, VectorAlignByteUsesFiveBitByteOffset()); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--context-state-only") == 0) { VulkanHarness vulkan; CheckPm4PolygonOffsetRegisters(vulkan.RuntimeRenderer()); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index bd8acfceb..0714ad369 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -1461,6 +1461,8 @@ void TestNewShaderRecompilerMoreAluFamilies() { EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x14e, 50), EncodeVop3Word1(5 + 256, 6 + 256, 129), + EncodeVop3Word0(0x14f, 115), + EncodeVop3Word1(5 + 256, 6 + 256, 129), EncodeVop3Word0(0x36d, 51), EncodeVop3Word1(5 + 256, 6 + 256, 7 + 256), EncodeVop3Word0(0x169, 52), @@ -1811,6 +1813,8 @@ void TestNewShaderRecompilerMoreAluFamilies() { "new decoder did not decode VOP3 bitfield insert-select"); Check(Common::ContainsStr(result.decoded_dump, "v_alignbit_b32 v50"), "new decoder did not decode VOP3 alignbit"); + Check(Common::ContainsStr(result.decoded_dump, "v_alignbyte_b32 v115"), + "new decoder did not decode VOP3 alignbyte"); Check(Common::ContainsStr(result.decoded_dump, "v_add3_u32 v51"), "new decoder did not decode VOP3 add3"); Check(Common::ContainsStr(result.decoded_dump, "v_mul_lo_u32 v52, v5, v6"), @@ -2186,6 +2190,8 @@ void TestNewShaderRecompilerMoreAluFamilies() { "VOP3 bitfield insert-select did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "AlignBitU32 v50"), "VOP3 alignbit did not lower to IR"); + Check(Common::ContainsStr(result.ir_dump, "AlignByteU32 v115"), + "VOP3 alignbyte did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IAdd3U32 v51"), "VOP3 add3 did not lower to IR"); Check(Common::ContainsStr(result.ir_dump, "IMulU32 v52, v5, v6"),