diff --git a/src/graphics/shader/recompiler/frontend/translate/Control.cpp b/src/graphics/shader/recompiler/frontend/translate/Control.cpp index 0f4269e3c..2940dd567 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Control.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Control.cpp @@ -256,13 +256,14 @@ void Translator::ScalarSelect64(const Decoder::Instruction& inst, const auto selected_mask_valid = IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMaskValid(inst.src0), ReadMaskValid(false_source)})); - if (IsExecOrVcc(inst.dst)) { - WriteMask(inst.dst, selected_mask, true); - return; - } WriteU32Pair(inst.dst, {ir.Select(condition, lhs[0], rhs[0]), ir.Select(condition, lhs[1], rhs[1])}); - if (inst.dst.kind == Decoder::OperandKind::Sgpr) { + // Scalar selects preserve the raw pair even when EXEC/VCC holds an address. + if (inst.dst.kind == Decoder::OperandKind::ExecLo) { + ir.SetExec(selected_mask); + } else if (inst.dst.kind == Decoder::OperandKind::VccLo) { + ir.SetVcc(selected_mask); + } else if (inst.dst.kind == Decoder::OperandKind::Sgpr) { const auto dst = static_cast(inst.dst.reg); ir.SetThreadBitScalarReg(dst, selected_mask); ir.SetScalarMaskTag(dst, selected_mask_valid); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 2062454a9..af18a7acd 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -20020,10 +20020,13 @@ TestCase ScalarMaskHighWriteInvalidatesProvenance() { O::S_ENDPGM}}; } -TestCase ScalarSelectB64PreservesMaskProvenance() { +TestCase ScalarSelectB64PreservesMaskProvenance(u32 wave_size) { using O = ShaderOpcode; - std::vector code; + TestCase test; + test.name = wave_size == 32 ? "ScalarSelectB64PreservesMaskProvenanceWave32" + : "ScalarSelectB64PreservesMaskProvenanceWave64"; + auto &code = test.code; const auto set_scc = [&] { code.push_back(EncodeSopc(0x06, InlineU32(1), InlineU32(1))); }; @@ -20040,24 +20043,53 @@ TestCase ScalarSelectB64PreservesMaskProvenance() { code.push_back(EncodeSop1(0x08, 6, 4)); capture_scc(8); - code.push_back(EncodeVopc(0xc2, Vgpr(0), 0)); + code.push_back(EncodeVopc(0xc2, InlineU32(0), 0)); // Only lane zero is set. code.push_back(EncodeSop1(0x04, 10, 106)); set_scc(); code.push_back(EncodeSop2(0x0b, 12, 10, 2)); code.push_back(EncodeSop1(0x08, 14, 12)); capture_scc(16); - AppendStoreSgpr(&code, 8, 0); - AppendStoreSgpr(&code, 16, 1); + AppendStoreSgprAtLaneDwordOffset(&code, 8, 0, 0); + AppendStoreSgprAtLaneDwordOffset(&code, 16, 0, wave_size); + test.expected.assign(2u * wave_size, 1u); + code.push_back(EncodeSop1(0x04, 16, 126)); // Save full EXEC. + AppendVMovU32(&code, 2, 9); + constexpr std::array words{ + 0x5aa55aa5u, 0xc39ac39au, 0xa55aa55au, 0x3c653c65u}; + for (u32 i = 0; i < words.size(); ++i) AppendSMovLiteral(&code, i, words[i]); + for (const u32 destination : {106u, 126u}) { + for (const u32 scc : {0u, 1u}) { + code.push_back(EncodeSopc(0x06, InlineU32(scc), InlineU32(1))); + code.push_back(EncodeSop2(0x0b, destination, 0, 2)); + code.push_back(EncodeSop1(0x04, 8, destination)); // Preserve both raw words. + code.push_back(EncodeSop1(0x04, 126, destination)); + AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, + static_cast(test.expected.size())); + const u32 selected = scc ? 0u : 2u; + for (u32 lane = 0; lane < wave_size; ++lane) + test.expected.push_back((words[selected + lane / 32u] >> (lane & 31u)) & 1u + ? 9u : 0xdeadbeefu); + code.push_back(EncodeSop1(0x04, 126, 16)); + for (u32 component = 0; component < 2; ++component) { + AppendStoreSgprAtLaneDwordOffset(&code, 8 + component, 0, + static_cast(test.expected.size())); + test.expected.insert(test.expected.end(), wave_size, words[selected + component]); + } + } + } AppendEnd(&code); - return {"ScalarSelectB64PreservesMaskProvenance", - code, - {}, - {1, 1}, - {O::S_MOV_B32, O::S_CMP_EQ_U32, O::S_CSELECT_B64, O::S_NOT_B64, - O::S_CSELECT_B32, O::V_CMP_EQ_U32, O::S_MOV_B64, O::V_MOV_B32, - O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; + test.initial.assign(test.expected.size(), 0xdeadbeefu); + test.opcodes = {O::S_MOV_B32, O::S_CMP_EQ_U32, O::S_CSELECT_B64, O::S_NOT_B64, + O::S_CSELECT_B32, O::V_CMP_EQ_U32, O::S_MOV_B64, O::V_MOV_B32, + O::V_LSHLREV_B32, O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + 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.compute_info.wave_size = wave_size; + test.has_compute_info = true; + return test; } TestCase ScalarWqmB32Masks(u32 wave_size) { @@ -33077,7 +33109,8 @@ std::vector MakeCases() { AddCase(ScalarConditionalMoveB64PreservesMasks); AddCase(ScalarAndn2B64SccUsesMaskShadow); AddCase(ScalarMaskHighWriteInvalidatesProvenance); - AddCase(ScalarSelectB64PreservesMaskProvenance); + cases.push_back(ScalarSelectB64PreservesMaskProvenance(32)); + cases.push_back(ScalarSelectB64PreservesMaskProvenance(64)); AddCase(ScalarWqmB64SelectsSccDomain); AddCase(ScalarWqmB64PreservesPartialMasks); cases.push_back(ScalarWqmB32Masks(32)); @@ -38629,7 +38662,8 @@ int main(int argc, char **argv) { RunCase(&vulkan, ScalarSubvectorLoops(32)); RunCase(&vulkan, ScalarSubvectorLoops(64)); RunCase(&vulkan, ScalarNotB64UpdatesScc()); - RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance()); + RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance(32)); + RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance(64)); RunCase(&vulkan, ScalarConditionalMoveB64()); RunCase(&vulkan, ScalarConditionalMoveB64PreservesMasks()); RunCase(&vulkan, ScalarWqmB64SelectsSccDomain()); diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 1138895a4..292544167 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -8849,6 +8849,46 @@ void TestNativeScalarReadDescriptorPlanning() { } } +void TestScalarSelectedVccBufferAddress() { + using namespace ShaderRecompiler::IR; + // 68ca7b6fd9c44cfa selects an ordinary 64-bit address through VCC before + // copying its words into a writable buffer descriptor. + const uint32_t shader[] = { + EncodeSopc(0x07, 4, 128), EncodeSop2(0x0b, 106, 0, 2), + EncodeVop1(0x01, 1, 106), EncodeVop1(0x02, 8, 257), + EncodeSMovB32(9, 107), EncodeSMovB32(10, 132), + EncodeSMovB32(11, 255), 3u << 28u, + EncodeMubuf0(0x1c, 0, false), EncodeMubuf1(0, 2, 0), + EncodeSopp(0x01), + }; + std::array userdata{0x12345670u, 0x12u, 0xb27c0000u, 0x30u, 0u}; + for (const uint32_t wave_size : {32u, 64u}) { + auto options = MakeCompileOptions(ShaderType::Compute); + auto input = *options.input_info.compute; + input.threads_num[0] = input.wave_size = options.wave_size = wave_size; + options.input_info.compute = &input; + options.user_data = userdata; + auto translated = ShaderRecompiler::TranslateProgram(shader, options); + auto plan = ExtractResourcePlan(translated.program); + ResourceSnapshot snapshot; + ResourceSpecialization specialization; + for (const uint32_t condition : {0u, 1u}) { + userdata[4] = condition; + Check(MaterializeResources(plan, {.user_data = userdata}, snapshot, specialization), + "scalar-selected VCC address did not materialize"); + const auto offset = condition ? 0u : 2u; + Check(snapshot.buffers.size() == 1 && snapshot.flattened_srt.empty() && + snapshot.specialization_reads.empty() && + snapshot.buffers[0].dwords[0] == userdata[offset] && + snapshot.buffers[0].dwords[1] == userdata[offset + 1], + "VCC selection replaced raw address words with a lane ballot"); + } + const auto compiled = ShaderRecompiler::CompileProgram( + std::move(translated), options, specialization); + CheckSpirvBinaryValidates(compiled.spirv); + } +} + void TestNativeGuardedSamplerSource() { constexpr uint32_t nested = 12, sample = 18, join = 20, end = 22; const uint32_t shader[] = { @@ -14682,6 +14722,7 @@ int main() { TestSharedExitPreservesNativeDescriptorSources(); TestNestedSelectionPreservesDescriptorSources(); TestNativeScalarReadDescriptorPlanning(); + TestScalarSelectedVccBufferAddress(); TestNativeGuardedSamplerSource(); for (const bool scalar_key : {false, true}) TestBoundedMaterialBufferStores(scalar_key); TestNativeDescriptorProvenanceKeepsGpuSelection();