This commit is contained in:
Marcin Mikołajczyk 2026-04-01 00:30:55 +02:00
parent 969955b8a0
commit 5978035675
5 changed files with 161 additions and 2 deletions

View File

@ -3,6 +3,7 @@
#include <algorithm>
#include "common/assert.h"
#include "core/libraries/kernel/process.h"
#include "shader_recompiler/frontend/decode.h"
#include <magic_enum/magic_enum.hpp>
@ -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<SdwaVopc*>(&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<SdwaVop12*>(&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);

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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::U32>(ir.GetScc());
}
break;
default:
UNREACHABLE();
default: {
if (Libraries::Kernel::sceKernelIsNeoMode()) {
switch (operand.field) {
case OperandField::Inv2Pi:
value = get_imm(static_cast<float>(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) {