From 5978035675a02f96994875a2c90f4c0807c909c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Wed, 1 Apr 2026 00:30:55 +0200 Subject: [PATCH] SDWA --- src/shader_recompiler/frontend/decode.cpp | 74 +++++++++++++++++++ src/shader_recompiler/frontend/decode.h | 1 + src/shader_recompiler/frontend/instruction.h | 66 +++++++++++++++++ src/shader_recompiler/frontend/opcodes.h | 3 + .../frontend/translate/translate.cpp | 19 ++++- 5 files changed, 161 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 37e8a0973..d3645521c 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -3,6 +3,7 @@ #include #include "common/assert.h" +#include "core/libraries/kernel/process.h" #include "shader_recompiler/frontend/decode.h" #include @@ -131,6 +132,7 @@ GcnInst GcnDecodeContext::decodeInstruction(GcnCodeSlice& code) { // Note: Literal constant decode must be performed after meta info updated. if (encodingLen == sizeof(u32)) { decodeLiteralConstant(encoding, code); + decodeSubDwordAddressing(encoding, code); } repairOperandType(); @@ -423,6 +425,78 @@ void GcnDecodeContext::decodeLiteralConstant(InstEncoding encoding, GcnCodeSlice } } +void GcnDecodeContext::decodeSubDwordAddressing(InstEncoding encoding, GcnCodeSlice& code) { + // Find if the instruction contains SDWA (it's legal only as src0) + if (m_instruction.src[0].field == OperandField::Sdwa) { + ASSERT_MSG(Libraries::Kernel::sceKernelIsNeoMode(), "SDWA is not supported in Base mode"); + + m_instruction.src[0].code = code.readu32(); + m_instruction.length += sizeof(u32); + + if (encoding == InstEncoding::VOPC) { + SdwaVopc sdwa = *reinterpret_cast(&m_instruction.src[0].code); + m_instruction.src[0].field = + sdwa.s0 == 0 ? OperandField::VectorGPR : OperandField::ScalarGPR; + m_instruction.src[0].code = sdwa.src0; + m_instruction.src[0].sdwa_sel = SdwaSelector(sdwa.src0_sel); + + m_instruction.src[0].input_modifier.neg = sdwa.src0_neg; + m_instruction.src[0].input_modifier.abs = sdwa.src0_abs; + m_instruction.src[0].input_modifier.sext = sdwa.src0_sext; + + m_instruction.src[1].field = + sdwa.s1 == 0 ? OperandField::VectorGPR : OperandField::ScalarGPR; + m_instruction.src[1].sdwa_sel = SdwaSelector(sdwa.src1_sel); + + m_instruction.src[1].input_modifier.neg = sdwa.src1_neg; + m_instruction.src[1].input_modifier.abs = sdwa.src1_abs; + m_instruction.src[1].input_modifier.sext = sdwa.src1_sext; + + m_instruction.dst[0].sdwa_sel = SdwaSelector(sdwa.dst_sel); + m_instruction.dst[0].sdwa_dst = SdwaDstUnused(sdwa.dst_u); + m_instruction.dst[0].output_modifier.clamp = sdwa.clamp; + + switch (sdwa.omod) { + case 0: + m_instruction.dst[0].output_modifier.multiplier = 0.f; + break; + case 1: + m_instruction.dst[0].output_modifier.multiplier = 2.0f; + break; + case 2: + m_instruction.dst[0].output_modifier.multiplier = 4.0f; + break; + case 3: + m_instruction.dst[0].output_modifier.multiplier = 0.5f; + break; + } + } else if (encoding == InstEncoding::VOP1 || encoding == InstEncoding::VOP2) { + SdwaVop12 sdwa = *reinterpret_cast(&m_instruction.src[0].code); + m_instruction.src[0].field = + sdwa.s0 == 0 ? OperandField::VectorGPR : OperandField::ScalarGPR; + m_instruction.src[0].code = sdwa.src0; + m_instruction.src[0].sdwa_sel = SdwaSelector(sdwa.src0_sel); + + m_instruction.src[0].input_modifier.neg = sdwa.src0_neg; + m_instruction.src[0].input_modifier.abs = sdwa.src0_abs; + m_instruction.src[0].input_modifier.sext = sdwa.src0_sext; + + m_instruction.src[1].field = + sdwa.s1 == 0 ? OperandField::VectorGPR : OperandField::ScalarGPR; + m_instruction.src[1].sdwa_sel = SdwaSelector(sdwa.src1_sel); + + m_instruction.src[1].input_modifier.neg = sdwa.src1_neg; + m_instruction.src[1].input_modifier.abs = sdwa.src1_abs; + m_instruction.src[1].input_modifier.sext = sdwa.src1_sext; + + m_instruction.dst[0].field = OperandField::ScalarGPR; + m_instruction.dst[0].code = sdwa.sdst; + } else { + UNREACHABLE_MSG("illegal instruction: SDWA used outside VOP1/VOP2/VOPC"); + } + } +} + void GcnDecodeContext::decodeInstructionSOP1(u32 hexInstruction) { u32 ssrc0 = bit::extract(hexInstruction, 7, 0); u32 op = bit::extract(hexInstruction, 15, 8); diff --git a/src/shader_recompiler/frontend/decode.h b/src/shader_recompiler/frontend/decode.h index 8125ce7fb..5ab3b7080 100644 --- a/src/shader_recompiler/frontend/decode.h +++ b/src/shader_recompiler/frontend/decode.h @@ -70,6 +70,7 @@ private: void decodeInstruction32(InstEncoding encoding, GcnCodeSlice& code); void decodeInstruction64(InstEncoding encoding, GcnCodeSlice& code); void decodeLiteralConstant(InstEncoding encoding, GcnCodeSlice& code); + void decodeSubDwordAddressing(InstEncoding encoding, GcnCodeSlice& code); // 32 bits encodings void decodeInstructionSOP1(uint32_t hexInstruction); diff --git a/src/shader_recompiler/frontend/instruction.h b/src/shader_recompiler/frontend/instruction.h index f4e7bc9f2..064d0341e 100644 --- a/src/shader_recompiler/frontend/instruction.h +++ b/src/shader_recompiler/frontend/instruction.h @@ -26,6 +26,7 @@ enum OperandFieldRange { struct InputModifiers { bool neg = false; bool abs = false; + bool sext = false; }; /// These are applied before storing an operand register. @@ -34,11 +35,31 @@ struct OutputModifiers { float multiplier = 0.f; }; +enum class SdwaSelector : u32 { + Byte0 = 0, + Byte1 = 1, + Byte2 = 2, + Byte3 = 3, + Word0 = 4, + Word1 = 5, + Dword = 6, + Invalid = 7, +}; + +enum class SdwaDstUnused : u32 { + Pad = 0, + Sext = 1, + Preserve = 2, + Invalid = 3, +}; + struct InstOperand { OperandField field = OperandField::Undefined; ScalarType type = ScalarType::Undefined; InputModifiers input_modifier = {}; OutputModifiers output_modifier = {}; + SdwaDstUnused sdwa_dst = SdwaDstUnused::Invalid; + SdwaSelector sdwa_sel = SdwaSelector::Invalid; u32 code = 0xFFFFFFFF; }; @@ -183,6 +204,51 @@ union InstControl { InstControlEXP exp; }; +struct SdwaVopc { + u32 src0 : 8; + u32 dst_sel : 3; + u32 dst_u : 2; + u32 clamp : 1; + u32 omod : 2; + u32 src0_sel : 3; + u32 src0_sext : 1; + u32 src0_neg : 1; + u32 src0_abs : 1; + u32 : 1; + u32 s0 : 1; + + u32 src1_sel : 3; + u32 src1_sext : 1; + u32 src1_neg : 1; + u32 src1_abs : 1; + u32 : 1; + u32 s1 : 1; +}; + +struct SdwaVop12 { + u32 src0 : 8; + u32 sdst : 7; + u32 sd : 1; + u32 src0_sel : 3; + u32 src0_sext : 1; + u32 src0_neg : 1; + u32 src0_abs : 1; + u32 : 1; + u32 s0 : 1; + + u32 src1_sel : 3; + u32 src1_sext : 1; + u32 src1_neg : 1; + u32 src1_abs : 1; + u32 : 1; + u32 s1 : 1; +}; + +union Sdwa { + SdwaVopc vopc; + SdwaVop12 vop12; +}; + struct GcnInst { Opcode opcode; InstEncoding encoding; diff --git a/src/shader_recompiler/frontend/opcodes.h b/src/shader_recompiler/frontend/opcodes.h index 7390a3940..d7d82010d 100644 --- a/src/shader_recompiler/frontend/opcodes.h +++ b/src/shader_recompiler/frontend/opcodes.h @@ -2391,6 +2391,9 @@ enum class OperandField : u32 { ConstFloatNeg_2_0, ConstFloatPos_4_0, ConstFloatNeg_4_0, + Inv2Pi, + Sdwa, + Dpp, VccZ = 251, ExecZ = 252, Scc = 253, diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index de3822296..d4c7be140 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -4,6 +4,7 @@ #include "common/io_file.h" #include "common/path_util.h" #include "core/emulator_settings.h" +#include "core/libraries/kernel/process.h" #include "shader_recompiler/frontend/decode.h" #include "shader_recompiler/frontend/fetch_shader.h" #include "shader_recompiler/frontend/translate/translate.h" @@ -339,8 +340,22 @@ T Translator::GetSrc(const InstOperand& operand) { value = ir.BitCast(ir.GetScc()); } break; - default: - UNREACHABLE(); + default: { + if (Libraries::Kernel::sceKernelIsNeoMode()) { + switch (operand.field) { + case OperandField::Inv2Pi: + value = get_imm(static_cast(1.0f / (2.0f * std::numbers::pi))); + break; + case OperandField::Sdwa: + UNREACHABLE_MSG("unhandled SDWA"); + case OperandField::Dpp: + UNREACHABLE_MSG("unhandled DPP"); + default: + break; + } + } + UNREACHABLE_MSG("unexpected operand: {}", std::to_underlying(operand.field)); + } } if constexpr (is_float) {