diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index eceeb057..425ef10a 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -104,6 +104,7 @@ constexpr MemoryOpcodeInfo DS_OPCODE_LIST[] = { {0x4fu, Opcode::DS_WRITE2ST64_B64, 4, 32}, {0x76u, Opcode::DS_READ_B64, 2, 32}, {0x77u, Opcode::DS_READ2_B64, 4, 32}, {0x78u, Opcode::DS_READ2ST64_B64, 4, 32}, {0xa6u, Opcode::DS_READ_U16_D16, 1, 16}, + {0xa7u, Opcode::DS_READ_U16_D16_HI, 1, 16}, {0xb0u, Opcode::DS_WRITE_ADDTID_B32, 1, 32}, {0xb1u, Opcode::DS_READ_ADDTID_B32, 1, 32}, {0xdeu, Opcode::DS_WRITE_B96, 3, 32}, {0xdfu, Opcode::DS_WRITE_B128, 4, 32}, {0xfeu, Opcode::DS_READ_B96, 3, 32}, {0xffu, Opcode::DS_READ_B128, 4, 32}, @@ -421,10 +422,11 @@ void DecodeDs(uint32_t pc, std::span code, uint32_t word_index, } DecodeVectorGpr(vdst, inst.dst); - if (inst.opcode == Opcode::DS_READ_U16_D16) { + if (inst.opcode == Opcode::DS_READ_U16_D16 || + inst.opcode == Opcode::DS_READ_U16_D16_HI) { // Native D16 reads update only the selected destination half. Reuse the partial - // destination representation so typed IR translation preserves the upper word. - inst.dst.sdwa_sel = 4u; + // destination representation so typed IR translation preserves the other half. + inst.dst.sdwa_sel = inst.opcode == Opcode::DS_READ_U16_D16 ? 4u : 5u; } DecodeVectorGpr(addr, inst.src0); DecodeVectorGpr(data0, inst.src1); diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 4a843db4..8ca2b7f1 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -497,6 +497,7 @@ enum class Opcode { DS_READ_I16, DS_READ_U16, DS_READ_U16_D16, + DS_READ_U16_D16_HI, DS_READ2_B32, DS_READ2ST64_B32, DS_READ_B32, diff --git a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp index ed9b66fe..c4ed21e1 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Memory.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Memory.cpp @@ -1050,6 +1050,7 @@ bool Translator::EmitMemory(const Decoder::Instruction& inst, std::string* error case Decoder::Opcode::DS_READ_I16: case Decoder::Opcode::DS_READ_U16: case Decoder::Opcode::DS_READ_U16_D16: + case Decoder::Opcode::DS_READ_U16_D16_HI: case Decoder::Opcode::DS_READ_B32: case Decoder::Opcode::DS_READ_B64: case Decoder::Opcode::DS_READ_B96: diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index c0df2b88..4f6ba5bd 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -12018,6 +12018,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode, case Opcode::DS_READ_I16: case Opcode::DS_READ_U16: case Opcode::DS_READ_U16_D16: + case Opcode::DS_READ_U16_D16_HI: case Opcode::DS_READ2_B32: case Opcode::DS_READ_B32: case Opcode::DS_READ_B64: @@ -18038,6 +18039,33 @@ TestCase DsReadU16D16CapturedPreservesHighHalf() { return test; } +TestCase DsReadU16D16HiCapturedPreservesLowHalf() { + using O = ShaderOpcode; + + std::vector code; + AppendVMovU32(&code, 1, 0); + AppendVMovLiteral(&code, 2, 0x89abcdefu); + code.push_back(EncodeDs0(0x0d)); + code.push_back(EncodeDs1(0, 2, 1)); // ds_write_b32 v2, v1 + + AppendVMovLiteral(&code, 5, 0x12345678u); + AppendVMovU32(&code, 6, 0); + code.push_back(0xda9c0000u); + code.push_back(0x05000006u); // captured ds_read_u16_d16_hi v5, v6 + + AppendStoreVgpr(&code, 5, 0); + AppendEnd(&code); + + TestCase test{"DsReadU16D16HiCapturedPreservesLowHalf", + code, + std::vector(1, 0), + {0xcdef5678u}, + {O::V_MOV_B32, O::DS_WRITE_B32, O::DS_READ_U16_D16_HI, + O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; + test.decoded_counts = {{"DS_READ_U16_D16_HI", 1}}; + return test; +} + TestCase DsReadWrite2Variants() { using O = ShaderOpcode; @@ -20556,6 +20584,7 @@ std::vector MakeCases() { AddCase(FlatStoreVariants); AddCase(DsReadWriteVariants); AddCase(DsReadU16D16CapturedPreservesHighHalf); + AddCase(DsReadU16D16HiCapturedPreservesLowHalf); AddCase(DsAppendConsumeUsesEncodedLdsSelector); AddCase(DsAppendUsesEncodedGdsSelector); AddCase(DsGdsSubdwordAndAtomicWrites); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index efe8debb..c0968568 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -4099,6 +4099,15 @@ void TestNewShaderDecoderArchitecture() { Check(boot.opcode == Opcode::DS_SWIZZLE_B32 && boot.offset == 0xc480u, "DS decoder rejected a captured boot-shader instruction"); + const uint32_t d16_hi_ds[] = {0xda9c0000u, 0x05000006u}; + Instruction d16_hi; + Check(DecodeInstruction(d16_hi_ds, 0u, d16_hi, &error), error.c_str()); + Check(d16_hi.opcode == Opcode::DS_READ_U16_D16_HI && + d16_hi.src_count == 1u && d16_hi.data_bits == 16u && + d16_hi.dst.reg == 5u && d16_hi.dst.sdwa_sel == 5u && + d16_hi.dst.sdwa_dst_unused == 2u && d16_hi.src0.reg == 6u, + "DS decoder rejected the captured high-half D16 read"); + constexpr uint32_t packed_source_selectors[][2] = { {0xcc0e0000u, 0x0c0a0300u}, // Source 0: instruction bit 59. {0xcc0e0000u, 0x140a0300u}, // Source 1: instruction bit 60.