shader: resolve getpc/add/addc long jumps through any scalar pair

This commit is contained in:
nmzik committed 2026-10-30 10:12:37 +01:00
1 parent 154b175daf
commit 346e9a3c69
2 files changed
+32 -14

No files matched your search

@@ -69,10 +69,6 @@ BranchCondition ConditionForOpcode(Opcode opcode) {
} }
} }
bool IsRegister(const Decoder::Operand& operand, Decoder::OperandKind kind, uint32_t reg) {
return operand.kind == kind && operand.reg == reg;
}
bool ScalarOperandCode(const Decoder::Operand& operand, uint32_t& code) { bool ScalarOperandCode(const Decoder::Operand& operand, uint32_t& code) {
switch (operand.kind) { switch (operand.kind) {
case Decoder::OperandKind::Sgpr: code = operand.reg; return true; case Decoder::OperandKind::Sgpr: code = operand.reg; return true;
@@ -517,23 +513,21 @@ bool ResolveSetpcTarget(const Decoder::Program& program, uint32_t setpc_index, u
} }
const auto& setpc = program.instructions[setpc_index]; const auto& setpc = program.instructions[setpc_index];
if (setpc.opcode != Opcode::S_SETPC_B64 || setpc.src0.kind != Decoder::OperandKind::Sgpr) { uint32_t pc_code = 0;
if (setpc.opcode != Opcode::S_SETPC_B64 || !ScalarOperandCode(setpc.src0, pc_code)) {
return false; return false;
} }
const auto pc_reg = setpc.src0.reg;
if (setpc_index >= 2u) { if (setpc_index >= 2u) {
const auto& arith = program.instructions[setpc_index - 1u]; const auto& arith = program.instructions[setpc_index - 1u];
const auto& getpc = program.instructions[setpc_index - 2u]; const auto& getpc = program.instructions[setpc_index - 2u];
if (getpc.opcode == Opcode::S_GETPC_B64 && getpc.dst.kind == Decoder::OperandKind::Sgpr && if (getpc.opcode == Opcode::S_GETPC_B64 && IsScalarCode(getpc.dst, pc_code) &&
getpc.dst.reg == pc_reg && arith.dst.kind == Decoder::OperandKind::Sgpr && IsScalarCode(arith.dst, pc_code)) {
arith.dst.reg == pc_reg) {
uint32_t imm = 0; uint32_t imm = 0;
bool adds = arith.opcode == Opcode::S_ADD_U32 || arith.opcode == Opcode::S_ADD_I32; bool adds = arith.opcode == Opcode::S_ADD_U32 || arith.opcode == Opcode::S_ADD_I32;
bool subs = arith.opcode == Opcode::S_SUB_U32 || arith.opcode == Opcode::S_SUB_I32; bool subs = arith.opcode == Opcode::S_SUB_U32 || arith.opcode == Opcode::S_SUB_I32;
if ((adds || subs) && if ((adds || subs) &&
(IsRegister(arith.src0, Decoder::OperandKind::Sgpr, pc_reg) || (IsScalarCode(arith.src0, pc_code) || IsScalarCode(arith.src1, pc_code)) &&
IsRegister(arith.src1, Decoder::OperandKind::Sgpr, pc_reg)) &&
(IsImmediate(arith.src0, imm) || IsImmediate(arith.src1, imm))) { (IsImmediate(arith.src0, imm) || IsImmediate(arith.src1, imm))) {
const auto base = InstructionEndPc(getpc); const auto base = InstructionEndPc(getpc);
target = (adds ? base + imm : base - imm) & ~3u; target = (adds ? base + imm : base - imm) & ~3u;
@@ -544,14 +538,14 @@ bool ResolveSetpcTarget(const Decoder::Program& program, uint32_t setpc_index, u
if (setpc_index >= 1u) { if (setpc_index >= 1u) {
const auto& getpc = program.instructions[setpc_index - 1u]; const auto& getpc = program.instructions[setpc_index - 1u];
if (getpc.opcode == Opcode::S_GETPC_B64 && getpc.dst.kind == Decoder::OperandKind::Sgpr && if (getpc.opcode == Opcode::S_GETPC_B64 && IsScalarCode(getpc.dst, pc_code)) {
getpc.dst.reg == pc_reg) {
target = InstructionEndPc(getpc); target = InstructionEndPc(getpc);
return true; return true;
} }
} }
return false; // Long jump: s_getpc_b64; s_add_u32 lo, imm; s_addc_u32 hi, 0.
return ResolvePcRelativeBase(program, setpc_index, pc_code, &target);
} }
bool ResolveSetpcTargets(const Decoder::Program& program, uint32_t setpc_index, bool ResolveSetpcTargets(const Decoder::Program& program, uint32_t setpc_index,
+24
View File
@@ -11272,6 +11272,29 @@ void TestNewShaderRecompilerSetpcBranch() {
CheckSpirvBinaryValidates(result.spirv); CheckSpirvBinaryValidates(result.spirv);
} }
void TestNewShaderRecompilerSetpcLongBranch() {
using namespace ShaderRecompiler;
const uint32_t shader[] = {
EncodeSop1(0x1f, 106, 0), // s_getpc_b64 vcc
EncodeSop2(0x00, 106, 106, 255), 0x14u, // s_add_u32 vcc_lo, vcc_lo, 0x14
EncodeSop2(0x04, 107, 107, 128), // s_addc_u32 vcc_hi, vcc_hi, 0
EncodeSop1(0x21, 125, 106), // s_swappc_b64 null, vcc
EncodeSMovB32(0, 129), // skipped
0xbf810000u, // s_endpgm at 0x18
};
Decoder::Program decoded;
Decoder::DecodeProgram(shader, decoded);
const auto graph = CFG::BuildGraph(decoded);
const auto* jump = graph.FindBlockByPc(0u);
const auto* target = graph.FindBlockByPc(0x18u);
Check(jump != nullptr && target != nullptr &&
jump->terminator.kind == CFG::TerminatorKind::Branch &&
jump->terminator.true_block == target->id,
"S_GETPC/S_ADD/S_ADDC long jump through VCC missed its target");
CheckSpirvBinaryValidates(RecompileForTest(shader, MakeCompileOptions(ShaderType::Compute)).spirv);
}
void TestFusedShaderHandoffPreservesRegisters() { void TestFusedShaderHandoffPreservesRegisters() {
using namespace ShaderRecompiler; using namespace ShaderRecompiler;
uint32_t front[] = { uint32_t front[] = {
@@ -17365,6 +17388,7 @@ int main() {
TestNewShaderRecompilerPixelImageSampleLodSelection(); TestNewShaderRecompilerPixelImageSampleLodSelection();
TestNewShaderRecompilerBranchConditionForms(); TestNewShaderRecompilerBranchConditionForms();
TestNewShaderRecompilerSetpcBranch(); TestNewShaderRecompilerSetpcBranch();
TestNewShaderRecompilerSetpcLongBranch();
TestGraphicsComputeGetreg(); TestGraphicsComputeGetreg();
TestMemoryFedScalarLeafCall(); TestMemoryFedScalarLeafCall();
TestFusedShaderHandoffPreservesRegisters(); TestFusedShaderHandoffPreservesRegisters();