diff options
| author | Duck912 <350528965+Duck912@users.noreply.github.com> | 2026-11-01 21:41:29 -0500 |
|---|---|---|
| committer | Duck912 <350528965+Duck912@users.noreply.github.com> | 2026-11-01 21:54:48 -0500 |
| commit | a169391e6019d6356cc0492c5a25cf7dbfc22fa3 (patch) | |
| tree | 0dce3444db29d490bef16383491d0caeedc20d18 | |
| parent | ef3ae4ae23b2148fac5b7b335c001b2802a9222b (diff) | |
shader: count only register writes in the callee link check
The check that a native callee keeps its link register read bits 16-23
of every instruction as a destination. RET was exempt, but BRA, NOP and
ISETP/FSETP (which write predicates) also have zero there, so callees
with an R0 link that compare or branch were rejected. IMAD.WIDE writes a
register pair, so its second register now counts as well.
Assisted-by: Claude Opus 5.5 (debugging, implementation and drafting)
Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com>
| -rw-r--r-- | src/shader_recompiler/frontend/ampere/lower_to_maxwell.h | 9 | ||||
| -rw-r--r-- | src/tests/video_core/ampere_returns.cpp | 27 |
2 files changed, 34 insertions, 2 deletions
diff --git a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h index 79f98017ae..489d003e60 100644 --- a/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h +++ b/src/shader_recompiler/frontend/ampere/lower_to_maxwell.h @@ -92,8 +92,13 @@ inline LoweredProgram LowerToMaxwell(std::span<const Instruction> code) { active_link = call_links[call]; } } - // RET has no destination GPR; bits 16..23 are reserved, not a write to R0. - if (active_link && opcode != 0x950 && insn.Bits(16, 8) == *active_link) + // RET, BRA, NOP and ISETP/FSETP (predicate outputs) have no destination GPR; bits 16..23 + // are not a write to R0. IMAD.WIDE writes a register pair. + const bool no_destination = opcode == 0x950 || opcode == 0x947 || opcode == 0x918 || + (opcode & 0x1ff) == 0x00c || (opcode & 0x1ff) == 0x00b; + const bool pair = opcode == 0x225 || opcode == 0x625 || opcode == 0x825; + if (active_link && !no_destination && + (insn.Bits(16, 8) == *active_link || (pair && insn.Bits(16, 8) + 1 == *active_link))) return fail(index, "native callee overwrites link register"); } // Scratch values and emulated uniform state occupy reserved high GPRs. diff --git a/src/tests/video_core/ampere_returns.cpp b/src/tests/video_core/ampere_returns.cpp index 56b8b374da..163758e07e 100644 --- a/src/tests/video_core/ampere_returns.cpp +++ b/src/tests/video_core/ampere_returns.cpp @@ -48,4 +48,31 @@ TEST_CASE("Ampere callees still reject writes to the link", "[video_core][ampere REQUIRE(lowered.error == "native callee overwrites link register"); REQUIRE(lowered.error_offset == 0x30); } + +TEST_CASE("Ampere callees can compare into predicates with an R0 link", "[video_core][ampere]") { + // ISETP's zero destination field is not a write to R0. + const std::array<Instruction, 5> code{{ + {{0x00007802, 0x20, 0x00000f00, 0}}, + {{0x00007944, 0x10, 0x03c00000, 0}}, + {{0x0000794d, 0, 0x03800000, 0}}, + {{0x0100720c, 0x00000002, 0x03f06070, 0}}, + {{0x00007950, 0xffffffb0, 0x03c3ffff, 0}}, + }}; + REQUIRE(static_cast<bool>(LowerToMaxwell(code))); +} + +TEST_CASE("Ampere callees reject a wide multiply over the link", "[video_core][ampere]") { + // IMAD.WIDE.U32 R54:R55 writes the R55 link. + const std::array<Instruction, 5> code{{ + {{0x00377802, 0x20, 0x00000f00, 0}}, + {{0x00007944, 0x10, 0x03c00000, 0}}, + {{0x0000794d, 0, 0x03800000, 0}}, + {{0x01367225, 0x00000002, 0x078e00ff, 0}}, + {{0x37007950, 0xffffffb0, 0x03c3ffff, 0}}, + }}; + const auto lowered = LowerToMaxwell(code); + REQUIRE(!static_cast<bool>(lowered)); + REQUIRE(lowered.error == "native callee overwrites link register"); + REQUIRE(lowered.error_offset == 0x30); +} } // namespace |
