From 9e41d8320a2774b303fbad344c2347e8d0292e83 Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 13 Oct 2026 14:48:38 +0200 Subject: [PATCH] shader: support returning 64-bit LDS max and compare-exchange --- .../backend/spirv/spirvEmitterInstructions.h | 2 + .../backend/spirv/spirvEmitterMemory.cpp | 66 ++++++++------ .../spirv/spirvEmitterMemoryHelpers.cpp | 4 +- .../recompiler/frontend/decode/MemoryOps.cpp | 6 +- .../frontend/decode/ShaderDecoder.cpp | 2 + .../frontend/decode/ShaderDecoder.h | 2 + .../recompiler/frontend/translate/Memory.cpp | 12 ++- .../frontend/translate/Translate.cpp | 5 ++ .../recompiler/ir/opcodes/ValueOpcodes.cpp | 4 + .../recompiler/ir/opcodes/ValueOpcodes.inc | 2 + tests/ShaderRecompilerComputeTests.cpp | 90 ++++++++++++++++++- tests/shaderCfgTests.cpp | 24 +++++ 12 files changed, 188 insertions(+), 31 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h index ad6b3138..985ab8a6 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInstructions.h @@ -322,6 +322,8 @@ inline constexpr auto EmitSharedAtomicDec32 = EmitSharedIncDec; inline constexpr auto EmitSharedAtomicSMin32 = EmitAtomic32; inline constexpr auto EmitSharedAtomicUMin32 = EmitAtomic32; inline constexpr auto EmitSharedAtomicSMax32 = EmitAtomic32; +inline constexpr auto EmitSharedAtomicUMax64 = EmitSharedAtomic64; +inline constexpr auto EmitSharedAtomicCmpSwap64 = EmitSharedAtomic64; inline constexpr auto EmitSharedAtomicUMax32 = EmitAtomic32; inline constexpr auto EmitSharedAtomicAnd32 = EmitAtomic32; inline constexpr auto EmitSharedAtomicOr32 = EmitAtomic32; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp index 3b504312..9577b33e 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp @@ -1240,32 +1240,48 @@ void EmitSharedAtomic64(ValueEmitContext& ctx, const IR::Inst& inst) { auto& state = ctx.state; const auto& mem = ctx.Memory(inst); EnsureLdsStorage(state); - EmitIfCondition(state, ctx.Arg(inst, 2), [&]() { - const auto address = Binary(state, spv::OpBitwiseAnd, TypeU32(state), - ByteAddress(ctx, inst, mem), ConstantU32(state, 0xfff8u)); - const auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), address, - ConstantU32(state, 3u)); - const auto in_bounds = Binary(state, spv::OpULessThan, TypeBool(state), index, - ConstantU32(state, LdsDwordCount(state) / 2u)); - EmitIfCondition(state, in_bounds, [&]() { - auto native_index = index; - auto semantics = spv::MemorySemanticsWorkgroupMemoryMask; - if (state.lds_storage_class == spv::StorageClassStorageBuffer) { - native_index = EmitAddU32(state, index, EmitBinaryU32( - state, spv::OpShiftRightLogical, state.lds_base_dwords, ConstantU32(state, 1))); - semantics = spv::MemorySemanticsUniformMemoryMask; - } - const auto pointer = state.builder.AllocateId(); + const auto address = Binary(state, spv::OpBitwiseAnd, TypeU32(state), + ByteAddress(ctx, inst, mem), ConstantU32(state, 0xfff8u)); + const auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), address, + ConstantU32(state, 3u)); + const auto in_bounds = Binary(state, spv::OpULessThan, TypeBool(state), index, + ConstantU32(state, LdsDwordCount(state) / 2u)); + const auto active = EmitLogicalAndBool(state, ctx.Arg(inst, inst.NumArgs() - 1), in_bounds); + const auto emit = [&]() { + auto native_index = index; + auto semantics = spv::MemorySemanticsWorkgroupMemoryMask; + if (state.lds_storage_class == spv::StorageClassStorageBuffer) { + native_index = EmitAddU32(state, index, EmitBinaryU32( + state, spv::OpShiftRightLogical, state.lds_base_dwords, ConstantU32(state, 1))); + semantics = spv::MemorySemanticsUniformMemoryMask; + } + const auto pointer = state.builder.AllocateId(); + state.builder.AddFunction( + spv::OpAccessChain, TypePointer(state, state.lds_storage_class, TypeU64(state)), + pointer, state.lds_u64_variable, ConstantU32(state, 0), native_index); + const auto old = state.builder.AllocateId(); + const auto opcode = SpirvAtomicOpcode(inst.GetOpcode()); + const auto scope = ConstantU32(state, spv::ScopeWorkgroup); + const auto acquire_release = + ConstantU32(state, spv::MemorySemanticsAcquireReleaseMask | semantics); + if (opcode == spv::OpAtomicCompareExchange) { + // RDNA2 DS compares DATA and stores DATA2, opposite to BUFFER atomics. state.builder.AddFunction( - spv::OpAccessChain, TypePointer(state, state.lds_storage_class, TypeU64(state)), - pointer, state.lds_u64_variable, ConstantU32(state, 0), native_index); - const auto value = ctx.Arg(inst, 1); - state.builder.AddFunction( - SpirvAtomicOpcode(inst.GetOpcode()), TypeU64(state), state.builder.AllocateId(), - pointer, ConstantU32(state, spv::ScopeWorkgroup), - ConstantU32(state, spv::MemorySemanticsAcquireReleaseMask | semantics), value); - }); - }); + opcode, TypeU64(state), old, pointer, scope, acquire_release, + ConstantU32(state, spv::MemorySemanticsAcquireMask | semantics), + ctx.Arg(inst, 2), ctx.Arg(inst, 1)); + } else { + state.builder.AddFunction(opcode, TypeU64(state), old, pointer, scope, + acquire_release, ctx.Arg(inst, 1)); + } + return old; + }; + if (inst.HasUses()) { + ctx.Define(inst, EmitValueOrDefaultIfCondition( + state, active, TypeU64(state), ConstantU64(state, 0), emit)); + } else { + EmitIfCondition(state, active, emit); + } } uint32_t EmitBufferFloatAtomic(ValueEmitContext& ctx, const IR::Inst& inst) { diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp index a073bd24..d7465e87 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp @@ -464,6 +464,7 @@ uint32_t EncodeFormattedStoreComponent(EmitterState& state, const Format::Buffer spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) { switch (opcode) { + case IR::ValueOpcode::SharedAtomicCmpSwap64: case IR::ValueOpcode::ImageAtomicCompareSwap32: case IR::ValueOpcode::BufferAtomicCmpSwap32: return spv::OpAtomicCompareExchange; case IR::ValueOpcode::ImageAtomicSwap32: @@ -491,7 +492,8 @@ spv::Op SpirvAtomicOpcode(IR::ValueOpcode opcode) { case IR::ValueOpcode::ImageAtomicUMax32: case IR::ValueOpcode::ImageAtomicUMax64: case IR::ValueOpcode::BufferAtomicUMax32: - case IR::ValueOpcode::SharedAtomicUMax32: return spv::OpAtomicUMax; + case IR::ValueOpcode::SharedAtomicUMax32: + case IR::ValueOpcode::SharedAtomicUMax64: return spv::OpAtomicUMax; case IR::ValueOpcode::ImageAtomicAnd32: case IR::ValueOpcode::ImageAtomicAnd64: case IR::ValueOpcode::BufferAtomicAnd32: diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 6cf44eab..e38b1c3e 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -116,7 +116,8 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = { {0x3fu, Opcode::DS_ORDERED_COUNT, 1, 32}, {0x40u, Opcode::DS_ADD_U64, 2, 32}, {0x4au, Opcode::DS_OR_B64, 2, 32}, {0x4du, Opcode::DS_WRITE_B64, 2, 32}, {0x4eu, Opcode::DS_WRITE2_B64, 4, 32}, - {0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32}, + {0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x68u, Opcode::DS_MAX_RTN_U64, 2, 32}, + {0x70u, Opcode::DS_CMPST_RTN_B64, 2, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32}, {0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32}, {0xa0u, Opcode::DS_WRITE_B8_D16_HI, 1, 8}, {0xa1u, Opcode::DS_WRITE_B16_D16_HI, 1, 16}, @@ -192,6 +193,8 @@ bool IsDsAtomicOpcode(Opcode opcode) { case Opcode::DS_MIN_RTN_U32: case Opcode::DS_MAX_U32: case Opcode::DS_MAX_RTN_U32: + case Opcode::DS_MAX_RTN_U64: + case Opcode::DS_CMPST_RTN_B64: case Opcode::DS_AND_B32: case Opcode::DS_AND_RTN_B32: case Opcode::DS_OR_B32: @@ -211,6 +214,7 @@ uint32_t DsSourceCount(Opcode opcode) { case Opcode::DS_WRITE2ST64_B32: case Opcode::DS_WRITE2_B64: case Opcode::DS_WRITE2ST64_B64: + case Opcode::DS_CMPST_RTN_B64: case Opcode::DS_MSKOR_B32: return 3u; case Opcode::DS_MIN_F32: case Opcode::DS_MAX_F32: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 331984b2..d3b0f776 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -708,6 +708,8 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::DS_MIN_RTN_U32: case Opcode::DS_MAX_U32: case Opcode::DS_MAX_RTN_U32: + case Opcode::DS_MAX_RTN_U64: + case Opcode::DS_CMPST_RTN_B64: case Opcode::DS_AND_B32: case Opcode::DS_AND_RTN_B32: case Opcode::DS_OR_B32: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 43667d7c..da8ea544 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -624,6 +624,8 @@ enum class Opcode { DS_MIN_RTN_U32, DS_MAX_U32, DS_MAX_RTN_U32, + DS_MAX_RTN_U64, + DS_CMPST_RTN_B64, DS_AND_B32, DS_AND_RTN_B32, DS_OR_B32, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index 32eb3421..f53be76a 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -578,11 +578,13 @@ void Translator::DS_ATOMIC(const Decoder::Instruction& inst, IR::ValueOpcode opc bool returns_value) { const auto memory = MemoryInfoFromDecoded(inst); const auto address = ReadU32(inst.src0); - const IR::Value value = - memory.data_dwords == 2u ? IR::Value(ReadU64(inst.src1)) : IR::Value(ReadU32(inst.src1)); + const auto read = [&](const Decoder::Operand& operand) -> IR::Value { + return memory.data_dwords == 2u ? IR::Value(ReadU64(operand)) : IR::Value(ReadU32(operand)); + }; + const auto value = read(inst.src1); const auto flags = AddMemoryInfo(memory, inst.pc); const auto result = inst.src_count == 3u - ? ir.Emit(opcode, {address, value, ReadU32(inst.src2), ir.GetExec()}, flags) + ? ir.Emit(opcode, {address, value, read(inst.src2), ir.GetExec()}, flags) : ir.Emit(opcode, {address, value, ir.GetExec()}, flags); if (returns_value) { WriteOperand(inst.dst, result); @@ -1073,6 +1075,10 @@ void Translator::EmitMemory(const Decoder::Instruction& inst) { return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, false); case Decoder::Opcode::DS_MAX_RTN_U32: return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax32, true); + case Decoder::Opcode::DS_MAX_RTN_U64: + return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicUMax64, true); + case Decoder::Opcode::DS_CMPST_RTN_B64: + return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicCmpSwap64, true); case Decoder::Opcode::DS_AND_B32: return DS_ATOMIC(inst, IR::ValueOpcode::SharedAtomicAnd32, false); case Decoder::Opcode::DS_AND_RTN_B32: diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index abee5306..d18be59d 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -948,9 +948,14 @@ void IncludeInstructionVectorRegisters(const Decoder::Instruction& inst, uint32_ switch (inst.opcode) { case Decoder::Opcode::DS_ADD_U64: case Decoder::Opcode::DS_OR_B64: + case Decoder::Opcode::DS_MAX_RTN_U64: case Decoder::Opcode::DS_WRITE_B64: case Decoder::Opcode::DS_WRITE_B96: case Decoder::Opcode::DS_WRITE_B128: include_vector(inst.src1, inst.data_dwords); break; + case Decoder::Opcode::DS_CMPST_RTN_B64: + include_vector(inst.src1, 2u); + include_vector(inst.src2, 2u); + break; case Decoder::Opcode::DS_WRITE2_B32: case Decoder::Opcode::DS_WRITE2ST64_B32: case Decoder::Opcode::DS_WRITE2_B64: diff --git a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp index 9cad01fd..a6e7560d 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp +++ b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.cpp @@ -175,6 +175,8 @@ SharedAccess SharedAccessOf(ValueOpcode opcode) { case ValueOpcode::SharedAtomicUMin32: case ValueOpcode::SharedAtomicSMax32: case ValueOpcode::SharedAtomicUMax32: + case ValueOpcode::SharedAtomicUMax64: + case ValueOpcode::SharedAtomicCmpSwap64: case ValueOpcode::SharedAtomicAnd32: case ValueOpcode::SharedAtomicOr32: case ValueOpcode::SharedAtomicOr64: @@ -190,6 +192,8 @@ uint32_t SharedComponentCount(ValueOpcode opcode) { switch (opcode) { case ValueOpcode::SharedAtomicIAdd64: case ValueOpcode::SharedAtomicOr64: + case ValueOpcode::SharedAtomicUMax64: + case ValueOpcode::SharedAtomicCmpSwap64: case ValueOpcode::LoadSharedU32x2: case ValueOpcode::WriteSharedU32x2: return 2u; case ValueOpcode::LoadSharedU32x3: diff --git a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc index 30b86651..69c52a89 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc @@ -303,6 +303,8 @@ VALUE_OPCODE(SharedAtomicSMin32, U32, U32, U32, U1) VALUE_OPCODE(SharedAtomicUMin32, U32, U32, U32, U1) VALUE_OPCODE(SharedAtomicSMax32, U32, U32, U32, U1) VALUE_OPCODE(SharedAtomicUMax32, U32, U32, U32, U1) +VALUE_OPCODE(SharedAtomicUMax64, U64, U32, U64, U1) +VALUE_OPCODE(SharedAtomicCmpSwap64, U64, U32, U64, U64, U1) VALUE_OPCODE(SharedAtomicAnd32, U32, U32, U32, U1) VALUE_OPCODE(SharedAtomicOr32, U32, U32, U32, U1) VALUE_OPCODE(SharedAtomicOr64, Void, U32, U64, U1) diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 4aab6012..ffa8f75d 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -20100,6 +20100,8 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::FLAT_STORE_DWORDX4: case Opcode::DS_ADD_U32: case Opcode::DS_ADD_U64: + case Opcode::DS_MAX_RTN_U64: + case Opcode::DS_CMPST_RTN_B64: case Opcode::DS_ADD_RTN_U32: case Opcode::DS_SUB_U32: case Opcode::DS_SUB_RTN_U32: @@ -33184,6 +33186,90 @@ TestCase DsAtomic64Bounds(bool add) { return test; } +TestCase DsAtomicReturn64() { + using O = ShaderOpcode; + TestCase test; + test.name = "DsAtomicReturn64"; + auto &code = test.code; + const auto pair = [&](u32 reg, uint64_t value) { + AppendVMovLiteral(&code, reg, static_cast(value)); + AppendVMovLiteral(&code, reg + 1u, static_cast(value >> 32u)); + }; + const auto result = [&](u32 reg, uint64_t value) { + AppendStoreVgpr(&code, reg, static_cast(test.expected.size())); + test.expected.push_back(static_cast(value)); + AppendStoreVgpr(&code, reg + 1u, static_cast(test.expected.size())); + test.expected.push_back(static_cast(value >> 32u)); + }; + AppendVMovU32(&code, 7, 0); + AppendVMovLiteral(&code, 6, 0x13579bdfu); + code.push_back(EncodeDs0(0x0d, 24)); + code.push_back(EncodeDs1(0, 6, 7)); + struct Case { uint64_t before, value; u32 address, offset; }; + constexpr std::array cases{ + Case{0x7fffffffffffffffull, 0x8000000000000000ull, 0, 8}, + Case{0x8000000000000000ull, 0x7fffffffffffffffull, 0, 8}, + Case{0x8000000000000001ull, 0x8000000000000002ull, 0x10007, 2}, + Case{0xffffffffffffffffull, 0, 0, 8}}; + for (const auto &item : cases) { + pair(2, item.before); + code.push_back(EncodeDs0(0x4d, 8)); + code.push_back(EncodeDs1(0, 2, 7)); + pair(4, item.value); + AppendVMovLiteral(&code, 0, item.address); + // CS23e96bf5029f62fd pc 0xec8: d9a00008 00000400. VDst aliases ADDR. + code.push_back(EncodeDs0(0x68, item.offset)); + code.push_back(0x00000400u); + result(0, item.before); + code.push_back(EncodeDs0(0x76, 8)); + code.push_back(EncodeDs1(8, 0, 7)); + result(8, std::max(item.before, item.value)); + } + struct CompareCase { uint64_t before, compare, replacement; }; + constexpr std::array compare_cases{ + CompareCase{0xfedcba9876543210ull, 0xfedcba9876543210ull, 0x0123456789abcdefull}, + CompareCase{0xfedcba9876543210ull, 0x0edcba9876543210ull, UINT64_MAX}, + CompareCase{0xfedcba9876543210ull, 0xfedcba9876543211ull, UINT64_MAX}, + CompareCase{UINT64_MAX, UINT64_MAX, 0}}; + AppendVMovU32(&code, 63, 0); + for (const auto &item : compare_cases) { + pair(4, item.before); + code.push_back(EncodeDs0(0x4d, 16)); + code.push_back(EncodeDs1(0, 4, 7)); + pair(0, item.compare); + pair(2, item.replacement); + // pc 0x1048: DATA compares, DATA2 replaces, VDst aliases DATA2. + code.insert(code.end(), {0xd9c00010u, 0x0202003fu}); + result(2, item.before); + code.push_back(EncodeDs0(0x76, 16)); + code.push_back(EncodeDs1(8, 0, 7)); + result(8, item.before == item.compare ? item.replacement : item.before); + } + pair(4, UINT64_MAX); + AppendVMovU32(&code, 0, 24); // One DWORD remains: ignore the entire atomic. + code.push_back(EncodeDs0(0x68)); + code.push_back(EncodeDs1(2, 4, 0)); + result(2, 0); + pair(2, 0xabcdef0112345678ull); + AppendVMovU32(&code, 0, 0); + code.push_back(EncodeSMovB32(126, InlineU32(0))); + code.insert(code.end(), {0xd9a00008u, 0x02000400u}); + code.push_back(EncodeSMovB32(126, InlineU32(1))); + result(2, 0xabcdef0112345678ull); // Inactive lanes retain both destination DWORDs. + code.push_back(EncodeDs0(0x36, 24)); + code.push_back(EncodeDs1(6, 0, 7)); + AppendStoreVgpr(&code, 6, static_cast(test.expected.size())); + test.expected.push_back(0x13579bdfu); + AppendEnd(&code); + test.compute_info.lds_size_dwords = 7; + test.opcodes = {O::V_MOV_B32, O::S_MOV_B32, O::DS_WRITE_B32, O::DS_WRITE_B64, + O::DS_MAX_RTN_U64, O::DS_CMPST_RTN_B64, O::DS_READ_B32, O::DS_READ_B64, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.required_spirv = {"OpAtomicUMax %ulong", "OpAtomicCompareExchange %ulong", "WorkgroupMemoryExplicitLayoutKHR", "Aliased"}; + test.forbidden_spirv = {"OpAtomicLoad", "OpLoopMerge"}; + return test; +} + TestCase DsAtomic64Contention(bool add, u32 wave_size) { using O = ShaderOpcode; TestCase test; @@ -39204,6 +39290,7 @@ std::vector MakeCases() { cases.push_back(DsMaskedOrContention(wave_size)); } AddCase(DsAtomicReturnVariants); + AddCase(DsAtomicReturn64); for (bool add : {false, true}) { cases.push_back(DsAtomic64Bounds(add)); for (u32 wave_size : {32, 64}) { @@ -44656,7 +44743,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, DsWideLdsPartialBounds()); RunCase(&vulkan, DsAtomic64Bounds(false)); RunCase(&vulkan, DsAtomic64Bounds(true)); - for (auto test : {DsWideLdsPartialBounds(), DsAtomic64Bounds(false), DsAtomic64Bounds(true)}) { + for (auto test : {DsWideLdsPartialBounds(), DsAtomic64Bounds(false), DsAtomic64Bounds(true), DsAtomicReturn64()}) { test.compute_info.lds_storage = true; std::erase(test.required_spirv, "WorkgroupMemoryExplicitLayoutKHR"); test.required_spirv.push_back("StorageBuffer"); @@ -44700,6 +44787,7 @@ int main(int argc, char **argv) { RunCase(&vulkan, DsAtomic64Contention(add, wave_size)); } } + RunCase(&vulkan, DsAtomicReturn64()); RunCase(&vulkan, BufferAtomicAndX2GlcAndExec()); RunCase(&vulkan, ImageAtomicSignedMinMax()); RunCase(&vulkan, ImageAtomicSignedMinMax()); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index c9c01d53..2bf30993 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -7913,6 +7913,29 @@ void TestNewShaderRecompilerDsAddtidTranslation() { CheckSpirvBinaryValidates(result.spirv); } +void TestDsReturningAtomic64Decoder() { + using namespace ShaderRecompiler::Decoder; + // CS23e96bf5029f62fd: native DATA is the comparison and DATA2 the replacement. + const uint32_t max_words[] = {0xd9a00008u, 0x00000400u}; + const uint32_t cmp_words[] = {0xd9c00010u, 0x0202003fu}; + Instruction decoded; + DecodeInstruction(max_words, 0u, decoded); + Check(decoded.opcode == Opcode::DS_MAX_RTN_U64 && + decoded.word_count == 2u && decoded.data_dwords == 2u && + decoded.data_bits == 32u && decoded.src_count == 2u && + decoded.src0.reg == 0u && decoded.src1.reg == 4u && + decoded.dst.reg == 0u && decoded.offset == 8u && !decoded.gds, + "captured DS_MAX_RTN_U64 lost its QWORD width, byte offset, or aliases"); + DecodeInstruction(cmp_words, 0u, decoded); + Check(decoded.opcode == Opcode::DS_CMPST_RTN_B64 && + decoded.word_count == 2u && decoded.data_dwords == 2u && + decoded.data_bits == 32u && decoded.src_count == 3u && + decoded.src0.reg == 63u && decoded.src1.reg == 0u && + decoded.src2.reg == 2u && decoded.dst.reg == 2u && + decoded.offset == 16u && !decoded.gds, + "captured DS_CMPST_RTN_B64 lost its comparison/replacement pairs or aliases"); +} + void TestDsFloatMinMaxDecoder() { using namespace ShaderRecompiler::Decoder; for (bool max_value : {false, true}) { @@ -15716,6 +15739,7 @@ int main() { // here. TestScalarAshrI64Decoder(); TestImageAtomicWidthDecoder(); + TestDsReturningAtomic64Decoder(); TestDsFloatMinMaxDecoder(); TestNewShaderDecoderArchitecture(); TestImageAddressOperands();