diff --git a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp index a8f1a701a..4b87933d2 100644 --- a/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp +++ b/src/graphics/shader/recompiler/frontend/cfg/ShaderCFG.cpp @@ -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) { switch (operand.kind) { case Decoder::OperandKind::Sgpr: code = operand.reg; return true; @@ -516,24 +512,22 @@ bool ResolveSetpcTarget(const Decoder::Program& program, uint32_t setpc_index, u return false; } - const auto& setpc = program.instructions[setpc_index]; - if (setpc.opcode != Opcode::S_SETPC_B64 || setpc.src0.kind != Decoder::OperandKind::Sgpr) { + const auto& setpc = program.instructions[setpc_index]; + uint32_t pc_code = 0; + if (setpc.opcode != Opcode::S_SETPC_B64 || !ScalarOperandCode(setpc.src0, pc_code)) { return false; } - const auto pc_reg = setpc.src0.reg; if (setpc_index >= 2u) { const auto& arith = program.instructions[setpc_index - 1u]; const auto& getpc = program.instructions[setpc_index - 2u]; - if (getpc.opcode == Opcode::S_GETPC_B64 && getpc.dst.kind == Decoder::OperandKind::Sgpr && - getpc.dst.reg == pc_reg && arith.dst.kind == Decoder::OperandKind::Sgpr && - arith.dst.reg == pc_reg) { + if (getpc.opcode == Opcode::S_GETPC_B64 && IsScalarCode(getpc.dst, pc_code) && + IsScalarCode(arith.dst, pc_code)) { uint32_t imm = 0; 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; if ((adds || subs) && - (IsRegister(arith.src0, Decoder::OperandKind::Sgpr, pc_reg) || - IsRegister(arith.src1, Decoder::OperandKind::Sgpr, pc_reg)) && + (IsScalarCode(arith.src0, pc_code) || IsScalarCode(arith.src1, pc_code)) && (IsImmediate(arith.src0, imm) || IsImmediate(arith.src1, imm))) { const auto base = InstructionEndPc(getpc); 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) { const auto& getpc = program.instructions[setpc_index - 1u]; - if (getpc.opcode == Opcode::S_GETPC_B64 && getpc.dst.kind == Decoder::OperandKind::Sgpr && - getpc.dst.reg == pc_reg) { + if (getpc.opcode == Opcode::S_GETPC_B64 && IsScalarCode(getpc.dst, pc_code)) { target = InstructionEndPc(getpc); 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, diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 1c65aebba..60ccdd7f0 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -11272,6 +11272,29 @@ void TestNewShaderRecompilerSetpcBranch() { 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() { using namespace ShaderRecompiler; uint32_t front[] = { @@ -17365,6 +17388,7 @@ int main() { TestNewShaderRecompilerPixelImageSampleLodSelection(); TestNewShaderRecompilerBranchConditionForms(); TestNewShaderRecompilerSetpcBranch(); + TestNewShaderRecompilerSetpcLongBranch(); TestGraphicsComputeGetreg(); TestMemoryFedScalarLeafCall(); TestFusedShaderHandoffPreservesRegisters();