Repository navigation
feat(shader): link callees of calls whose target is loaded from a table - #4706
Conversation
|
A second-vendor run, to go with the RTX 5090 one. Setup: Windows 11 (26200), WinLibs GCC 15.2.0 r7, Release, CI configure flags, an RTX 4070 Laptop GPU and an Intel Raptor Lake-S iGPU. The head 7c7b22d merged into
Written with AI assistance. |
|
In-game confirmation on Linux: with this PR, Ratchet & Clank: Rift Apart (PPSA01474) no longer aborts with the #3274 error. It plays the PS logo, the raytraced logo and the Insomniac logo, then compiles about 240 new shaders. It stops about 6 minutes in at the table shape you predicted from the offline replay.
DetailsSetup: CachyOS (Linux 7.2), RTX 4090 (NVIDIA 615.78), GCC 16, Release. The title is relinked with Stack: main
Merge notes against
Tests on the combined stack, all passing:
Tested and written by Claude (AI assistant) on a contributor's machine. |
|
Please rebase: |
677e8e7 to
10acd0c
Compare
|
Rebased onto main (23e93c0). Main took request format 24 in #4201 in the meantime, so Generated by Claude Code |
Addressed in 10acd0c: compile-time refusal kept for non-table calls, Vulkan test added, PreparedShaders.cpp back to main, debt line moved.
|
Rebased on main: linked call fallbacks moved to request format 26 (25 is now dithered coverage), and the vertex user-data tail link fills |
10acd0c to
638413c
Compare
An s_swappc_b64 whose target pair comes from an s_buffer_load of a buffer whose descriptor is loaded from user data now calls the targets that buffer holds: at dispatch the driver reads the buffer, links one copy of each distinct callee after the caller and rewrites the call into a compare chain that s_call_b64s the matching copy. A target that matches no copy, and any other computed call, records an UnlinkedCall fault that the driver reports, instead of failing the shader at compile time. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An s_swappc_b64 whose target is not resolved compiles to a faulting call only at the fallback the table-call linker emits; the linker lists those calls in the request (GuestContext::linkedCallFallbacks), and every other computed call is refused at compile time again, as on main. The new agc_driver_compute_table_calls test runs a linked two-record table on Vulkan in wave32 and wave64, with SCC 0 and 1 reaching the callee, and a record past the table reporting the UnlinkedCall fault. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Rebased onto main again: |
638413c to
3c1f439
Compare
|
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.
|
|
1348 Ex Voto (PPSA24249) is a second title with the Silent Hill: Townfall shape. On
From the dumped request: the shader loads its descriptor with Is anyone taking the follow-up? If not, I'd like to write it on top of this PR. |
|
@fma-rc that would be great, please go ahead. Silent Hill: Townfall has the same shape at 0x134, and I can test your head on it. One thing to know first: the descriptor isn't in the title's memory. I looked at this for Silent Hill, and 0xF_E0040000 belongs to Sony's What the shipped
So on hardware the call does happen. In Silent Hill the shader reads nothing back from it: after the call it builds M0 from its own registers and never reads What I measured on Silent Hill with a local stub that skips the call (details in #5176): with #5176 as it is, the title hangs within minutes. With the counter addressed by A direction that would match this: AnyPS5 commits the table at 0xF_E0040000 itself at init, with slot 0 pointing to a buffer whose routine entries are an AnyPS5-written routine that only returns ( AI-assisted: yes |
|
@tv7 thanks, that saved me a lot. I checked it against the PS5 12.00 AnyPS5 maps nothing there yet, so the follow-up will map that block, fill the table at init with routines that only return, and link that callee on top of this PR. It is up as #6160 (branch Edit: an earlier version of this comment said the block was not at 0xF_E0000000; the driver module shows it is. |
What
Fixes #3274: a compute shader's
s_swappc_b64whose target pair is loaded from a table (s_buffer_load_dwordx4of a buffer whose descriptor comes from user data, indexed by a shifted offset) failed at compile time with "computed/data-dependent s_swappc_b64 call target ... is not statically resolvable".FindTableCalls,LinkCalls, driverReadTableCallees): at dispatch the driver reads the descriptor and the buffer, collects the distinct non-zero targets at the loaded offset plus multiples of the offset's stride, reads each callee the same way user-data callees are read, and links one copy per call after the caller. The call becomes a branch to a compare chain (s_cmp_lg_u32with a literal +s_cbranch_scc1per dword) thats_call_b64s the matching copy. There is one chain per value of SCC at the call, and the chain restores SCC before the callee runs, so the callee sees the caller's SCC. Table contents stay out of the shader and pipeline keys; only the linked code (the target set) is keyed, as with user-data callees.UnlinkedCallfault (target and PC) through the existing fault buffer and stops the invocation, or continues after the call when faults don't stop invocations. The driver reports it like a BDA fault. The linker lists these fallbacks in the request (GuestContext::linkedCallFallbacks, request format 25). Any others_swappc_b64whose target is not resolved is still refused at compile time, as on main.PlanBuilder::Collectwalked the IR recursively and kept visited values in a vector. A linked program with 30 callees (about 29k words) overflowed the stack. The walk now uses an explicit stack and hashed sets, and records reads in the same order.Sources: AMD RDNA 2 ISA reference guide. The compare chain uses
s_cmp_lg_u32/s_cmp_eq_u32(§12.4, opcodes 6 and 7, encoding §13.1),s_cbranch_scc1/s_branch(§4.2, §12.5) ands_call_b64(§12.2), and it keeps SCC as §3.8 describes it. The SGPR-mask width used by the link-pair dataflow comes from three places: §2.1 ("The upper half of EXEC and VCC are ignored for wave32 waves"), §3.9 (in wave32 only the low 32 bits of VCC are used) and §6.2.3 (a VALU mask in SGPRs takes two consecutive SGPRs in wave64). From these, a wave32 VALU mask write is counted as one SGPR. That is an inference from the guide, not a measurement. When the wave size is unknown, the dataflow assumes the narrower overwrite and the wider read. Thes_buffer_loadrange is taken as the descriptor's whole size (stride × num_records, or num_records bytes). That size covers both possible range interpretations. Which one the PS5 uses was not measured, and this is recorded in TechnicalDebt. None of the new behaviour was measured on hardware.s_call_b64itself was already measured on gfx1036, as TechnicalDebt records.Data from R&C (PPSA01474): one table with 348,156 16-byte records
{target, arg}and 30 distinct leaf callees, 85–834 instructions each (about 92 KB in total). Each callee returns throughs_setpc_b64on the link pair and makes no further calls.Tested
ctestonmain23e93c0 + this PR, 1146/1146 passed. New and updated cases are inScalarCall.cpp(matcher, linker layout, CFG of the linked program, translation, unlinked fallbacks, wave32/64 mask writes, link-pair dataflow, jump-table refusal, s_call range),ShaderMemory.cpp(request format 25 and the new field's round trip), and the new execution testComputeTableCalls.cpp(agc_driver_compute_table_calls). That test runs a linked two-record table on Vulkan in wave64 and wave32, with SCC 0 and 1 reaching the callee, and checks that a record past the table reports theUnlinkedCallfault.tools/check_conventions.py --base origin/main: passed.src_private_base(shader: reading a scalar aperture source as a value refuses (Ghost of Yōtei PPSA26344, shader 0x8000356b00) #3803, which needs hardware values; not part of this PR).agc_shader_replayon the captured request, plus a local aperture stub that is not in this PR, the call is found and all 30 callees link (29,266 words). The linked CFG is accepted. Planning then fails in a callee that loads an image descriptor through the per-record pointer, which is a data-dependent descriptor (feat(agc): implement image and sampler tables indexed at runtime offsets #757 territory). Front-end and planning took about 28 s on this program, so compile cost is a concern for very large linked sets.storage image access to depth/stencil surface ... is not implemented. Plainmain(d592d54) without this PR does the same, so it is not caused by this change.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code