Repository navigation
feat(shader): resolve user-data s_swappc_b64 call targets - #2302
santhoshkumar-dev wants to merge 1 commit into
Conversation
|
Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.
|
|
GT7 performance gains observed on our local Windows/NVIDIA build:
These are local before/after observations, not measured gains from #2302, which we have not tested yet. |
|
Tested on Linux with a retail title. Setup: Omarchy (kernel 7.2.5), GCC 16.2.1, RTX 3090, NVIDIA 610.57.04 (subgroup 32), PR head cfa024b on
The throw comes out of s4 and s5 are never written in the program (userCount 16, base s0). Nothing between 0x60 and 0x9c writes s14 or s15. The only difference from the first call I can see is that it is the second call through the same link pair, so the previous writer of s[14:15] in program order, before the copies at 0x60/0x64, is the call at 0x48 itself. If the proof stops at the earlier call when it walks back from 0x9c, that would explain it; the copies sit after that call, so the earlier call should not count as a definition of the target. For scale: dumping every program
I have an independent implementation of the same idea on Not reached on either implementation: a dispatch of one of these programs. On the 3090 the title stops first at #997's AI-assisted: yes (Claude Code); reviewed by me. |
|
Closing: resolving the targets at dispatch puts per-invocation data into the prepared artifact, which goes against how #639 compiles at registration, and the title still stops right after it. #1836 is the route for these shaders: they are deferred and still throw until the calls are actually translated. |
What
Addresses #1817; related reports: #2062 and #2207.
Compute shaders can copy a function pointer out of user-data SGPRs and call it with
s_swappc_b64. Registration has the shader's code and ABI but no invocation user data, so the existing static call analysis rejects these programs before their target is available. Preparing one program per original caller also cannot represent dispatches that select different callees.Registration now defers only compute programs whose unresolved targets have a proven user-data origin. The analysis follows
s_mov_b32/s_mov_b64definitions, rejects branches that can bypass a definition, and stops at control transfers and earlier calls. An untouched user-data pair remains valid across loops. A pair written elsewhere in the caller is accepted only before its first write and without a back edge across the call. Every captured callee must preserve the source pairs used by this proof. Arbitrary GPU-data-dependent targets still throw.At dispatch, the existing
ShaderMemorycapture reader synchronizes and reads each callee from guest memory, bounds its size, and decodes it through its finals_setpc_b64. The caller's instructions and PCs stay intact. Each call site gets its own callee clone and explicit target/return binding, so one link pair can serve several calls, the same callee can be invoked at several sites, and every return reaches that site's continuation. Relative branches inside a callee retain their offsets. This avoids a dynamic return selector and changes no instruction translation semantics. The existingPC + 4link write ands_setpc_b64return follow AMD's RDNA2 ISA, sections 3.6.3 and 12.3, opcodes 32/33. Native ISA/PS5 equivalence has not been measured.The source, prepared-interface and disk keys include all combined caller/callee bytes plus each call's PC mapping, target address and user-data provenance. Source lookup still compares the full code on hash matches; disk entries verify the complete key. Prepared handles and captures reject bindings that no longer match their invocation's function-pointer words. Callee reads participate in dispatch-cache validation and are recorded as code reads, so the flat-resource-data fast path cannot mask a changed instruction. Targets aliasing immutable registration snapshots are rejected. Request serialization version 14 and
agc_shader_replay --dis --cfgpreserve the combined program and call bindings.Remaining unsupported cases throw: non-compute driver integration, arithmetic-derived targets, copy chains that fail the conservative control-flow proof, nested calls/cycles, PC-relative callees, escaping link values, and callees without one final return to the correct link pair. Limits are 64 captured call sites, 64 KiB per callee and 1 MiB combined code. Existing static/direct and fetch-call validation remains in place.
The overlap check was run against current open PRs, with its local query limit also extended to include all open PRs. No other open PR implements this callee-resolution capability. There are mechanical overlaps with #1836/#2257 (registration deferral), #1902/#1928/#2261 (prepared shader lookup), #1491/#1776 (frontend), #1492 (serialization version), #1547 (cache review list), and #2178 (technical debt). This PR contains no prerequisite commits and does not depend on those changes; their overlapping hunks need reconciliation if merged first.
Tested
All new fixtures are synthetic; no retail title or commercial shader file was used. No compatibility claim is made for Ghost of Yōtei, Astro Bot, Bloodlines 2, or any other retail title.
user_data_swappcexercises move-chain provenance, a direct user-data call sharing its link pair, four call sites/two pointers, separate continuations, repeated callee cloning, serialization and source/disk key sensitivity. Negative controls retain the original unbound-graph rejection, corrupt a return mapping, introduce GPU lane data into the target, bypass a defining move, reuse a clobbered pointer in a loop, and exercise invalid/null/unmapped/misaligned targets, malformed/truncated code, recursion/nested calls, unsupported PC-relative code, source-pair clobbers, link escapes, address overflow and size/call-count bounds.agc_driver_user_data_swappcregisters without function-pointer values, resolves at dispatch, and checks observable callee additions and caller additions after every return. It covers wave32/wave64, four shared-link calls, repeated calls inside a loop, two user-data pairs, target changes, and edits to both callees at unchanged addresses. It also checks deterministic guest-memory failures, stale prepared-source rejection, capture of return bytes, registration-snapshot alias rejection, and exclusion of code reads from flat-data masking.Windows x64, WinLibs GCC 15.2.0 r7 (
x86_64-ucrt-posix-seh), Release, AMD Radeon RX 6700 XT. Configuration usedBUILD_TESTING=ONandANYPS5_ENABLE_SPIRV_TOOLS=ON; builds used--parallel 2andCMAKE_BUILD_PARALLEL_LEVEL=2.cmake --build build-gcc15 --parallel 2— complete build passed.ctest --test-dir build-gcc15 --output-on-failure -R 'swappc|prepared|shader_disk_cache|structurizer|control_flow|shader_memory|cache_control'— all 18 passed (5.96 s), including all existing direct/fetch SWAPPC, graph, disk-cache and prepared-shader scenarios.ANYPS5_SHADER_CACHE_DIR, withANYPS5_NO_SHADER_CACHEunset; it wrote 19 artifact files while exercising target/code changes.ctest --test-dir build-gcc15 --output-on-failure --parallel 2— complete 512-test suite: 508 passed, one skipped, three failed (113.73 s). This includes the complete shader execution suite. The skip is the existing no-Vulkan WQHD detection case.d70b89989473ba1f6ae13e44e079e67f1f8a44b0, built with the same GCC 15.2/SPIR-V configuration and GPU:agc_driver_pixel_interlock(wave64 BDA-fault case counts 1 instead of 0),agc_driver_sdwa_float_selectors(lane 2 yields0xbainstead of 0), andagc_driver_sdwa_ldexp_f16(lane 2 yields0xfff0instead of 0). Their assertions and instruction lowering were not changed.windows_dependency_diagnosticspassed on both supported-toolchain checkouts.python tools/check_conventions.py --base origin/mainandgit diff --check origin/main...HEADpassed.tools/pr_overlap.pywas run, including a supplementary all-open-PR scan rather than only its default 200-PR window.Linux/macOS and native PS5 execution were not tested locally.
Checklist
main; no other open PR implements the same capability