The replay rebuilds a captured compute pipeline to time it. With the
library feature off, production now flips the SPIR-V generator word
before vkCreateShaderModule. The replay creates the same module, so it
hits the same AMD driver cache entries as the run it reproduces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AMD's Windows driver compiles pipelines differently when
VK_EXT_graphics_pipeline_library is enabled on the device, but its
on-disk cache (%LOCALAPPDATA%\AMD\VkCache) does not key on it. A process
in one mode is then served pipelines compiled in the other, and their
writes never land (#4768, #5271).
With the library feature off, every module now flips bit 15 of the SPIR-V
generator word, which no driver interprets, so the two modes hash to
separate cache entries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A guest wave's lanes see each other's LDS writes without a barrier, so
the recompiler adds a subgroup-scope OpControlBarrier between an LDS
write and the next LDS access when the guest wave is one host subgroup
(WaveLdsScope). AMD's proprietary Windows driver 32.0.21042.62 (Vulkan
driver 2.0.353, "AMD proprietary shader compiler", Radeon 0x13c0)
compiles that barrier to s_barrier, as the pipeline's internal ISA
shows (26 s_barrier for the guest's 9 in a small LDS reduction). In a
workgroup of several waves, the waves that enter a branch holding LDS
accesses then execute extra s_barriers, which pair with the other
waves' barriers, and those waves read LDS before it is written. LLPC
compiles a subgroup-scope control barrier to a wave barrier instead.
PS5LM Plus (PPSA99581) answered with garbage on that driver: the first
compute dispatch of its first graph, an RMS norm with a 512-thread
wave32 LDS tree reduction, scaled every row except its first 32
elements by a partial sum, while NVIDIA matched a NumPy reference and
the inputs were identical.
On that driver the barrier is now a subgroup-scope OpMemoryBarrier,
which it compiles to no instruction. A 32-lane subgroup is one hardware
wave there, which runs its lanes in lockstep and issues its LDS
instructions in order; Vulkan does not guarantee that, so it is
recorded as debt. Other drivers keep the control barrier. The flag is
part of the shader cache key, and agc_shader_replay takes
--wave-lds-memory-barriers to replay a request the way that driver
compiles it.
agc_driver_lds_tree_reduction sums over eight wave32 waves the way the
guest does, with its last four steps inside one wave and no s_barrier
between them: before, thread 32 read 1936 instead of 32896 on the AMD
driver; it passes on AMD and NVIDIA now. agc_subgroup_barrier checks
the driver quirk and the emitted barriers on every runner. PS5LM Plus
now answers " Paris. It is the largest city in Europe ..." on the AMD
iGPU without flash attention, as on NVIDIA and the CPU.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
lstat and readlink threw NotImplemented for every Windows reparse point
other than a symbolic link or a drive junction. A tag without the name
surrogate bit (cloud files, deduplication) does not stand for another path,
so lstat now answers as stat does and readlink returns EINVAL, as FreeBSD
does for a file that is not a link and as CPython's os.lstat and os.readlink
do (bpo-37834). A name surrogate that is neither a symbolic link nor a
junction (a mounted volume, a WSL link) is reported as the reparse point
itself with the mode of its attributes, and readlink returns EINVAL.
The tests set reparse tags with FSCTL_SET_REPARSE_POINT, one without the
name surrogate bit and one with it. A compact /exe (WOF) file cannot serve
as the fixture: on Windows 11 24H2 the WOF filter hides the reparse point
from GetFileAttributesW and FileAttributeTagInfo alike. Windows refuses
every open of an unhandled tag without FILE_FLAG_OPEN_REPARSE_POINT, so the
test requires lstat to agree with stat rather than to succeed.
mashingaan's boykopovar/AnyPS5#6447 returned EINVAL for other reparse
points without the tag check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.1 <[email protected]>
When the graph is built without the wave size and the return address
escapes under only one of wave32 and wave64, the graph records that size
and its error instead of throwing, and translation throws it only at
that size. A wave64 program that reads a compare mask written over a
call's link pair from a scalar instruction is now recompiled, while the
same program at wave32, where the high dword still holds the return
address, is refused.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The raw-byte check that runs before a syscall pattern between PS4 unwind
functions is skipped now also accepts mov rax, imm64, merges a following
mov al, imm8 or mov ax, imm16 into the earlier constant, and walks back
over any number of register moves or zeroings that leave rax alone
instead of at most seven. Visited states keep the walk linear in the
bytes it covers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
resource-lookup-changed skipped with its own texts when the device has no
host imports or refuses the import of the watched surface. Neither matches
DEVICE_LIMITATION in tools/check_skips.py, so a runner without host imports
would fail the job instead of recording a device skip. Both now print the
existing "skipped, host imports are unavailable".
The control-flow builder does not know the wave size (the front end is
shared across wave sizes), and with an unknown size it took a v_cndmask
mask to read two registers but a VOPC result to write only one. A wave64
compare that overwrote a call's link pair before a v_cndmask read it was
then rejected as a link register escaping the call/return model. The
check now runs once for wave32 and once for wave64 when the size is
unknown, so each read is matched with writes of the same width.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FreeBSD 11.4 vfs_cache_lookup answers ENOTDIR when the directory it searches
is not a directory (sys/kern/vfs_cache.c:2104-2105), the lookup behind the
calls fixed in the earlier ENOTDIR changes. These four calls answered ENOENT
for a path such as file/child on Windows.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A color view of one slice of a 2D array target refreshed the whole array
image before each draw, sampled lookup and present. When the array's unused
slices lie over other live targets (ps5-opengl declares every target as a
two-slice array and allocates the next one over its second slice), each
refresh stored the other targets' pending results, wrote back its own and
uploaded the whole image from guest memory again, on every pass.
StorageTexture::RefreshLayers refreshes the tracked layers that hold the
given array layers alone: it flushes and compares their memory only, and
results of other images pending over the other layers stay pending. A layer
outside the range whose memory changed or that holds another image's pending
results is left behind; GenerationFor reports it as never current, so a use
over it takes the whole Refresh. It falls back to Refresh for volumes,
single-layer and DCC images, an alias, or results of the image pending
outside the range.
A draw into an array slice, a 2D view of one layer of an array image and a
present of one layer use it. The compare memo of untracked memory is kept
per range, so a layer refresh's compare is memoized like the whole image's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>