diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index 71beb74ee..9772e92aa 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -38,6 +38,7 @@ constexpr OpcodeMap SOP2_OPCODE_LIST[] = { constexpr OpcodeMap SOP1_OPCODE_LIST[] = { {0x03u, Opcode::S_MOV_B32}, {0x04u, Opcode::S_MOV_B64}, + {0x06u, Opcode::S_CMOV_B64}, {0x07u, Opcode::S_NOT_B32}, {0x08u, Opcode::S_NOT_B64}, {0x0au, Opcode::S_WQM_B64}, diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index 04886c1a2..e7d1a0fab 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -469,6 +469,7 @@ std::string InstructionToString(const Instruction& inst) { switch (inst.opcode) { case Opcode::S_MOV_B32: case Opcode::S_MOV_B64: + case Opcode::S_CMOV_B64: return WithUnsupportedReason(inst, fmt::format("0x{:08x}: {} {}, {}", inst.pc, magic_enum::enum_name(inst.opcode), OperandToString(inst.dst).c_str(), diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 0be58a2b9..ffb7890da 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -38,6 +38,7 @@ enum class Opcode { S_MOV_B32, S_MOV_B64, + S_CMOV_B64, S_MOVK_I32, S_ABS_I32, S_ABSDIFF_I32, diff --git a/src/graphics/shader/recompiler/frontend/translate/Control.cpp b/src/graphics/shader/recompiler/frontend/translate/Control.cpp index 0c3cd163f..5de469c9d 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Control.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Control.cpp @@ -208,15 +208,16 @@ void Translator::S_CSELECT_B32(const Decoder::Instruction& inst) { WriteOperand(DestinationOperand(inst), result); } -void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) { +void Translator::ScalarSelect64(const Decoder::Instruction& inst, + const Decoder::Operand& false_source) { const auto condition = ir.GetScc(); const auto lhs = ReadU32Pair(inst.src0); - const auto rhs = ReadU32Pair(inst.src1); + const auto rhs = ReadU32Pair(false_source); const auto selected_mask = IR::U1( - ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(inst.src1)})); + ir.Emit(IR::ValueOpcode::SelectU1, {condition, ReadMask(inst.src0), ReadMask(false_source)})); const auto selected_mask_valid = IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, - {condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)})); + {condition, ReadMaskValid(inst.src0), ReadMaskValid(false_source)})); if (IsExecOrVcc(inst.dst)) { WriteMask(inst.dst, selected_mask, true); return; diff --git a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp index 66b49ffa1..d119422ba 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp @@ -12,7 +12,8 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) { case O::S_GETPC_B64: S_GETPC_B64(inst); return true; case O::S_SETPC_B64: return true; case O::S_CSELECT_B32: S_CSELECT_B32(inst); return true; - case O::S_CSELECT_B64: S_CSELECT_B64(inst); return true; + case O::S_CSELECT_B64: ScalarSelect64(inst, inst.src1); return true; + case O::S_CMOV_B64: ScalarSelect64(inst, inst.dst); return true; case O::S_SETREG_B32: EmitControlNop(); return true; case O::S_WAITCNT: EmitWaitcnt(); return true; diff --git a/src/graphics/shader/recompiler/frontend/translate/Translator.h b/src/graphics/shader/recompiler/frontend/translate/Translator.h index 9687caca6..bdf0a9265 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translator.h +++ b/src/graphics/shader/recompiler/frontend/translate/Translator.h @@ -233,7 +233,7 @@ private: void S_INST_PREFETCH(); void S_GETPC_B64(const Decoder::Instruction& inst); void S_CSELECT_B32(const Decoder::Instruction& inst); - void S_CSELECT_B64(const Decoder::Instruction& inst); + void ScalarSelect64(const Decoder::Instruction& inst, const Decoder::Operand& false_source); void MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers); void S_MOV_B64(const Decoder::Instruction& inst); void S_WQM_B64(const Decoder::Instruction& inst); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index c728bba01..3a43c1696 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -14143,6 +14143,73 @@ TestCase Scalar64BitOps() { O::V_MOV_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}}; } +TestCase ScalarConditionalMoveB64() { + using O = ShaderOpcode; + TestCase test; + test.name = "ScalarConditionalMoveB64"; + auto &code = test.code; + for (u32 scc : {0u, 1u}) { + AppendSMovLiteral(&code, 0, 0x13579bdfu); + AppendSMovLiteral(&code, 1, 0x2468ace0u); + AppendSMovLiteral(&code, 2, 0xabcdef01u); + AppendSMovLiteral(&code, 3, 0xfedcba98u); + code.push_back(EncodeSopc(0x06, InlineU32(scc), InlineU32(1))); + code.push_back(EncodeSop1(0x06, 0, 2)); + code.push_back(EncodeSop2(0x0a, 4, InlineU32(1), InlineU32(0))); + AppendStoreSgprPair(&code, 0, scc * 3); + AppendStoreSgpr(&code, 4, scc * 3 + 2); + } + AppendEnd(&code); + test.expected = {0x13579bdfu, 0x2468ace0u, 0, + 0xabcdef01u, 0xfedcba98u, 1}; + test.opcodes = {O::S_MOV_B32, O::S_CMP_EQ_U32, O::S_CMOV_B64, + O::S_CSELECT_B32, O::V_MOV_B32, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}; + return test; +} + +TestCase ScalarConditionalMoveB64PreservesMasks() { + using O = ShaderOpcode; + TestCase test; + test.name = "ScalarConditionalMoveB64PreservesMasks"; + auto &code = test.code; + code.push_back(EncodeSop1(0x04, 16, 126)); // Save full EXEC. + AppendVMovU32(&code, 1, 3); + AppendVMovU32(&code, 2, 9); + u32 offset = 0; + for (u32 destination : {126u, 106u, 8u}) { + for (u32 scc : {0u, 1u}) { + code.push_back(EncodeVopc(0xc2, Vgpr(0), 1)); // Only lane 3. + code.push_back(EncodeSop1(0x04, destination, 106)); + code.push_back(EncodeSopc(0x06, InlineU32(scc), InlineU32(1))); + // EXEC encoding is the captured Pathless instruction, 0xbefe06c1. + code.push_back(EncodeSop1(0x06, destination, 193u)); + code.push_back(EncodeSop2(0x0a, 4, InlineU32(1), InlineU32(0))); + code.push_back(EncodeSop1(0x04, 126, destination)); + AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, offset); + code.push_back(EncodeSop1(0x04, 126, 16)); + AppendStoreSgprAtLaneDwordOffset(&code, 4, 0, offset + 64); + for (u32 lane = 0; lane < 64; ++lane) { + test.expected.push_back(scc || lane == 3 ? 9u : 0xdeadbeefu); + } + test.expected.insert(test.expected.end(), 64, scc); + offset += 128; + } + } + AppendEnd(&code); + test.initial.assign(test.expected.size(), 0xdeadbeefu); + test.opcodes = {O::S_MOV_B64, O::V_MOV_B32, O::V_CMP_EQ_U32, + O::S_CMP_EQ_U32, O::S_CMOV_B64, O::S_CSELECT_B32, + O::V_LSHLREV_B32, O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, + O::S_ENDPGM}; + test.compute_info.threads_num[0] = 64; + 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 = 64; + test.has_compute_info = true; + return test; +} + TestCase ScalarAndn2B64SccUsesMaskShadow() { using O = ShaderOpcode; @@ -22512,6 +22579,8 @@ std::vector MakeCases() { AddCase(ScalarBfeI32CapturedRawSignExtends); AddCase(BitfieldExtractWidthPastEndEdges); AddCase(Scalar64BitOps); + AddCase(ScalarConditionalMoveB64); + AddCase(ScalarConditionalMoveB64PreservesMasks); AddCase(ScalarAndn2B64SccUsesMaskShadow); AddCase(ScalarMaskHighWriteInvalidatesProvenance); AddCase(ScalarSelectB64PreservesMaskProvenance); @@ -27041,6 +27110,8 @@ int main(int argc, char **argv) { RunCase(&vulkan, ScalarOrn2SaveexecUsesSourceOrNotExec()); RunCase(&vulkan, ScalarNotB64UpdatesScc()); RunCase(&vulkan, ScalarSelectB64PreservesMaskProvenance()); + RunCase(&vulkan, ScalarConditionalMoveB64()); + RunCase(&vulkan, ScalarConditionalMoveB64PreservesMasks()); RunCase(&vulkan, ScalarWqmB64SelectsSccDomain()); RunCase(&vulkan, ScalarWqmB64PreservesPartialMasks()); RunCase(&vulkan, ScalarMaskProvenanceOverlapAndMixedBinary());