From a8e3c29b2e566821d61af7a6d3ac56f71c89b90f Mon Sep 17 00:00:00 2001 From: Chai T Date: Sat, 17 Oct 2026 02:39:21 +0000 Subject: [PATCH] shader: implement V_ASHRREV_I64 (VOP3 0x301) --- .../frontend/decode/ShaderDecoder.h | 1 + .../frontend/decode/VectorAluOps.cpp | 2 + .../recompiler/frontend/translate/Integer.cpp | 6 ++ .../recompiler/frontend/translate/Vector.cpp | 3 + tests/ShaderRecompilerComputeTests.cpp | 76 +++++++++++++++++-- tests/shaderCfgTests.cpp | 37 +++++++++ 6 files changed, 120 insertions(+), 5 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 8cea8b447..faf563fcb 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -354,6 +354,7 @@ enum class Opcode { V_ASHRREV_I32, V_LSHLREV_B64, V_LSHRREV_B64, + V_ASHRREV_I64, V_LSHLREV_B16, V_LSHRREV_B16, V_ASHRREV_I16, diff --git a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp index 2e4433129..c9ca8d5c8 100644 --- a/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/VectorAluOps.cpp @@ -354,6 +354,7 @@ constexpr OpcodeMap VOP3_OPCODE_LIST[] = { {0x16cu, Opcode::V_MUL_HI_I32}, {0x2ffu, Opcode::V_LSHLREV_B64}, {0x300u, Opcode::V_LSHRREV_B64}, + {0x301u, Opcode::V_ASHRREV_I64}, {0x303u, Opcode::V_ADD_NC_U16}, {0x304u, Opcode::V_SUB_NC_U16}, {0x305u, Opcode::V_MUL_LO_U16}, @@ -1335,6 +1336,7 @@ uint32_t NativeVop3SourceCount(Opcode opcode) { case Opcode::V_SUB_NC_I16: case Opcode::V_LSHLREV_B64: case Opcode::V_LSHRREV_B64: + case Opcode::V_ASHRREV_I64: case Opcode::V_LSHLREV_B16: case Opcode::V_LSHRREV_B16: case Opcode::V_ASHRREV_I16: diff --git a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp index 1aa68a39d..5493242e3 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Integer.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Integer.cpp @@ -147,6 +147,12 @@ void Translator::SimpleInteger(const Decoder::Instruction& inst, IR::ValueOpcode const auto arg_type = IR::ArgTypeOf(opcode, index); const auto& operand = SourceAt(inst, reverse && index < 2u ? 1u - index : index); args[index] = ReadOperand(operand, arg_type == IR::Type::Void ? type : arg_type); + if (opcode == IR::ValueOpcode::ShiftRightArithmetic64 && index == 0u && + operand.kind == Decoder::OperandKind::LiteralConstant) { + args[index] = ir.ConstructU64( + ReadU32(operand), + IR::U32(IR::Value((operand.value & 0x80000000u) ? 0xffffffffu : 0u))); + } if (mask_shift_count && index == 1u) { args[index] = ir.BitwiseAnd(IR::U32(args[index]), IR::U32(IR::Value(31u))); } diff --git a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp index bfb234c69..4490b2324 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Vector.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Vector.cpp @@ -565,6 +565,9 @@ void Translator::EmitVector(const Decoder::Instruction& inst) { case O::V_LSHRREV_B64: return SimpleInteger(inst, IR::ValueOpcode::ShiftRightLogical64, IR::Type::U64, true, false, false); + case O::V_ASHRREV_I64: + return SimpleInteger(inst, IR::ValueOpcode::ShiftRightArithmetic64, IR::Type::U64, true, + false, false); case O::V_XNOR_B32: return ComposedIntegerBinary(inst, IR::ValueOpcode::BitwiseXor32, false, true, false); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 9336718d2..b4bf25295 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -26927,6 +26927,67 @@ TestCase VectorVop3LshlrevB64Captured() { return test; } +TestCase VectorVop3AshrrevI64Operands() { + using O = ShaderOpcode; + TestCase test; + test.name = "VectorVop3AshrrevI64Operands"; + test.initial = {33u, 0x89abcdefu, 0xfedcba98u}; + test.expected = test.initial; + auto &code = test.code; + const auto store_result = [&](u32 dst, uint64_t value, u32 count) { + const auto output = static_cast(test.expected.size()); + AppendStoreVgpr(&code, dst, output); + AppendStoreVgpr(&code, dst + 1u, output + 1u); + const auto expected = ReferenceAshrI64(value, count); + test.expected.push_back(static_cast(expected)); + test.expected.push_back(static_cast(expected >> 32u)); + }; + + constexpr uint64_t value = 0xfedcba9889abcdefull; + AppendVMovU32(&code, 30, sizeof(u32)); + AppendBufferLoadDword(&code, 2, 30); + AppendVMovU32(&code, 30, 2u * sizeof(u32)); + AppendBufferLoadDword(&code, 3, 30); + code.push_back(0xd7010007u); + code.push_back(0x00020498u); + store_result(7, value, 24u); + + AppendSMovLiteral(&code, 106, static_cast(value)); + AppendSMovLiteral(&code, 107, static_cast(value >> 32u)); + AppendVMovU32(&code, 12, 68); + code.push_back(0xd7010001u); + code.push_back(0x0000d50cu); + store_result(1, value, 68u); + + AppendVMovU32(&code, 30, 0); + AppendBufferLoadDword(&code, 12, 30); + for (const u32 literal : {0u, 0x7fffffffu, 0x80000000u, 0xffffffffu}) { + const uint64_t extended = + literal | ((literal & 0x80000000u) != 0 ? 0xffffffff00000000ull : 0ull); + for (const u32 count : {0u, 1u, 31u, 32u, 63u, 64u}) { + AppendVop3(&code, 0x301, 7, InlineU32(count), 255u); + code.push_back(literal); + store_result(7, extended, count); + } + AppendVop3(&code, 0x301, 7, Vgpr(12), 255u); + code.push_back(literal); + store_result(7, extended, 33u); + } + AppendVop3(&code, 0x301, 7, Vgpr(12), 193u); + store_result(7, UINT64_MAX, 33u); + AppendVMovLiteral(&code, 7, static_cast(value)); + AppendVMovLiteral(&code, 8, static_cast(value >> 32u)); + AppendVop3(&code, 0x301, 7, Vgpr(12), Vgpr(7)); + store_result(7, value, 33u); + + AppendEnd(&code); + test.initial.resize(test.expected.size()); + test.opcodes = {O::S_MOV_B32, O::V_MOV_B32, O::BUFFER_LOAD_DWORD, + O::V_ASHRREV_I64, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {"OpShiftRightArithmetic", "OpTypeInt 64 0"}; + return test; +} + TestCase VectorDynamicU64ShiftEdges() { using O = ShaderOpcode; TestCase test; @@ -26944,10 +27005,12 @@ TestCase VectorDynamicU64ShiftEdges() { // A buffer load prevents the constant-shift specialization. AppendVMovU32(&code, 30, index * sizeof(u32)); AppendBufferLoadDword(&code, 3, 30); - for (const bool left : {true, false}) { - AppendVop3(&code, left ? 0x2ff : 0x300, 33, Vgpr(3), Vgpr(4)); + for (const u32 opcode : {0x2ffu, 0x300u, 0x301u}) { + AppendVop3(&code, opcode, 33, Vgpr(3), Vgpr(4)); const auto amount = test.initial[index] & 63u; - const auto expected = left ? source << amount : source >> amount; + const auto expected = opcode == 0x2ffu ? source << amount + : opcode == 0x300u ? source >> amount + : ReferenceAshrI64(source, amount); const auto output = static_cast(test.expected.size()); AppendStoreVgpr(&code, 33, output); AppendStoreVgpr(&code, 34, output + 1u); @@ -26959,9 +27022,10 @@ TestCase VectorDynamicU64ShiftEdges() { AppendEnd(&code); test.initial.resize(test.expected.size()); test.opcodes = {O::V_MOV_B32, O::BUFFER_LOAD_DWORD, O::V_LSHLREV_B64, - O::V_LSHRREV_B64, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + O::V_LSHRREV_B64, O::V_ASHRREV_I64, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; test.required_spirv = {"OpShiftLeftLogical", "OpShiftRightLogical", - "OpTypeInt 64 0"}; + "OpShiftRightArithmetic", "OpTypeInt 64 0"}; return test; } @@ -45911,6 +45975,7 @@ std::vector MakeCases() { cases.push_back(ScalarAshrI64Masks(32)); cases.push_back(ScalarAshrI64Masks(64)); AddCase(VectorDynamicU64ShiftEdges); + AddCase(VectorVop3AshrrevI64Operands); AddCase(ScalarConditionalMoveB64); AddCase(ScalarConditionalMoveB64PreservesMasks); AddCase(ScalarAndn2B64SccUsesMaskShadow); @@ -53094,6 +53159,7 @@ int main(int argc, char **argv) { VulkanHarness vulkan; RunCase(&vulkan, ScalarDynamic64BitOps()); RunCase(&vulkan, VectorDynamicU64ShiftEdges()); + RunCase(&vulkan, VectorVop3AshrrevI64Operands()); RunCase(&vulkan, ScalarAshrI64Edges(false)); RunCase(&vulkan, ScalarAshrI64Edges(true)); RunCase(&vulkan, VectorCompareInteger64Edges()); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 9a31f7f4b..ac7b1428e 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -10550,6 +10550,43 @@ void TestNewShaderRecompilerNativeU64Translation() { "captured VOP3 V_LSHLREV_B64 was not present in the decoded dump"); CheckSpirvBinaryValidates(result.spirv); + const uint32_t ashrrev_i64_shader[] = { + 0xd7010001u, 0x0000d50cu, + EncodeSopp(0x01), + }; + Decoder::Instruction decoded_ashrrev_i64; + ShaderRecompiler::Decoder::DecodeInstruction(ashrrev_i64_shader, 0u, + decoded_ashrrev_i64); + Check(decoded_ashrrev_i64.opcode == Decoder::Opcode::V_ASHRREV_I64 && + decoded_ashrrev_i64.dst.kind == Decoder::OperandKind::Vgpr && + decoded_ashrrev_i64.dst.reg == 1u && + decoded_ashrrev_i64.src_count == 2u && + decoded_ashrrev_i64.src0.kind == Decoder::OperandKind::Vgpr && + decoded_ashrrev_i64.src0.reg == 12u && + decoded_ashrrev_i64.src1.kind == Decoder::OperandKind::VccLo, + "decoder rejected VOP3 V_ASHRREV_I64 fields"); + result = RecompileForTest(ashrrev_i64_shader, options); + Check((result.decoded_dump.find("V_ASHRREV_I64 v1, v12, vcc_lo") != std::string::npos), + "VOP3 V_ASHRREV_I64 was not present in the decoded dump"); + CheckSpirvBinaryValidates(result.spirv); + + const uint32_t ashrrev_captured[] = {0xd7010007u, 0x00020498u, EncodeSopp(0x01)}; + Decoder::Instruction decoded_ashrrev_captured; + ShaderRecompiler::Decoder::DecodeInstruction(ashrrev_captured, 0u, + decoded_ashrrev_captured); + Check(decoded_ashrrev_captured.opcode == Decoder::Opcode::V_ASHRREV_I64 && + decoded_ashrrev_captured.family == Decoder::Family::VOP3 && + decoded_ashrrev_captured.src_count == 2u && + decoded_ashrrev_captured.dst.kind == Decoder::OperandKind::Vgpr && + decoded_ashrrev_captured.dst.reg == 7u && + decoded_ashrrev_captured.src0.kind == Decoder::OperandKind::IntegerInlineConstant && + decoded_ashrrev_captured.src0.value == 24u && + decoded_ashrrev_captured.src1.kind == Decoder::OperandKind::Vgpr && + decoded_ashrrev_captured.src1.reg == 2u, + "captured VOP3 V_ASHRREV_I64 opcode 0x301 was not decoded"); + result = RecompileForTest(ashrrev_captured, options); + CheckSpirvBinaryValidates(result.spirv); + const uint32_t cmpx_i64_shader[] = { 0xd4b5007eu, 0x00020e80u, // v_cmpx_ne_i64 exec, 0, v[7:8] EncodeSopp(0x01),