Each SharedRange in agc_host_import_sparse registered a pointer to its
Backing in the global list Resolve searches, and never removed it. The
ranges live on the stack of each check, so by the last check the list
held pointers into stack frames that had been reused. On this machine
Resolve matched one of them for the fourth check's range and returned
an unrelated descriptor (st_size 0, offset 46330441829925), the sparse
path was not chosen, and the check failed with "a failure at
vkQueueBindSparse after the sparse path was chosen did not throw".
Whether it passes depends on what the compiler leaves in those frames.
The range now removes its Backing when it is destroyed. The test passes
20 of 20 here, and fails again when a vkQueueBindSparse failure no
longer throws, which it could not show before.
ShaderCacheVersion.cmake wrote the source version and the APS5_* switch
list into a generated ShaderCacheVersion.hpp that ShaderDiskCache.cpp
included. The version hashes every recompiler source, so any content
change under core/shader/recompiler rewrote that header and recompiled
ShaderDiskCache.cpp next to the file actually edited.
The script now writes ShaderCacheVersion.cpp, which defines
Generated::SourceVersion and Generated::RecompilerSwitches (a span over
the same sorted switch list) with constinit. They are declared in the
new ShaderCacheSourceVersion.hpp in the source tree, which
ShaderDiskCache.cpp includes; the generated directory is no longer an
include directory. The header has a different name from the old
generated one so that a build directory that still holds the stale
ShaderCacheVersion.hpp next to the generated .cpp cannot pick it up.
What is hashed is unchanged: the same manifest (recompiler sources,
HostThreadLocal.hpp, submodule commits, compiler, build type,
architecture and flags), the same checks and the same rewrite-only-
when-different step, so the version still changes exactly when it did.
Running the upstream/main script and this one with the same arguments
over this tree gives the same version (0x7907101dc4d8c62e) and the same
26 switches, and a second ninja run has no work to do. The value
differs from the one main 682ef0c17 generates (0xdce3a0ce03bf030a) only
because this commit edits hashed files, as any recompiler change does.
Tested with ninja -d explain -j 4 shader_recompiler_agc_driver,
CCACHE_DISABLE=1, after appending a line to
IntermediateRepresentation/src/IrType.cpp, three runs on main and then
three runs on this commit back to back, with the machine at 100% CPU
from other builds. On main ShaderCacheVersion.hpp and
ShaderDiskCache.cpp.obj are dirty and the rebuild takes 22.0/23.9/22.2 s.
With this change only ShaderCacheVersion.cpp.obj and IrType.cpp.obj are
compiled, ShaderDiskCache.cpp is absent from the explain output, and the
rebuild takes 12.2/14.7/15.0 s. The shader and recompiler ctest set
(26 tests) passes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Codex <[email protected]>
UploadFinish dissolves the cached address space when an import was
retired after the space was built. The space's base regions are then
merged with the build's own regions, which since readsOverBase may
overlap them. owner() searched the merged list for the region with the
greatest begin, a base region of one mapping, so a read-only V# merged
into an own region that began earlier was bound through it again:
guest buffer view 0x1f0390c1c4+0x10c1cc exceeds its GPU owner
0x1f03900000-0x1f03910000 (buffer 0, direct 1, mirror 0)
The dissolve now notes when the merged list overlaps, and owner() then
looks back for a region that holds the whole range. The test's third
build retires the imports between UploadPrepare and UploadFinish.
Each pixel used its own 16-byte slot across 512 pixels, so the
per-invocation LDS array held 8 KiB for a test that only needs one slot
per lane of a wave. 64 slots and the counter right after them keep the
array at 320 dwords, which software rasterizers compile in a fraction
of the time.
FunctionLdsDwords bounds every LDS access by its address argument and
gives up on opcodes without a dword count. DataAppend and DataConsume
have neither: their first argument is M0, which only places GDS
counters, and the emitter addresses the LDS counter by the instruction
offset alone. Any pixel or vertex-path program with ds_append or
ds_consume therefore declared the full 8192-dword Function array per
invocation. LLVM 20 llvmpipe needs about 6 s to compile that array in
the new pixel LDS read-modify-write test, which ran past the 5 s
graphics fence on CI. The counter now takes the dword at its offset.
build.yml runs git submodule update --init --recursive, but release.yml checked out only the top-level submodules. A submodule with its own submodules, such as Mbed TLS's framework in #3897, would leave the release configure step without them.
A shader with any v_movreld kept its whole relative-addressing window (167
registers in a PPSA08427 compute shader) in one Function-storage array, which
the device puts in local memory. The window array now holds only the slots the
relative accesses likely reach (from a bound analysis of their indices); every
other slot is a variable of its own the device can keep in a register. A
relative access whose index lands past the array at run time switches over
those variables, so the guess only affects speed. APS5_NO_WINDOW_NARROWING=1
turns it off.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
DecodeInstruction returned before reading the ModRM of FF /3 and FF /5,
so a far CALL or JMP through a RIP-relative pointer had no RIP-relative
displacement. A stub that moved such a JMP copied the displacement
without relocating it.
The decoder was compared with iced-x86 (AMD decoding) over 3.99 million
encodings: every opcode of the legacy, 0F, 0F38 and 0F3A maps under 34
prefix combinations and the VEX 0F, 0F38 and 0F3A maps, each with 48
ModRM forms. Apart from this, it agrees on length, RIP-relative offset,
control flow kind and branch target for every valid Zen 2 instruction
except near branches with a 66 prefix, recorded as technical debt, and
kernel-only returns, which it treats as sequential.
Add the comparison's metadata as cases: one RIP-relative encoding per
opcode, map and immediate size, every FF /r form, and the branch forms
with their flow kind and target. The existing cases check lengths only;
a loop or jrcxz decoded as an unconditional jump passed the whole suite.
The pr-template check only accepted "Depends on: #N" or "none" on the same
line, so a checklist answering it as a bulleted list failed CI although
pr_overlap reads exactly that form (test_pr_overlap_dependencies covers
"Depends on:
- #2
- #3"). Allow one list marker before the first answer;
an empty answer is still reported.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012zyXkUzTZGh3nBwNMtrLaW
The area-labels workflow listed open PRs with -L 200, so with more than 200
open PRs the older ones never entered the todo list and never got their area
labels (#3295 changes libkernel and has no libkernel label). List up to 1000;
the head -n 40 cap still bounds labeler work per run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012zyXkUzTZGh3nBwNMtrLaW
With every queue idle, no completions pending and no storage results
pending, queue 0 took the idle branch, found nothing to store
(idleResultsPending() is false since #4410 added the AnyPending() check)
and re-checked at once: the condition kept for "nothing was stored"
continued the loop whenever storeIdleResults was false. Queue 0 never
reached its wait and spun on the driver mutex, a full host core for as
long as the GPU sat idle.
Queue 0 now re-checks only when storage results became pending since the
check or a queue finished work (idle epoch changed); otherwise it waits
for a submission or the next idle epoch as before.
Test: agc_driver_queue_zero_idle_wait submits a NOP on queue 0 and
requires queue 0 to go dormant with nothing pending.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Check read every technical debt file and every changed file with its
own git show, so each run started one process per file: 99 git
processes for a typical pull request, 311 for a 118-file range, and the
count grew with docs/dev/debt. Process start-up dominates on Windows.
Read the blobs the checks use in one git cat-file --batch process,
keyed by the object ids ls-tree already returns, and batch the files
duplicates() inspects the same way. Paths that are not cached still go
through git show, so failures are unchanged.
SaveDataMemoryData was declared as 24 bytes, so libSceSaveData.native read the second and later entries of a guest data array from the reserved bytes of the first one, saw a zero size and skipped them while returning OK. The entry is 64 bytes (buffer, size, offset, 40 reserved), as SaveDataCore.hpp already asserted for its own copy of the struct and shadPS4 declares.
With the shared type the right size, SaveDataCore.hpp drops its private MemoryData and the casts to it and uses SaveDataMemoryData. Its range check takes the unsigned offset; a negative guest offset is still rejected because it exceeds any memory size.
Co-Authored-By: Claude Opus 5.5 <[email protected]>