From 053f5fd3cccf28634d9d350ed489a7a361c54c2e Mon Sep 17 00:00:00 2001 From: Bartlomiej Wolynczyk Date: Tue, 6 Oct 2026 14:22:09 +0000 Subject: [PATCH] shader: emit DPP row_share instead of an identity move --- .../backend/spirv/spirvEmitterHelpers.cpp | 10 ++++ tests/ShaderRecompilerComputeTests.cpp | 54 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp index 29db097a7..bd1f1e7e2 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp @@ -115,6 +115,16 @@ DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& fla if (control == 0x141u) { return EmitDppMirrorTargetLane(state, subid, true); } + // row_share:N reads lane N of the caller's row of 16. + if (control >= 0x150u && control <= 0x15fu) { + const auto row = state.builder.AllocateId(); + const auto target = state.builder.AllocateId(); + state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), row, subid, + ConstantU32(state, 0xfffffff0u)); + state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, row, + ConstantU32(state, control & 0xfu)); + return {target, ConstantBool(state, true)}; + } if (control >= 0x160u && control <= 0x16fu) { const auto target = state.builder.AllocateId(); state.builder.AddFunction(spv::OpBitwiseXor, TypeU32(state), target, subid, diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 0eb570b67..3aa130650 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -24904,6 +24904,56 @@ TestCase VectorDppRowXmask() { return test; } +TestCase VectorDppRowShare(u32 wave_size) { + using O = ShaderOpcode; + constexpr u32 sentinel = 0xaaaaaaaau; + TestCase test; + test.name = wave_size == 32 ? "VectorDppRowShareWave32" : "VectorDppRowShareWave64"; + auto &code = test.code; + AppendVMovU32(&code, 1, 100); + code.push_back(EncodeVop2(0x25, 1, Vgpr(0), 1)); // v1 = 100 + lane. + code.push_back(EncodeSop1(0x04, 10, 126)); + for (u32 selector = 0; selector < 16; ++selector) { + for (const bool masked : {false, true}) { + const u32 row_mask = masked ? 0xau : 0xfu; + const u32 bank_mask = masked ? 0x5u : 0xfu; + // Keep the selected source active in every row while masking destination + // lanes. Row and bank masks affect destinations, not the shared source. + const u32 source_lanes = 0x00010001u << selector; + const u32 exec[] = {masked ? 0xaaaaaaaau | source_lanes : 0xffffffffu, + masked ? 0x55555555u | source_lanes : 0xffffffffu}; + AppendVMovLiteral(&code, 2, sentinel); + AppendVMovLiteral(&code, 3, sentinel); + AppendSMovLiteral(&code, 126, exec[0]); + if (wave_size == 64) AppendSMovLiteral(&code, 127, exec[1]); + code.push_back(EncodeVop1(0x01, 2, 250)); + code.push_back(EncodeVop1Dpp(1, 0x150 + selector, row_mask, bank_mask)); + code.push_back(EncodeVop2(0x25, 3, 250, 0)); + code.push_back(EncodeVop2Dpp(1, 0x150 + selector, row_mask, bank_mask)); + code.push_back(EncodeSop1(0x04, 126, 10)); + for (const u32 reg : {2u, 3u}) { + AppendStoreVgprAtLaneDwordOffset(&code, reg, 0, u32(test.expected.size())); + for (u32 lane = 0; lane < wave_size; ++lane) { + const bool write = ((row_mask >> (lane / 16)) & 1u) && + ((bank_mask >> ((lane / 4) % 4)) & 1u) && + ((exec[lane / 32] >> (lane % 32)) & 1u); + const u32 source = (lane / 16) * 16 + selector; + test.expected.push_back(write ? 100 + source + (reg == 3 ? lane : 0) : sentinel); + } + } + } + } + AppendEnd(&code); + test.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32, O::S_MOV_B32, O::S_MOV_B64, + O::V_LSHLREV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.compute_info.wave_size = wave_size; + test.compute_info.threads_num[0] = wave_size; + test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1; + test.compute_info.thread_ids_num = 1; + test.has_compute_info = true; + return test; +} + TestCase VectorDppBankMaskPreservesDestination() { using O = ShaderOpcode; @@ -37124,6 +37174,8 @@ std::vector MakeCases() { cases.push_back(VectorDpp8Captured(true)); AddCase(VectorDppQuadPermuteReverse); AddCase(VectorDppRowXmask); + cases.push_back(VectorDppRowShare(32)); + cases.push_back(VectorDppRowShare(64)); AddCase(VectorDppBankMaskPreservesDestination); AddCase(VectorDppBoundsControlZeroPreservesDestination); AddCase(Vop3FmacF32NegatedSourceAccumulates); @@ -42803,6 +42855,8 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorDpp8Captured(true)); RunCase(&vulkan, VectorDppQuadPermuteReverse()); RunCase(&vulkan, VectorDppRowXmask()); + RunCase(&vulkan, VectorDppRowShare(32)); + RunCase(&vulkan, VectorDppRowShare(64)); RunCase(&vulkan, VectorDppBankMaskPreservesDestination()); RunCase(&vulkan, VectorDppBoundsControlZeroPreservesDestination()); return 0;