From 6cc9e5de11ba6d968f487d56daf7751200f6cd92 Mon Sep 17 00:00:00 2001 From: nmzik Date: Mon, 7 Sep 2026 06:04:40 +0400 Subject: [PATCH] shader: fix wave32 VCC halves and scalar mask moves Co-authored-by: Brandon Strong --- .../recompiler/frontend/translate/Control.cpp | 4 -- .../frontend/translate/Translate.cpp | 9 +++- tests/ShaderRecompilerComputeTests.cpp | 48 +++++++++++++++++++ 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/graphics/shader/recompiler/frontend/translate/Control.cpp b/src/graphics/shader/recompiler/frontend/translate/Control.cpp index 5de469c9d..810167715 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Control.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Control.cpp @@ -240,10 +240,6 @@ void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modi } void Translator::S_MOV_B64(const Decoder::Instruction& inst) { - if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) { - WriteMask(inst.dst, ReadMask(inst.src0), true); - return; - } const bool scalar_copy = inst.dst.kind == Decoder::OperandKind::Sgpr && inst.src0.kind == Decoder::OperandKind::Sgpr; IR::U1 source_mask; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index c9a574952..609d107ef 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -631,7 +631,10 @@ IR::U1 Translator::ReadMask(const Decoder::Operand& operand) { case Decoder::OperandKind::ExecLo: case Decoder::OperandKind::ExecHi: return ir.GetExec(); case Decoder::OperandKind::VccLo: - case Decoder::OperandKind::VccHi: return ir.GetVcc(); + case Decoder::OperandKind::VccHi: + return current_wave_size == 32u + ? ThreadBit({ReadRawU32(operand), IR::U32(IR::Value(0u))}) + : ir.GetVcc(); case Decoder::OperandKind::Scc: return ir.GetScc(); case Decoder::OperandKind::VccZ: return ir.LogicalNot(ir.GetVcc()); case Decoder::OperandKind::ExecZ: return ir.LogicalNot(ir.GetExec()); @@ -697,6 +700,10 @@ std::array Translator::WriteMask(const Decoder::Operand& operand, IR } case Decoder::OperandKind::VccLo: case Decoder::OperandKind::VccHi: { + if (!write_64 && current_wave_size == 32u) { + WriteRawU32(operand, mask[0]); + return mask; + } ir.SetVcc(value); ir.SetVccLo(mask[0]); ir.SetVccHi(mask[1]); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 63a4dd173..c57212fe6 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -18191,6 +18191,52 @@ TestCase VectorVopcSdwaCmpxClassF32CapturedExecMask() { return test; } +TestCase Wave32VccMasksPreserveOtherHalf() { + using O = ShaderOpcode; + TestCase test; + test.name = "Wave32VccMasksPreserveOtherHalf"; + auto &code = test.code; + AppendSMovLiteral(&code, 106, 0x12345678u); + AppendSMovLiteral(&code, 107, 0x2468ace0u); + AppendVMovU32(&code, 1, 20); + u32 offset = 0; + const auto check_masks = [&](u32 low, u32 high) { + AppendStoreSgprAtLaneDwordOffset(&code, 106, 0, offset); + AppendStoreSgprAtLaneDwordOffset(&code, 107, 0, offset + 4); + AppendVop3(&code, 0x101, 2, InlineU32(10), Vgpr(1), 107); + code.push_back(EncodeVop2(0x01, 3, InlineU32(10), 1)); + AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, offset + 8); + AppendStoreVgprAtLaneDwordOffset(&code, 3, 0, offset + 12); + test.expected.insert(test.expected.end(), 4, low); + test.expected.insert(test.expected.end(), 4, high); + for (u32 mask : {high, low}) { + for (u32 lane = 0; lane < 4; ++lane) { + test.expected.push_back((mask & (1u << lane)) != 0 ? 20u : 10u); + } + } + offset += 16; + }; + // PPSA10737 keeps a table offset in VCC_LO while comparing into VCC_HI. + AppendVop3(&code, 0xc2, 107, InlineU32(1), Vgpr(0)); + check_masks(0x12345678u, 2u); + AppendVop3(&code, 0xc2, 106, InlineU32(2), Vgpr(0)); + check_masks(4u, 2u); + code.push_back(EncodeSop2(0x10, 106, 106, 107)); // s_or_b32 + check_masks(6u, 2u); + code.push_back(EncodeSop1(0x04, 106, 193u)); // Explicit s_mov_b64 writes both. + check_masks(0xffffffffu, 0xffffffffu); + AppendEnd(&code); + test.opcodes = {O::S_MOV_B32, O::S_MOV_B64, O::S_OR_B32, O::V_MOV_B32, + O::V_CMP_EQ_U32, O::V_CNDMASK_B32, O::V_LSHLREV_B32, + O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.compute_info.threads_num[0] = 4; + test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1; + test.compute_info.thread_ids_num = 1; + test.compute_info.wave_size = 32; + test.has_compute_info = true; + return test; +} + TestCase VectorCompareF16Ops() { using O = ShaderOpcode; @@ -23655,6 +23701,7 @@ std::vector MakeCases() { AddCase(VectorCompareClassF32); AddCase(VectorVopcSdwaCmpxClassF32CapturedExecMask); AddCase(VectorCompareF16Ops); + AddCase(Wave32VccMasksPreserveOtherHalf); AddCase(Vop2SdwaCndmaskSourceModifier); AddCase(Vop2SdwaCndmaskCapturedByte3Source); AddCase(Vop3CndmaskUsesSgprMaskLaneBits); @@ -28024,6 +28071,7 @@ int main(int argc, char **argv) { #endif if (argc == 2 && std::strcmp(argv[1], "--wave64-only") == 0) { VulkanHarness vulkan; + RunCase(&vulkan, Wave32VccMasksPreserveOtherHalf()); RunCase(&vulkan, DsBpermuteWave64UsesIndependentHalves()); RunCase(&vulkan, Wave64CrossHalfLaneAndLds()); RunCase(&vulkan, Wave64RawMasksAndScalarBranch());