Repository navigation
fix(shader): link a table call whose descriptor load sits before its loop - #7413
thuanquang wants to merge 2 commits into
Conversation
|
Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.
|
|
Second-GPU run on head
Not checked: PPSA10737, the full Written with Claude Code assistance (validation by a subagent, reviewed in this session). |
|
EA SPORTS FC 25 (PPSA20049) needs this too. On |
…loop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d622a55 to
499e2db
Compare
reachingWriter followed only fall-through and direct branches, so a V# load on a path that reaches the loop through an s_setpc_b64 jump the control flow analysis resolves was not seen, and another load could be taken as the only writer. The walk now follows those jumps. It also fails at s_call_b64 targets, whose callers it does not follow, instead of walking from a function entry into the code above it. Co-Authored-By: Claude <noreply@anthropic.com>
|
Tested on Windows 11 (GCC 15.2.0, Release) on an RTX 5070 Ti (617.42) and an AMD Radeon iGPU, RDNA2 (
AI-assisted: yes (Claude Code); results above were run on this machine. |
klosixgaming-arch
left a comment
There was a problem hiding this comment.
Tested on Windows 11, MinGW-w64 GCC 15.2.0, NVIDIA GeForce RTX 5090 (driver 617.42), Ryzen 9 9950X3D. Head cc6901685 merged onto current main (a373e85); clean merge.
agc_driver_compute_table_callsandscalar_callpass with the change.- On
mainwith only this PR's test files both fail, so the new cases do need the fix. - I read
reachingWriter: the old same-block search is still tried first, so the callsmainlinks today are linked the same way. The new walk goes back over predecessors and gives up (returns false, so the call stays unlinked as before) if it reaches a call ors_swappc_b64, or finds two different writers of the descriptor registers. That is the conservative direction. Reading the diff, I did not find a path where it links a call it should not (I did not fuzz it).
Not tested: Little Nightmares Enhanced Edition (PPSA10737); I have no dump, so the title claim is the author's.
AI-assisted: yes. Written with AI assistance and reviewed by me.
|
Tested on a PC with a real title, Little Nightmares Enhanced Edition (PPSA10737, version 01.004.000), running the PR head (cc69016) as built, relinked and launched on Windows (RTX 5090). The relinked game started and ran for about 2 minutes 46 s before it stopped. The log shows the shaders that call The game then stopped on a different shader: compute shader 0x2010fa0000, stage 0, AI-assisted: yes. Written with AI assistance and reviewed by me. |
|
Follow-up on Little Nightmares Enhanced Edition (PPSA10737, version 01.004.000) on Windows 11 / RTX 5090 at With a local fix in The title then advances to compute shader Inspecting
AI-assisted: yes. Written with AI assistance and reviewed by me. |
|
Intel and AMD Windows data: both tests fail on Windows 11 Pro 26300, WinLibs GCC 15.2.0 r7, Release, every execution test built,
I also ran full CTest with this PR and the 13 other open AGC and shader PRs I tested in the same batch merged together (#7668, #7659, #7504, #7432, #7426, #7480, #7465, #7466, #7464, #7449, #7489, #7673, #7560). Neither GPU showed a failure beyond AI-assisted: yes (Claude Code) |
|
Thanks @klosixgaming-arch for the title runs and for tracking the next two stops. @wachi35676 and @savvaniss, thanks for the runs too. The description now lists all of them. On shader 0x200c340000:
That's a follow-up after this PR and #7395, not something to add here. Do you plan to open the x8/x16 part, since you have it locally? If not, I can open it with a |
What
Little Nightmares Enhanced Edition (PPSA10737) stops before its first frame in 3 of 3 runs on
main(@zaryabzip, discussion #6275):The shader calls a function from a table once per lane, in a waterfall loop. Its
agc_shader_replay --asmlisting in #6275 shows the shape:s_load_dwordx4 s[96:99], s[0:1], 0x10at 0x0c08, well before the loop;s_buffer_load_dwordx4 s[16:19], s[96:99], vcc_lo;s_swappc_b64at 0x1034.FindTableCalls(#4706) links a table call only when thes_load_dwordx4that defines the V# is in the call's own block:findLastWriterstops at the first branch, andsameBlockrejects any label in between. The loop header is always such a label, so the call is never linked, and the shader is compiled with the raw computed call, which throws.The same-block rule is still tried first, so every call
mainlinks is linked the same way. When it fails, the descriptor is now looked for with a reaching-definition walk (reachingWriter). It goes back from the record load over every predecessor of each instruction: the previous instruction unless it iss_branch,s_endpgm,s_code_endors_setpc_b64, every direct branch to it, and everys_setpc_b64whose targetresolveSetpcTargetresolves to it (the long-branch forms the control flow analysis already follows). Each path stops at the first write of any of the four descriptor registers. The search fails, and the call stays unlinked as onmain, if:s_call_b64or a statically resolved call, whose callee is not modelled;s_call_b64target). The callers' paths are not followed, so this fails even when the instruction before the entry falls through into it;The paths may run through any
s_swappc_b64that is not resolved statically, including the table call itself through the loop's back edge. Its callee is assumed to keep the descriptor registers. If it doesn't, nothing wrong runs: the linked compare chain compares the target each iteration actually loads with the address of each linked copy. A miss takes theUnlinkedCallfault (#4706), so the worst case is that fault, never a wrong callee. Every other check is unchanged:s_load_dwordx4from an unchanged user-data pointer or, since feat(agc): link calls through the AGC FS table #6160, from a constant pointer that twos_mov_b32set in the writer's block;s_lshl_b32must be in the call's block.Overlap: #6160, since merged, changed the same line of
FindTableCalls(it added areaderargument to thefindLastWritercall this replaces). This branch is rebased onto it, and the two are independent:The reporter wrote that #6160 does not fix this stop.
Tested
Windows 11 26200, AMD Radeon RX 6700 XT (gfx1031), driver 32.0.21045.5002, WinLibs GCC 15.2.0 r7, Release,
BUILD_TESTING=ON,ANYPS5_REQUIRE_VULKAN=1:scalar_callhas a newCheckHoistedTableCalls, built from the Little Nightmares Enhanced Edition (PPSA10737) #6275 listing, run for wave32 and wave64.s_cbranch_scc0past the call.s_mov_b32of 0xe0040000 and 15 intos[106:107], feat(agc): link calls through the AGC FS table #6160's FS-table form) must link with that constant pointer and descriptor offset 0.s96on one branch arm;s97rewritten after the call inside the loop;s_call_b64to an in-program function on the branch arm;s_getpc_b64/s_sub_u32/s_setpc_b64long branch.s_call_b64with two paths to the record load, one through a second V# load, must stay unlinked;s_call_b64and the call in one block must link as onmain;s_call_b64target) that the code above it falls into, with the V# loaded in that code, must stay unlinked.main'sGraphBuilder.cppthe hoisted shapes find no table call, so the test fails.agc_driver_compute_table_callshas a second caller whose V# is loaded before a conditional branch. On this GPU, records 0 and 1 must call their callees (16 and 33 in every lane) in wave32 and wave64.main'sGraphBuilder.cppthe dispatch stops with the title's message:computed/data-dependent s_swappc_b64 call target at program counter 0x00000038 is not statically resolvable.mainc34e9d855after feat(agc): link calls through the AGC FS table #6160, which lets the same-block search cross a forward branch past the call; the same-block check keeps that, andreachingWriterfollows every predecessor already.scalar_call,agc_driver_compute_table_callsandagc_driver_compute_fs_table_callspass 3 of 3 on the rebased head. WithGraphBuilder.cppatc34e9d855, the first two fail as onmain(computed/data-dependent s_swappc_b64 call target at program counter 0x00000038 is not statically resolvable) and feat(agc): link calls through the AGC FS table #6160's test passes.s_setpc_b64edges, the long-branch shape links the 0x10 table, and the linked program translates (wave32, local probe). Lanes that took the long branch would then reach their targets only through theUnlinkedCallfault. With the edges, the search finds both loads and leaves the call unlinked.FindTableCallsreturns the fall-through program's call.GraphBuilderthen rejects the linked program (unclosable scalar call/return pairing at program counter 0x00000004: control flow enters the call region from program counter 0x0000000c). With the check, the error staysmain'snot statically resolvable. So this check changes no compiled shader; it keeps the driver from reading a table through a V# that the call path never loads.scalar_call,agc_driver_compute_table_callsandagc_driver_compute_fs_table_callspass 3 of 3 on cc69016.ctest --parallel 4 --timeout 120onmainbb2624a53with this change: 1469 tests. The failures all happen without it too:agc_driver_address_passes(broken on Windows GPUs by perf(agc): settle Windows imports under 1 MiB without waiting #6678, fixed onmainby fix(agc): keep a settled import compared while a storage image overlaps it #7476),agc_driver_lds_mixed_width_atomics(fix(shader): release the LDS lock with an exchange and drop fences inside it #6976),agc_driver_lds_lock_budget(AGC: AMD device-backed LDS lock-budget case returns half of expected sum #7344) andagc_driver_layered_volume_vertex_no_gpl(AMD Windows: identical AGC executables invert GPL pass/fail results after relocation #4768).python tools/check_conventions.py --base origin/mainwith this title and body passes.[gpu] driver failurefor a computeds_swappc_b64target. The title then stopped in the same compute shader 0x2010fa0000 on a separate gap,GetImageResource dword 0 is not a valid runtime value(reported on feat(shader): read samplers through nested and pointer-keyed descriptor tables #7395). With a local fix for that, the shader compiles with the call at 0x1034 linked by this PR. The title then stops in shader 0x200c340000, whose table calls take their V# from 4-dword slices of ans_load_dwordx8. That shape is not part of this PR (see below).1002:13c0, 32.0.21042.62), at d622a55 and cc69016.scalar_callandagc_driver_compute_table_callspass on both, and fail with the title's message withGraphBuilder.cppfrom the base. 728 of 728 pass in theagc_|shader|recompiler|scalar_callset on NVIDIA. On AMD onlymain's three known failures fail.mainwith only this PR's test files.main(lane 0 is 3405691582, expected 16).s_swappccalls).Next stops seen in the title, not addressed here:
s_load_dwordx8/x16, whichFindTableCallsdoes not accept;LinkCallsappends each call's dispatch chain and its own callee copies in call order. The copies can't be shared between calls, because a callee region reached from twos_call_b64sites is rejected (is shared with another call). So linking all six calls of that shader needs far calls from the chains, which have to keep SCC for the callee.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)1002:13c0(@wachi35676), RTX 5090 (@klosixgaming-arch), Intel UHD 770 and AMD RX 9070 XT (@savvaniss)🤖 Generated with Claude Code