lseek on a non-directory descriptor no longer takes the directory table lock when no directory is open, and a listing is read under the descriptor's own lock. SEEK_CUR with offset 0 only reports the position, and an overflowing SEEK_CUR offset reports EOVERFLOW.
With f16 rounding toward -infinity, v_fma_mixlo_f16 and v_fma_mixhi_f16
gave +0 for an exact cancellation such as 1.0 * 1.0 + -1.0, because the
f64 sum is computed to nearest. RDNA2 gives -0 there, as IEEE 754 does
for an exact zero sum rounded toward -infinity, measured on gfx1036 in
#2631. An exact zero sum now gives -0 in that mode unless both the
product and the addend are +0. A nonzero result that rounds to zero
keeps its sign, so a tiny positive sum still gives +0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The f16 forms of v_fma_mix threw for any FLOAT_MODE with an f16 rounding
other than to nearest even. ps5-opengl's shaders run with FLOAT_MODE
0xcc, f16 rounding toward zero with f16 denormals kept, so PPSA99005
stopped at its first draw (#2631).
The exact product-sum, already computed in f64 with round to odd, is now
rounded once in the mode's direction: scaled by the f16 step at its
exponent, truncated, floored or ceiled, and scaled back. A result beyond
65504 becomes infinity or 65504 as IEEE 754 gives for the direction. On
RDNA2 (gfx1036) -1.5 * 2^-24 + 2^-126 gives 0x8002 with round toward
-infinity, and this gives the same. Flushed f16 denormals still throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review follow-up to the 8_8 SWAP_ALT blending. The second channel of
such a target holds the guest's alpha. Vulkan applies the green blend
constant to it for CONSTANT_COLOR and ONE_MINUS_CONSTANT_COLOR, where the
guest's alpha equation uses the alpha constant. Decoding now throws for
these factors on a SWAP_ALT 8_8 target, unless the equation is MIN or
MAX, which ignore factors.
The second pass of a split draw runs every stage again, not only the
pixel shader, so a vertex or geometry stage that writes memory would
write twice. The memory-write check (now ShadersWriteMemory) covers all
the draw's stages.
A shader preparation transaction took the global writers lock when it
was constructed and kept it to its end. ResolveShaderAbi, reached from
sceAgcCreateInterpolantMapping and sceAgcLinkShaders, compiled its
variants inside such a transaction, so every game thread creating or
linking shaders waited behind one compile: Uncharted's stalled frames
spent about half a second each in shader registration.
The root transaction now takes the lock on its first Read, Edit or
Hold. ResolveShaderAbi prepares the variants before reading the
prepared state when its ABI was not committed yet. Registry
publication holds the lock as before: PublishRegisteredShader holds
it, and RegisterShader holds it before the driver mutex, so the lock
order is unchanged. Commit takes no lock: a transaction with changes
already holds it.
Propagation: Paradise Hotel (PPSA15878) does not load on main: its
executable imports 15 PS VR2 functions that no library exports, the
first reported being sceVrTracker2GetPlayAreaOrientedBoundingBox
(snYs7Nf-RKk). Declare the nine libSceHmd2 reprojection and gaze
exports, three libSceVrTracker2 exports and three scePadVrController
trigger effect exports, without parameters, as throwing stubs. Names
come from the aerolib NID list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60): sceSysmoduleIsLoaded,
sceSysmoduleLoadModule and sceSysmoduleUnloadModule with id 0, and the
Internal and InternalWithArg calls with id 0 or 0x80000000, return
0x805A1000 and leave the WithArg result untouched. They threw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The empty-path utimes check restores the working directory's write time
afterwards and requires the restore to succeed. With MinGW,
std::filesystem::last_write_time fails with EACCES when setting a
directory's time (a file's works), so guest_filesystem aborts there on
Windows. The restore stays, only its result is no longer checked.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
inet_pton and inet_ntop no longer call the host resolver library, whose
accepted forms and output differ from the guest's (leading zeros,
zone ids, IPv4-embedded printing).
(cherry picked from commit 058d808ae7c43042d0ffd5fad76062a2df16b871)
Two catalog titles stop on the absent libSceLibcInternal export reallocf
(YMZO9ChZb0E) on main 0f863c10:
- PPSA97510 PSXS5 2.2.0
- PPSA98510 SwanStationPS5 1.1.3
FAIL: unresolved ELF import YMZO9ChZb0E from libSceLibcInternal.prx
reallocf follows FreeBSD 11 lib/libc/stdlib/reallocf.c (freebsd-src
stable/11 5ed7eb0d97ba4436218e810f61bd059acba984c2) on top of the guest
realloc: it reallocates through the application heap, and when realloc
fails (the application allocator returns null or the default heap
rejects the size) it frees the block through the same allocator, sets
ENOMEM and returns null. Size 0 frees and returns null as realloc does
here, so the block is not freed twice. Other heap failures throw, as
for realloc. The NID matches tools/nid_names.py and aerolib.
The application heap tests cover grow, shrink, a null pointer, size 0
and failure with both the replacement and the default allocator.
Co-Authored-By: screamm <noreply@github.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Killzone: Liberation (PPSA11142) writes its frame fence with
`sceAgcDcbWriteData(dst 6, cache policy 3, write confirm)`, which the
SPRX-confirmed encoding turns into WRITE_DATA with DST_SEL 3 and a label
address in the title's memory that RELEASE_MEM and WAIT_REG_MEM then
use. The validator rejected DST_SEL 3 as the GDS; it now writes guest
memory like DST_SEL 5, resolves as a label, and the title runs past its
first frame. TechnicalDebt records where the semantics come from.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 9c53b7f6e8ce2ca81d8f0fa551d12632acfeb60d)
Titles that call Open/Speak after a failed initialize should get the same unavailable-engine error instead of a throw.
Co-authored-by: Cursor <cursoragent@cursor.com>
Deathloop (PPSA01670), Minecraft Legends (PPSA05510) and GTA III
Definitive Edition import both from libSceAmpr. They take a pointer to
the handle slot and act on the conditions created by the libc _Cnd_init:
_Cnd_signal wakes one waiter through a libc helper, _Cnd_init_with_name
is _Cnd_init with the name ignored. _Thrd_id still throws.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Document the level-triggered user event semantics of sceKernelWaitEqueue in TechnicalDebt.
(cherry picked from commit db79bee1e597effff34c72493ed893d4e750b95d)
A user event added with sceKernelAddUserEvent stays triggered until it is
deleted. The collection loop kept re-emitting it until the caller's event
buffer was full, so a wait with num > 1 returned the same event num times.
Like kevent, each event is now reported at most once per call; distinct
queued completions of one event are still delivered in order.
(cherry picked from commit db28ebbb7f078cb593cf6b7bb34b8db49dd94630)
The half-wave scan rules for 32-lane hosts made lane 31 of a matched
scan a subgroup reduction and threw on a device without subgroup
arithmetic, where the read used to build as a shuffle of the scan. Such
a device now keeps the shuffle for lane 31; lane 63 still reads the
identity, which needs no arithmetic.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wave64 vertex program with the culling scan of #2783 (lane < s3's
primitive count, row_shr bound_ctrl:1 OR scan, v_permlanex16_b32,
v_readlane_b32 of lanes 31 and 63) compares the wave OR with the ballot
of its keys and collapses its triangle when they differ. On a 32-lane
device every pixel of the grid must be covered.
Before the s3 change the program does not build on 32 lanes; with a
shuffle from lane 31 instead of the subgroup reduction no pixel is
covered, as the device leaves lanes of each vertex warp idle. Other
subgroup widths skip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wave64 vertex program runs at one guest lane per invocation, so on a
32-lane host subgroup its lanes 32-63 have no invocation. The vertex
entry still set s3 = 64 | 64 << 8, telling the program that its wave
holds 64 vertices and 64 primitives. An NGG culling program then keys a
full-wave OR on lane < s3's primitive count, lanes 32-63 hold real cull
results, and its v_readlane_b32 of lane 63 cannot be built (#2783).
The entry now sets s3 = 32 | 32 << 8 for a wave64 program on a 32-lane
host, the wave the host runs; the console launches waves with fewer
vertices and primitives. The host subgroup width reaches the vertex
input the way it reaches the compute and mesh inputs
(BuildShaderStageInputInfo from HostSubgroupSize), and the source,
prepared-shader and disk cache keys already carry it for every stage.
Programs that do not read s3 are unchanged; wave32 programs and other
host widths keep the full wave.
The keys are then 0 in lanes 32-63, so the read is sound. On a 32-lane
host, when the existing half-wave scan rules fail, the scan may also:
- run under s_mov_b64 exec, -1 (EXEC constant true);
- use bound_ctrl:1 row_shr steps when 0, the fill, is the identity (OR,
add, unsigned max), also into another register, whose old value never
shows since such a step writes every lane;
- take keys shown to be 0 in lanes 32-63: built from 0 through selects
under a lane id or v_mbcnt of -1 below at most 32, v_cmp_ne_u32 against
0, ORs, DPP and permlanes, in stages whose entry EXEC is every lane.
Lane 63 is then the identity and lane 31 the subgroup reduction of the
host's invocations. A shuffle from lane 31 is not enough: an RTX 5070
runs the vertex warps of a non-indexed triangle list with 30 of their 32
invocations. A v_readlane_b32 outside the host subgroup of another value
that is 0 in lanes 32-63 reads 0. Only reads that used to throw change,
besides lane 31 of such scans.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XPSemu (PPSA97358) stops next at sigsetjmp (aRo9AhFUXcM) from libkernel,
per the body of #3323 "feat(libkernel): implement cpuset_setaffinity for
the calling thread or process": "XPSemu: with this PR, the next stop is
`sigsetjmp` (`aRo9AhFUXcM`)". RetroArch and PS5MC list it under libkernel
in the absent-import tables of #1578; aerolib names aRo9AhFUXcM sigsetjmp.
sigsetjmp is FreeBSD's amd64 sigsetjmp (lib/libc/amd64/gen/sigsetjmp.S,
release/11.4.0 8c639945), the counterpart of the merged siglongjmp (#2935)
and its sigjmp_buf layout (sys/x86/include/setjmp.h, _JBLEN 12): it stores
savemask at offset 88 and, when it is nonzero, saves the current mask at 72
through _sigprocmask(SIG_BLOCK, NULL, oset); then it records the return
address, rbx, rsp, rbp, r12-r15 and the x87 control word, and returns 0.
guest_signal_sets now jumps through the export instead of its copy of
FreeBSD's sigsetjmp, and checks that savemask 0 leaves the mask slot
untouched, a nonzero savemask records the current mask and its own value,
and the callee-saved registers come back after siglongjmp.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 8a609a5a1f189dce107a871e7da0fd55c746921f)
The macOS image initializer called a guest module's DT_INIT with no
arguments, so a module started with sceKernelLoadStartModule never saw
its args and argp, and its result was dropped. The Linux writer already
calls DT_INIT through libkernel's pending argument handoff. On the
macOS port, the PSN plugin of PPSA12544 refuses to start without its
argument block ("PSN is an old version that cannot be used by the
current player runtime") and the title then crashes.
A guest module with DT_INIT now gets two slots bound to
__aps5_get_pending_module_args_nid_no_patch and
__aps5_set_module_init_result_nid_no_patch, and its initializer calls
DT_INIT with the args and argp that sceKernelLoadStartModule records,
as on Linux, then hands the result back. The TechnicalDebt sentence
about the missing arguments on macOS is removed.
guest_module_args also relinks its fixture with --macos and checks the
two binds; on x86-64 macOS it loads the module and checks that DT_INIT
receives the arguments and that its result is returned.
AI-assisted: yes
Stremio Plus (PPSA74126) stops at start-up on _ioctl, which its statically
linked tc* functions call. FreeBSD's _ioctl is the same system call as
ioctl, so it forwards to ioctl.
Implement arc4random (UISQ-dQzw28) in libScePosixForWebKit on top of the
existing arc4random_buf, so both draw from the same thread-local host
std::random_device. Like FreeBSD 11.4 lib/libc/gen/arc4random.c
(8c639945) it returns a uniformly distributed 32-bit value and has no
failure result.
Cloud Play (PPSA99600) imports it: the import audit in #3249 on main
ccf9ff90 lists `UISQ-dQzw28` `arc4random` among nine absent imports
("no PR or issue found"). The ps5-payload-dev SDK stubs (f5d40f02,
sce_stubs/libScePosixForWebKit.c) place arc4random, arc4random_buf and
fnmatch in libScePosixForWebKit; arc4random_uniform is in neither that
set nor libSceLibcInternal, so it is not added.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit 95da68017f6817abec785864e682530e9b135a2b)
Register copied the protection it had recorded for pages it already
watched over the protection the kernel had just reported. A guest that
reserves address space, which registers it as PROT_NONE, then maps
direct memory over it read-write and registers it again kept the stale
PROT_NONE. Collect only write-protects pages with PROT_WRITE, so writes
to that memory were reported once and never again. With PPSA12544 on
the macOS port, the title menu text blinked and glyphs went missing.
Register now keeps a recorded protection only where its own write
protection explains what the kernel reports, and takes the kernel's
protection otherwise. A protected page that was mapped again also loses
its protected mark, so erase no longer restores the old protection over
the new mapping. The kernel is read under the lock, so a concurrent
Collect cannot protect a page between the read and the merge.
Test write_watch_mapped: a reservation, a mapping over it and a second
registration keep reporting writes, and a protected page reserved again
stays PROT_NONE.
AI-assisted: yes
There is no system trophy UI, so the call returns success without showing a
list, like the other silent NpTrophy2 entry points; recorded in
docs/dev/TechnicalDebt.md. PPSA15552 (Dead Cells) calls it and
aborted on the NotImplemented exception.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Dqgm9nUKygsXzr6poJsHJ
The pixel program packs its color for the export format, and the test now draws with FP16, UNORM16 and SNORM16 exports: an alpha of 0.75 keeps the pixel, 0.25 drops it, and so does -0.5 with SNORM16. Passes on an RTX 3050, a Radeon 660M and lavapipe.
Formats 4, 5, 6 and 9 (FP16, UNORM16, SNORM16 and 32-bit float ABGR) decode with alpha-to-coverage; formats 1, 2 and 3, which carry no floating-point alpha, still throw.
A dispatch whose group count exceeds maxComputeWorkGroupCount aborted the
process; a software device (lavapipe) reports the spec minimum 65535, and
Ghost of Yotei (PPSA26344) dispatches 129199x1x1 in its presentation path.
The count is now recorded as consecutive vkCmdDispatchBase chunks of at
most the limit per dimension, each continuing the grid at its base offset
(a plain vkCmdDispatch would restart group ids at 0 and re-execute the
first chunk's workgroups), with a barrier between chunks so a later chunk
sees an earlier one's writes. Compute pipelines are created with
VK_PIPELINE_CREATE_DISPATCH_BASE_BIT, which a nonzero base requires.
A split on NVIDIA throws instead of dispatching: measured on an RTX 5070
Ti, the driver drops the first workgroups of a vkCmdDispatchBase dispatch
with a nonzero base (reproduced with a standalone Vulkan program outside
this repository), so its 65535 Y/Z limits cannot be split through. A split
of a dispatch whose LDS lives in device memory also throws: its buffer
slot base is computed from gl_NumWorkGroups, which inside a chunk is the
chunk's count, so workgroups of different chunks would alias (a 1D X split
only works because the base degenerates to the group id).
Fixes#3212
The Linux desktop entry was written for every non-Windows output, macOS
included since --macos landed. It is now written only for Linux output,
and its paths go through Io::NativePath/Utf8Path like the rest of the
relinker. An end-to-end test relinks for Linux, macOS and Windows.
Co-Authored-By: Claude <noreply@anthropic.com>
GuestMemory.cpp carried its own procmap_query fallback and a ProcMapsQueryFd that opened /proc/self/maps once, probed PROCMAP_QUERY and honoured APS5_NO_PROCMAP_QUERY, the same as HostMapping::QueryFd. It now includes HostMapping.hpp and calls HostMapping::QueryFd.
MarkDirty stores uncompressed keys at an image's first write and marks
its uploaded keys uncompressed. The write-back stored them again,
whenever the key bytes did not read uncompressed. By then the title may
have reused the metadata memory: RIDE 4 (PPSA01599) frees its 1920x1088
render targets after the legal screen and Unreal Engine's small-block
allocator takes the 64 KiB key block that followed one of them. The
deferred write-back then filled the allocator's free lists with 0xff
and the title crashed popping one.
The write-back now stores keys only when the image's uploaded keys are
not uncompressed already, that is when no store at the first write or
an earlier write-back made them so since the keys were last read.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PPSA08427 (Killing Floor 3) stops on main at start-up because its bundled
pros.sdk.Prospero.prx imports it:
pros.sdk.Prospero.prx.guest.prx: undefined symbol: y7JD1gfwyfA
The signature is unknown, so it is declared without parameters and
throws, like the other entitlement flag queries the title does not reach.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uzy15wqYTt6iWyxYXEzUWo
note_bda_write loaded the chunk's page mask and set the page's bit after the probe loop, through a function variable holding the slot. NVIDIA 610.57.04 (Linux) never finishes a dispatch that takes that path: agc_driver_bda_device and agc_driver_flat_apertures_0, _2, _3, _4, _5 lose the device. The same load and conditional OpAtomicOr now run in the loop body, on the iteration that finds or claims the slot.
Dungeons (PPSA16064) calls select on descriptor 0 and stopped with
"select on descriptor 0 (not a libSceNet socket) not implemented".
Descriptor 0 is now never readable and descriptors 1 and 2 are always
writable, as for a console title with no input. The assumption is in
TechnicalDebt next to the termios entry for the same descriptors.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
The abort only said "select on descriptors that are not libSceNet
sockets", which leaves no way to tell from a title's log what it waited
on. It now names the descriptor and says whether libSceNet was loaded.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
select passed every descriptor it was given to the libSceNet socket poller
and aborted on any it did not know. A title that selects on its
/dev/urandom descriptor (Dungeons, PPSA16064, aborts right after opening
it) stopped with "select on descriptors that are not libSceNet sockets".
On FreeBSD the random device is always readable and writable, so these
descriptors are now answered without the poller, and a ready one makes
the socket poll return at once.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
sceAgcDebugRaiseException (NID T6xuVw0KUJo) now logs the exception id
with APS5_LOG_OUT and returns 0, as libkernel's
sceKernelDebugRaiseException does.
Palworld PPSA23126 (#2598) and 007 First Light PPSA11386 (#2523) import
it; neither is shown calling it.
OpenAGC 3c7e8072 (src/game_compat.c) returns 0 and says the function is
a no-op on retail hardware; the ud2 trap it describes for development
hardware is not reproduced. KytyPS5 6f0cddd4 (src/libs/agc.cpp) takes a
uint32 exception_id, logs it and returns 0. OpenAGC declares no
arguments, but a body that only traps reads none, so the uint32 is kept
and the conflict is recorded in TechnicalDebt. The aerolib name is from
zecoxao/sce_symbols 2883963a; ps5-payload-dev/sdk f5d40f02 has only an
untyped stub.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
image_sample_c_lz on gfx1035 returns 0 for 8-bit sRGB, so the unorm clamp was the wrong result. Bilinear cube comparison without DISABLE_CUBE_WRAP keeps throwing because that filter blends across faces.
Co-authored-by: Cursor <cursoragent@cursor.com>
On an RTX 4060 (Ada, driver 617.42) the optimized link of a pipeline built
from libraries loses the device (VK_ERROR_DEVICE_LOST) as soon as it is
bound, which fails agc_driver_pixel_valid_mask in 16 of 20 runs. With
APS5_NO_PIPELINE_LTO=1 it passes 20 of 20. Reporters see the same loss on an
Ampere card with a depth attachment; two Blackwell cards do not lose it.
A device whose driver is VK_DRIVER_ID_NVIDIA_PROPRIETARY keeps the
fast-linked pipeline and no longer queues the link-time-optimized relink.
Other drivers are unchanged.
Refs #2866
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
GuestMemory.cpp reads PROCMAP_QUERY_VMA_READABLE and PROCMAP_QUERY_VMA_WRITABLE from the query result. The fallback for kernel headers without PROCMAP_QUERY now defines them too, so GuestMemory can use this header instead of its own copy.
The icon, title and output path are checked before the ELF is written,
so a malformed icon0.png or param.json leaves no output behind. Paths
with control characters are refused instead of producing an invalid
entry.
Co-Authored-By: Claude <noreply@anthropic.com>
ELF has no icon resource, so when sce_sys/icon0.png sits beside the input
the relinker copies it next to the output and writes <output>.desktop
(freedesktop Desktop Entry) with that icon. Name is the param.json title
without trademark signs or line breaks, or the output name without
param.json. Exec is quoted per the spec and the entry is marked executable
so file managers launch it. A malformed icon0.png or param.json fails the
relink; without icon0.png nothing extra is written.
Co-Authored-By: Claude <noreply@anthropic.com>
sceAgcFuseShaderHalves returns a fused hull as a copy of its back half
(type 7) that is not registered: type 3, no user data, and shader
registers copied to scratch memory with the checksum, RSRC1/RSRC2 and
the LS program address rewritten. sceAgcCreatePrimState accepts only that
type-3 header, and graphics ABI resolution refused it as a replaced
shader header.
SameHeader now also takes a type-3 header as the registered type-7
header for the same code when every other field is equal and its shader
registers differ only in the fused ones. Graphics ABI resolution then
applies the bound header's shader registers over the registered state.
PPSA26344 fuses its hull halves during its loading screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One 4-page direct mapping loses its second page to munmap, then pages
1-2 are mapped fixed. The range starts in the hole; before the fix the
unmap of the overlap threw "guest protection or unmap range has a
hole". The pages around it keep their data.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Subnautica Below Zero stops while compiling a menu shader that compares an sRGB color texture or a bilinear cube face. sRGB uses the unorm reference clamp, and a bilinear cube sample stays on the face it hits.
Written with AI assistance.
GuestAllocationsUnmap took the piece containing the cursor from the
allocation's original extent. After part of an allocation was unmapped,
a later unmap or fixed map starting in that hole still matched the
allocation, and replaceRange threw on the hole. Match against the
registered range itself and end each piece at its end. RIDE 4
(PPSA01599) maps fixed over a partly unmapped direct memory range.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A child process runs from a root whose app0/sce_sys/playgo-chunk.dat
declares 3 chunks: ids 0-2 are local, id 3 is rejected with
SCE_PLAYGO_ERROR_BAD_CHUNK_ID and scePlayGoGetChunkId counts 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without playgo-chunkdefs.xml every chunk id was accepted, so a title
that probes chunk ids until SCE_PLAYGO_ERROR_BAD_CHUNK_ID never stops.
RIDE 4 (PPSA01599, Unreal Engine 4) grows its chunk list until it runs
out of memory. The package's chunk table header holds the chunk count
at offset 0x0A; ids below it are valid. Every id is still accepted only
when neither file exists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FreeBSD fchdir(2) changes the working directory to the directory referenced by a descriptor. The guest imports fchdir from libkernel_sys (PPSA99764, issue #1578) and the module did not export it.
The descriptor's directory is resolved from the path recorded when the directory was opened on Windows, and from the /proc/self/fd link target on Linux. The guest working directory is then set through the libc routine shared with chdir. A descriptor that is not open returns EBADF and one that is not a directory returns ENOTDIR.
AI-assisted: yes
(cherry picked from commit 76329cbb08781e13d757c3ff955acf71f8bd506f)
`truncate` (`ayrtszI7GBg`) is an absent import of PS5MC (PPSA99420),
XPSemu (PPSA97358) and Porpoise (PPSA99764) on `main`. It resolves
through `sceKernelTruncate`, which now returns `EISDIR` for a directory
and the host errno for other failures instead of `EIO`, as FreeBSD 11
`kern_truncate` does. The guest-relative test `guest_truncate` covers
shrinking, growing with zero fill, a negative length, a missing path,
an empty path, a null path, a directory and a read-only file.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hypot(inf, NaN) returned a NaN on main. C Annex F returns +inf when
either operand is infinite, even if the other is a NaN, and FreeBSD
msun (lib/msun/src/e_hypot.c) does so for a signalling NaN too. The
infinite case is now checked before std::hypot; other inputs do not
change.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
#698 taught the reader, the module builder and the Windows writer to
resolve relocations against SHN_ABS symbols and to leave them out of the
exports, but the macOS writer still threw "Unsupported guest export
section" for them. PPSA12544 bundles modules with such an export
(RKDbTdN8TO0), so --macos stopped before writing anything.
The macOS writer now resolves a relocation against an absolute symbol to
its value plus the addend, without a rebase, and skips absolute symbols
when exporting, as the Windows writer does. guest_absolute_symbols covers
the macOS output too.
AI-assisted: yes
C17 7.21.6.2p16 returns EOF only if input fails before the first conversion completes, and a suppressed conversion completes. FreeBSD counts it in nconversions (vfscanf.c, releng/11.4, lines 799-805). The host scanf returned EOF there, so sscanf("12", "%*d%d", &v) reported EOF instead of 0. Each suppressed conversion now records its end with a %n and a result of EOF becomes 0 when one was recorded.
QueueState held the 48 KiB constant RAM inline, so every QueueState
cleared 48 KiB when constructed and copied it when copied.
ResolveShaderAbi and ResolveGraphicsStagesAbi construct one on each call
(sceAgcLinkShaders, sceAgcCreatePrimState, interpolant mapping),
resolveGroupAhead copies the live state, and a queue reset builds a new
one, although few command buffers write the constant RAM.
The constant RAM is now a vector allocated when WRITE_CONST_RAM first
writes it. ConstantRam returns a shared all-zero view until then, so
DUMP_CONST_RAM of unwritten words still stores zeros, and the bounds use
QueueState::ConstantRamWords. The store view ResolveStore returns is
copied by the recorder within the packet, as before.
testMemory dumps unwritten constant RAM and checks for zeros, and checks
that a copied queue state keeps the words written before the copy.
A QueueState is 336 bytes instead of 49464; constructing one takes 0.3
to 0.8 us instead of 1.5 to 2.1 us, and copying one 0.4 to 0.6 us
instead of 1.9 to 3.8 us (GCC 13 -O3). On the title screen of PPSA21402
(Balatro) the clears from the two resolvers took 30 to 129 of 3459 to
7838 CPU samples per 10 s.
PSXS5, SwanStationPS5 and Nuvio stop at start-up on setcontext on main,
and EVO Player and ProsperoStore do on Windows. The three are imported
by the statically linked OpenSSL's ASYNC code.
The three use FreeBSD 11's amd64 ucontext_t (mcontext at 0x10, mc_len
800, uc_link at 0x330, 0x370 bytes), which the titles are built
against: EVO Player's ASYNC_is_capable captures into a 0x370-byte stack
buffer, and async_fibre_makecontext sets uc_link at 0x330 and uc_stack
at 0x338. getcontext saves every register at its call in assembly and
stores the context as FreeBSD's sys_getcontext does: rax and rdx as 0,
the carry flag clear, the FXSAVE image and the signal mask, and nothing
from uc_link on. setcontext checks mc_len and mc_flags as set_mcontext
does, installs the signal mask, masks MXCSR with the host's MXCSR_MASK
and returns through iretq, so every general register and the flags are
restored and nothing is written below the target stack pointer.
makecontext follows FreeBSD 11's amd64 libc.
Windows checks guard-page growth and exception unwinding against the
stack bounds in the TEB. setcontext and longjmp now set them to the
stack they switch to, which makecontext registers and pins writable as
libSceFiber does for fiber stacks. OpenSSL enters a context with
setcontext and then switches with _setjmp and _longjmp.
Iterating a Registers advances with nextPresent, which recomputes the
word, mask and next bit from the index on every step, about 3.5 ns per
register. BuildRegisteredAbiKey walks all of a state's registers, about
300 with the context, and ResolveGraphicsStagesAbi walks each stage's
registered state to merge it, on every sceAgcLinkShaders,
sceAgcCreatePrimState and interpolant mapping call.
Registers::ForEach visits the set registers in ascending order straight
from the presence words, and both paths use it. The key and the merged
state are unchanged.
testRegisterFile checks that ForEach visits what iteration visits, in
order, and skips an erased register.
Building the key for 297 registers takes 200 ns instead of 1030 ns (GCC
13 -O3). On the title screen of PPSA21402 (Balatro) the two functions
took 0.18 to 0.29 CPU samples per sample of the title's own code, and
0.06 to 0.12 with this change.
sleep_for can return up to a scheduler tick early against the
performance counter the guest clock reads, so the first reading could be
just under 50 ms on a loaded machine.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Titles that import it now load; calling it throws until push event
handles have operations that can be aborted.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f50cb08c988736d645f0cc1c329f911f732fa935)
(cherry picked from commit ae3227dbfa2c972b36242056df5971d858ac65c3)
(cherry picked from commit 3df66a6a5dd9f0cd73daaee1342c22742c1915e4)
A T# picked per lane with SelectU32 and made uniform with
v_readfirstlane, as in a waterfall loop, left a descriptor web without
Phis that the web walk could not split. CollectDescriptorPhis now steps
through ReadFirstLane, and a Phi-free tuple containing SelectU32 or
ReadFirstLane is split on the shared condition (SelectU32) or wrapped in
a matching ReadFirstLane. The new nodes are placed after the last
original node of the tuple in its block and inserted only when the web
is split; otherwise they are invalidated like the web's Phis.
PPSA26344 compute shader 0x800037f200 selects its T# per lane between
SRT reads or zero and reads the first lane of the result.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
s_buffer_load returns zero for dwords outside the V#'s range. The SRT
evaluator refused such a read instead, so evaluating a descriptor failed
as soon as one arm of a split T# read through a null or short V#, even
when that arm is never taken. The evaluator now returns zero for those
reads, as the hardware does.
PPSA26344 compute shader 0x800037f200 reads one arm's T# through a null
V# and stopped at "SrtWalker::EvaluateRuntimeSources failed to evaluate
runtime sources".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window was read from APS5_HOST_IMPORT_WINDOW_MIB on the device.
It is now always 8 MiB, the value the previous commit was measured
with; SetHostImportWindow stays for the tests. TechnicalDebt records
the window as a choice.
one span. A title that grows a heap 16 KiB at a time then re-imports the
whole chain on every growth: Ratchet & Clank: Rift Apart re-imported about
1.5 TiB while loading its first scene, and NVIDIA refused grown spans with
VK_ERROR_OUT_OF_DEVICE_MEMORY, which leaves those ranges copied per draw.
Ranges are now joined only when a chain covers a whole aligned window (8 MiB
by default, APS5_HOST_IMPORT_WINDOW_MIB on the device); until the window is
complete each range is imported on its own, and a span never crosses a
window edge. Growth therefore re-imports a range once more at most when its
window completes, instead of the chain on every growth. Joining any part of
a window was tried first and still re-imported 2 GB per second in Ratchet,
as each growth re-imported the window's partial span. A single range larger
than the window is imported whole. SetHostImportWindow(0) keeps the
unbounded spans, which is what the existing tests use.
The new importWindowTests (agc_driver_graphics) checks that a full window is
one span, that a partly filled window imports its range alone, that a view
crossing a window edge gets no import, and that completing one window leaves
the other window's import in place; with the window ignored it fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Titles that import it now load; calling it throws like the other
coredump queries, since host crashes are not turned into guest dumps.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
(cherry picked from commit e0e5fd9ffbbaffdc065d1471ce1c91b144af5f98)
(cherry picked from commit 2ac0c88fdbb9e380b6eb5a37d5f98d557940f131)
(cherry picked from commit 1279010b1bc123d3b87664ede15ec6ab2ced806a)
DecodeShaderStages accepts LS, HS and an NGG passthrough ES, reads the
3-bit VGT_TF_PARAM fields, refuses its other bits but DISTRIBUTION_MODE,
and needs HOS levels that keep [1, 63]; 0x286-0x287 join the draw key.
Local and domain programs are prepared with their stage group: its
headers are merged over one set of defaults, and a local program left
unbound by the hull header is prepared at its first draw.
The local program ends at its jump into the hull half.
TessellationLowering lowers LDS and ring addresses that are affine in
the control point and relative index to tessellation interfaces with
strides from the code, and refuses accesses outside the patch, per-patch
writes from other control points and a modified primitive export. Hull
lanes read their own saved exec bit, also of host-subgroup-masked
copies; other masks are refused, gl_InvocationID loads as a scalar and
factors clamp to 63.
A descriptor dword built by S_BREV_B32, V_BFREV_B32 or S_BREV_B64 from a
constant stayed a runtime value, so the resource could not be resolved
and the shader failed to prepare. The constant folder now reverses the
bits of an immediate the way the hardware does (D[31-i] = S0[i]), like
the existing BitCount32 fold. ConstantBitTest covers the fold.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Waits on eventCondition go through libkernel's TimedWait::Condition (precise
timed waits on Windows) instead of std::condition_variable::wait_for, as
requested in review.
libkernel.prx does not export TimedWait::*, so libSceVideoOut.prx had an
unresolved import and titles stopped at load. Compile TimedWait.cpp into
libSceVideoOut, and use NotifyAll for the notify added on main.
The flip queue's condition variable went through winpthreads, whose
timed waits end on GetTickCount64 steps: the present thread's 10 ms poll
for SDL events and the pad, taken while no flip is queued, ended 11 to
24 ms later on Windows, and WaitForFlipRoom's 60 s bound had the same
granularity. Use TimedWait::Condition, which waits on an event and a
high-resolution timer, so the poll ends at 10 ms and a submitted flip
still wakes the present thread at once.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Use unlocked POSIX EOF/error queries after unlocked byte operations so status synchronization cannot reacquire the stream lock. Keep the verified UCRT status calls on Windows, and cover byte, status, error, and held-lock behavior.
Co-authored-by: MarcZX100 <marczxrr@gmail.com>
agc_driver_dcc_keys_first_write only exercises the import window when the
device's own host import resolves the write protection of the imported
pages, as the NVIDIA driver does on Linux. CI's devices do not, so the
comparison that #2984 adds for BlockMaybeWritten units is not run there.
RunImportWindow opens an import window itself over a pending storage
image with GuestMemory::ImportWatched and stores the byte already there,
which leaves the same stamp without depending on the driver: the image
must stay pending and unchanged. A store of a new byte inside a window
must still be seen. Devices whose imports are compared print that the
window is not tested, as the recorder's (w) test does. Linux only: the
Windows path compares imports.
TechnicalDebt records that an identical-byte CPU store inside a window
cannot be told apart from the import, and that images tracked by layer
still upload again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
range is write-watched. Since #2984, Refresh turns a BlockMaybeWritten
unit into BlockUnchanged only through compareUntracked, which needs that
copy. Where a host import leaves its range watched and making it stamps
the imported pages (the probe's writtenAtImport, 4 of 4 on NVIDIA's
Linux driver), the import window stamps the surface, so a pending
fast-cleared image counted as changed again and
agc_driver_dcc_keys_first_write failed on main (#3179).
HostImports now keeps whether making each kind of import stamped the
probe's pages, and WatchedImportsStampPages reports whether an import
that leaves its range watched can stamp it. The clear path skips the
copy only when no such import can, as oneandonlydean proposed in #3179.
Devices whose probe saw no pages written at the import, devices without
host imports and Windows (whose alias imports don't disarm the guest
views) keep #2699's saving.
An unprobed watch (APS5_WRITE_WATCH_IMPORTS=watch) keeps the copy.
The fast-clear check in agc_driver_dcc_keys_first_write now expects the
copy exactly when the range is unwatched or such a stamp is possible.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceKernelGetProcessTime and sceKernelGetProcessTimeCounter measured from
a start captured on their first call, so the first reading was about 0
however long the process had run, and exactly 0 when both reads fell in
one tick of the host clock (100 ns on Windows). Capture the start when
libkernel loads.
Titles that ship their own libc.prx or Unity's PS5Util.prx import
_umtx_op (04AjkP0jO9U) for atomic waits, call_once and futex-style
waits. Implement UMTX_OP_WAIT, UMTX_OP_WAKE, UMTX_OP_WAIT_UINT,
UMTX_OP_WAIT_UINT_PRIVATE and UMTX_OP_WAKE_PRIVATE as FreeBSD 11's
kern_umtx.c does, on the sceKernelSyncOnAddressWait waiter queue.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HeC9bA3n74TzJRYv9AgBE8
Two defects found in review of the Linux delivery:
A raise right after scePthreadCreate reached the new thread before
RunThread set currentThread, so scePthreadSelf() in the handler adopted
a second handle, and could deadlock on glibc's thread lock when the
signal interrupted pthread_getattr_np. The creator now blocks all
signals around the std::thread construction, so the child starts with
them blocked, and RunThread restores the creator's mask once the thread
is registered, as libthr does. A raise in that window stays pending and
is delivered just before the entry runs.
A handler that set reserved MXCSR bits made rt_sigreturn fail with
SIGSEGV. The MXCSR the handler leaves is now masked with the frame's
MXCSR_MASK (0xffbf when it reads 0), as FreeBSD's fpusetregs does, on
both platforms; Windows also keeps CONTEXT.MxCsr equal to
FltSave.MxCsr.
guest_raise_exception raises right after each of 50 thread creations and
checks scePthreadSelf() in the handler, and its context round trip sets
a reserved MXCSR bit and FZ and checks that only FZ is kept.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwJx7AHrSUaMGSpEGhL1Kb
sceKernelRaiseException threw on Linux, so a title whose garbage
collector stops threads with it (Unity's PS5Util raises SIGUSR1 on each
thread and reads mc_rsp from the handler's context) could not run there.
The raise now sends the host SIGUSR1 to the target thread with
pthread_kill. The host handler, installed on first use with SA_SIGINFO
and SA_RESTART, builds the FreeBSD-layout ucontext from the interrupted
registers and the FXSAVE area, calls the guest handler on the target's
own stack and writes the registers back, so the handler's changes reach
the interrupted code, as they do on Windows. errno is left as the
handler leaves it, as libthr does.
A raise on another thread takes the target's join lock with the host
signal blocked and returns ESRCH once the thread has finished, so a
detached thread's pthread_t is never signalled after it exits. The
pthread_t is recorded when the thread is created or adopted. Handlers
are now lock-free atomics, as the host handler reads them.
guest_raise_exception now runs on every platform. It also checks that
r13 and xmm8 reach the handler and that r12 and xmm8 written by the
handler reach the interrupted code.
AI-assisted: yes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwJx7AHrSUaMGSpEGhL1Kb
Comparison sampling of a color texture required the view's X channel to select red. A title runs image_sample_c_lz on a 1x1 R8 unorm placeholder whose dst_sel is constant 0 on all four channels.
On hardware the compare result is placed in every channel and each returned channel then applies its own dst_sel: 0 gives 0, 1 gives 1, R/G/B/A give the compare result. Return each dmask register from its own select, and throw only for the reserved selects 2 and 3.
On Windows every unwind step called VirtualQuery to find the frame's
module, and a module with a plain .eh_frame (libstdc++, libgcc, libwinpthread)
was searched by decoding each FDE in turn. A C++ exception crossing those
frames cost about a millisecond. Look the module up with RtlPcToFileHeader
first, falling back to VirtualQuery for images the loader does not know,
and binary-search a per-module index of FDE start addresses built on first
use.
A 1D sample or load of a 2D T# had no runtime image mode after the mode table landed, so it was still refused at materialization. Sampled 1D accesses now also accept a 2D view; the address completion in the emitter reads the missing row as 0.
Measured on hardware: a position component the instruction omits reads as 0, like an explicit 0, and the next VGPR is ignored. Missing array layers and derivative components are 0 too. Cube coordinates still need all components.
Multi-level views, and views starting above mip 0, are bound as their base
level only when every use of the image is image_sample_lz through an
unnormalized S# without minimum LOD or LOD bias, the case measured exact on
RDNA2. Selected-LOD, implicit-LOD and offset uses keep throwing.
The recompiler records selected-LOD uses (SamplerUseNonZeroLod) and proves
the case per image (imageUnnormalizedBaseLevel); the shader disk cache
format goes to 27.
Co-Authored-By: Claude <noreply@anthropic.com>
Vulkan only accepts unnormalized samplers on single-level views, while the
guest binds full mip chains and samples their base level. Bind a lazily
created one-level view of the descriptor's base mip instead of refusing
mipmapped views and views that start above mip 0.
Forwards _sceUltWaitingQueueResourcePoolCreate and _sceUltSemaphoreCreate to the existing calls, like the other underscore Ult exports. Declares sceNetShowIfconfig, sceNpWebApi2GetMemoryPoolStats, Json::Value::setNullAccessCallBack, kqueue, kevent and one pad call of unknown name without an implementation.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
normF32 took the error of p = |x| * scale from fma(|x|, scale, -p).
Vulkan only guarantees GLSL Fma on f32 the precision of a multiply
followed by an add, and lavapipe in Mesa 25.2.8 splits it, so the error
was 0 there and products that round to k + 0.5 were resolved by
roundEven: v_cvt_pknorm_u16_f32 of 0x37c000c0 gave 0x00020002 instead of
0x00010001.
scale + 1 is a power of two, so A = |x| * (scale + 1) is exact and the
product is A - |x|. Since A >= |x|, p = A - |x| and the error
(A - p) - |x| are computed with correctly rounded subtractions only
(Fast2Sum), which every driver gives exactly.
Map red and blue exports, write masks and constant-color blend factors together. Reject conflicting simultaneous target constants.
Semantics: Mesa 26.2.2 src/amd/common/ac_formats.c ac_translate_colorswap; console equivalence remains unverified. Graphics tests passed. Written with AI assistance.
v_min_f16, v_max_f16, v_min3_f16, v_max3_f16, v_med3_f16, v_pk_min_f16
and v_pk_max_f16 converted their sources to f32, ran the f32 min, max
or med3 with its NaN and signed-zero rules, and converted the result
back to f16 before applying the f16 NaN rules on the bits. The result
is always one of the sources, or for two zeros their sign bits
combined, so it is now chosen on the f16 bits: an integer comparison of
the bits with the sign folded into an ordering key, the same AND/OR
rule for two zeros, then the existing NaN, IEEE_MODE, denormal flush
and clamp rules.
The integer comparison doesn't depend on how the driver converts f16
denormals to f32. Comparing the f32 conversions instead fails on an
Intel UHD, whose UnpackHalf2x16 makes f16 denormals zero when the
module declares no f16 DenormPreserve, as these instructions no longer
convert to f16.
New test agc_driver_float16_min_max_bits checks the seven instructions
(VOP2 and VOP3 forms with abs, neg, op_sel and clamp; packed op_sel,
op_sel_hi and neg) on random and special inputs against a model of
those rules, in seven float modes including the f16 input denormal
flush. Current main gives the same results as the model on an RTX
4060, an Intel UHD and lavapipe.
Capture errno before the ENOTDIR lookup, translate EINVAL from a name
under a regular file to ENOTDIR, check the parent only once and strip a
trailing separator before taking it, and split the dense rmdir return.
On Windows mkdir_nid_postfix returned ENOENT and sceKernelMkdir and
sceKernelRmdir returned ENOENT or EIO when a regular file was used as a
directory in the path. FreeBSD mkdir(2) and rmdir(2) return ENOTDIR for
a path component that is not a directory.
Allocate cached sets without individual-free pools. Reset a pool after its
last owner releases it. Keep stable pool indices, skip full pools, and
terminate if reset fails. No GPU wait is added.
This avoids RADV 26.2.2's affected individual-free allocation path.
Validation: Fedora 44 Release graphics tests pass. The reset-failure
regression fails on the previous behavior and passes on this change.
llvmpipe and RX 6600 pass the 3,301-set lifecycle test with core and
synchronization validation. Windows was not tested.
AI-assisted: yes.
FreeBSD's kernel_sigaction rejects SIGKILL and SIGSTOP only for a handler other than SIG_DFL, so signal(9, SIG_DFL) and signal(17, SIG_DFL) succeed. Drop the SIG_ERR rejection, which has no kernel basis and which sigaction does not apply.
Add an optional input helper for test runs without desktop input injection. Preserve physical input and release injected buttons automatically.
Validation: guest_pad tests and Python helper CLI smoke checks passed. Written with AI assistance.
signal() returned SIG_ERR with EINVAL for any signal without a host
equivalent, such as SIGHUP, SIGPIPE and SIGUSR1, while sigaction()
accepted them. FreeBSD's signal() is sigaction() with SA_RESTART and fails
only for numbers outside 1..128 and for SIGKILL and SIGSTOP.
The Windows formatter passed %p to the host CRT, which prints 16
zero-padded digits without a prefix. FreeBSD prints 0x followed by the
lowercase hex digits (0x0 for a null pointer), as if by %#lx.
Add standard quiet NaN generators nanf (DZU+K1wozGI) and nan (zck+6bVj5pA) to libc.
Resolves startup failure in VLC for PS5 (PPSA85300) where execution halts at nanf on Linux once strsep is available.
PPSA21703's Unity PSN plugin imports sceNpWebApi2GetMemoryPoolStats
(Xweb+naPZ8Y), so the title did not start.
sceNpWebApi2Initialize now records the pool size of each context and
sceNpWebApi2Terminate forgets it. The statistics report that size with
no peak or current use, as sceHttp2GetMemoryPoolStats does, since nothing
is allocated from the pool. A null output or an unknown context throws.
The signature and layout follow the Http2 statistics and are recorded in
TechnicalDebt.
The library gets its own CMakeLists.txt, equivalent to the generic rule,
to register its test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The typed descriptor heaps write a null descriptor for a null guest
image, and the device therefore required VK_EXT_robustness2's
nullDescriptor. MoltenVK has the extension but not the feature, so on
macOS no Vulkan device opened and every GPU test was skipped. The
feature is now enabled when the device has it. Without it, the device
creates a placeholder image per typed image heap: 1x1, cleared to zero,
of the heap's dimension, sample count, numeric class and access, and a
null guest image binds the placeholder of its heap. A heap whose
placeholder the device cannot create throws when a null image reaches
it. TechnicalDebt.md lists where a placeholder differs from a null
descriptor.
agc_placeholder_images checks every image resource the recompiler maps
to a heap against that heap's placeholder, so the two cannot drift
apart, and agc_shader_device_profile now expects a device without
nullDescriptor to be accepted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
NVIDIA's Vulkan compiler (driver 616.92) aborts while building a pipeline
whose OpFUnordLessThanEqual or OpFUnordGreaterThanEqual compares against
+infinity. The negated inverse ordered relation gives the same result for
every input, NaN included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace per-draw Vulkan descriptor copies with a single write from retained buffer and image metadata. Keep per-draw guest snapshots, sets and resource ownership unchanged. Baseline RX 6600 profiling attributed 28% of graphics-worker cycles to descriptor copies.
Validation: mixed descriptor and snapshot regression passes on RADV RX 6600 and llvmpipe with Vulkan validation; graphics and guest-memory tests pass. Written with AI assistance.
v_ldexp_f16 ignored the FLOAT_MODE denorm16 flush bits on main. Flush a
denormal half input when bit 0 is clear. When bit 1 is clear, flush the
result when its exact value is below the minimum normal half, decided
on the f32 product before the half conversion rounds it: 0x07ff scaled
by 2^-1 is zero on RDNA2, not the minimum normal. NaN handling and
destination high-half preservation stay the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Report the thread's affinity mask as a 32-byte FreeBSD cpuset_t, with
FreeBSD 11's errors: ERANGE for any other set size, ESRCH for a null
thread and EFAULT for a null set.
EVO-PLAYER (PPSA99039 v0.11.0) imports it (Jb2uGFMr688) and calls it
once with a 32-byte set on pthread_self().
A fill was taken to cover the surface when it spanned ceil(w/8)*ceil(h/8)*4 bytes, a linear HTILE layout. In
the pipe-aligned SW_64KB_Z_X layout a surface's HTILE words lie across a 32 KiB metadata block per 1024x512
pixels (AMD PAL gfx10 addrlib: a 16x16 surface has a valid word at offset 260), so a fill of the linear size
cleared tiles it never wrote. HtileMetadataBytes gives that extent; the fast clear tests fill it (the compute
fixture now dispatches one group per KiB with TGID_X).
Reported with a PAL reproducer by @Amoenus.
Co-Authored-By: Amoenus <5391396+Amoenus@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`Recorder::Active()` is process-wide. With two devices (agc_driver_depth_fast_clear_submit draws on its
own device; its compute-fill DCB makes the driver create a second one, whose recorder becomes the
active one) a surface's fast clear was recorded into the other device's command buffer, unordered with
the draw: the compute-fill case failed about once in 60-90 runs. A surface now records into the active
recorder only when that recorder is its own device's, and submits on its own device otherwise.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rebased onto main as one commit (the six commits of #374). A uniform fill over a depth surface's whole
HTILE (DMA_DATA, the fill kernels or a CPU store) clears the depth and stencil aspects its ZMask and
SMem fields say, to DB_DEPTH_CLEAR / DB_STENCIL_CLEAR, when the surface is next bound or sampled.
Main's RADV clear_htile_mask recognition (#2229) now goes through the same pending clear: an HTILE clear
marks the matching surfaces' depth aspect, and one for a surface not created yet is applied when it is
first bound. Main's HTILE address decode and this one are merged into one (TILE_SURFACE_ENABLE, a
nonzero DB_HTILE_DATA_BASE and a depth format).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Resolve SDL2, freetype, Vulkan-Headers, SPIRV-Headers, glslang and
SPIRV-Tools from the system instead of the bundled submodules when the
option is ON. It is OFF by default, so the bundled build is unchanged.
Every package is required: a missing one, or Vulkan-Headers older than
1.4.310 (the agc driver uses VK_KHR_maintenance8), fails the configure
with an error and nothing falls back to the submodule. The system
packages are exposed under the same target names the bundled path uses,
so the rest of the CMake is untouched.
SDL2 is linked from the system as a shared library because the prx
libraries are shared objects and distribution static SDL2 is usually not
PIC. ffmpeg-core, libjpeg-turbo, stb, VulkanMemoryAllocator and LibAtrac9
stay bundled.
sceSharePlayGetCurrentConnectionInfoA now writes a 72-byte
SceSharePlayConnectionInfoA that reports no session: status DORMANT,
mode 0, empty online IDs, account IDs 0 and both user IDs invalid (-1).
A null pointer returns SCE_SHARE_PLAY_ERROR_INVALID_ARGS (0x810E0001)
instead of throwing. KytyPS5 and fpPS4 agree on the layout and the error
code; KytyPS5 sets the user IDs to -1, fpPS4 leaves them 0.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
Relinks the ELF modules of extra directories under the module parent directory. Modules the executable or another module needs start with the executable; the others are initialized when the title loads them with sceKernelLoadStartModule, with its start arguments (Windows: __aps5_guest_initialize export; Linux: left out of DT_NEEDED).
Squashed onto main by the reviewer (history had a merge commit).
Explicit-LOD offset samples through FORCE_UNNORMALIZED samplers now add
sign-extended texel offsets to f32 coordinates before clamping, using
OpFAdd with NoContraction and a sampler specialization selector.
Without native nonconstant-offset support, retain only runtime image
modes valid for unnormalized sampling and reject normalized samplers.
Preserve constant-swizzle handling, indirect-image rejection and main's
flat-volume and flat-line compatibility rules when selecting those modes.
Serialize image compatibility and folding flags and the sampler folding
flag in cache format 34, with roundtrip, preparation and execution coverage.
Retain main's request format 15 and runtime ABI 11.
MinGW-w64's UCRT has no aligned_alloc, so the Windows build of
guest_job_manager_tests fails since #3170. Use the aligned operator new
and delete instead, which are standard C++17 on every host.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60):
- sceSslGetMemoryPoolStats returns 0x8095F007 for a null output, before
the id check, and 0x8095F006 for an unknown or terminated context,
zeroing the 32-byte output.
- sceHttp2GetMemoryPoolStats returns 0x817B1225 for a null output,
before the id check, and 0x817B1100 for an unknown or terminated
context, zeroing the 32-byte output.
- sceNetGetMemoryPoolStats checks the pool before the output, so an
unknown pool with a null output returns EBADF.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PS5 dumps carry no playgo-chunkdefs.xml (only sce_sys/playgo-scenario.json, which
lists no chunks), so the valid set fell back to chunk 0 and every query for another
chunk returned SCE_PLAYGO_ERROR_BAD_CHUNK_ID. A title that asks for all of its chunks
in one scePlayGoGetLocus call then never sees them as installed and waits forever.
Without definitions the chunk table is unknown and a dump is fully installed, so any
chunk id is accepted and reported as local. With playgo-chunkdefs.xml present,
validation is unchanged. scePlayGoGetChunkId still reports the known set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coherent (GLC/DLC) global loads were emitted as Volatile loads through
physical pointers. Volatile does not make another workgroup's stores
visible within the same dispatch, so a workgroup spinning on a flag
written by another one (decoupled look-back scans, as in RADV's
acceleration-structure build) never saw it on NVIDIA. Aligned coherent
dword reads now use OpAtomicLoad at device scope; the byte path keeps
the old reads for unaligned addresses.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
VLC (PPSA85300) imports daemon (Wn6I3wVATUE) and fails to load because
no library exports it. Forking into the background is not supported,
so it throws if called. VLC 3.0 calls daemon only when its daemon
option is set, and the title starts libVLC with --ignore-config and
without --daemon.
A job's inputs were joined into one vector before decoding, copying every
input buffer at its stated size. PPSA36082's FMOD submits Opus run jobs
whose one input buffer states 0xFFFFFE74 bytes (a remaining size that
went below zero) ahead of a single packet, so each such job copied about
4 GiB and sceAjmBatchStart took about a second; the mixer missed its
deadline and the title pushed silence. A job with one input buffer now
decodes from that buffer in place, reading only the packets it decodes;
jobs with several input buffers are joined as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the retry on <name>.prx after libs/<name>.prx fails. The test now
runs from its own directory holding libs/libSceFiber.prx, copied from
the libSceFiber output, so the single path resolves in the test too.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A stack base need not be page aligned (it is not on Linux), so rounding the
range down first could leave the stack and miss the stack path. The range the
guest asked for is now checked against the guest stack, and the rounded
pages are protected, as FreeBSD rounds the address down.
A thread that exits with part of its stack protected left those pages to the
next thread that reused the stack. Protected stack pages are recorded, and a
thread's stack is made readable and writable again when it is unregistered at
exit.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows a guest thread runs on its host thread's stack, which is not a
registered guest allocation, so mprotect on any of its pages failed with
EINVAL. A guest that guards the bottom of its own stack (Dolphin's JIT
read-protects 64 KiB there to catch stack overflows) got an error where the
PS5 changes the protection.
A range that lies inside a live guest thread stack, with no GPU access
requested, now takes the native protection directly. Ranges that cross the
stack's bounds are still rejected.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Link libc, configure Windows unwinding for the test so its runtime_error
checks are caught, and run it from libSceFiber's output directory so
openHostModule finds the unpatched libSceFiber.prx next to the modules the
test already loads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9MzdVqDf6wHUKzNymQSze
Return a host module handle for a loaded sysmodule id so callers can
sceKernelDlsym after sceSysmoduleLoadModule. Unknown or null inputs
throw like neighbouring internal load paths; unloaded ids return
0x805A1001. Unblocks Prospero Win past NID D8cuU4d72xM at load.
Co-Authored-By: Composer <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
#3158 imports a run of address-adjacent registered ranges as one span.
On Windows a shared mapping in the span needs a read-write alias, and
MapAlias refuses one unless the range is a single contiguous run of
views of one section. Ranges that are adjacent in guest address can be
separate views, so the refusal escaped from importAllocation and
agc_driver_dcc_keys_first_write failed on main with "read-write alias
of shared guest memory ...: the range is not one contiguous run of
views of a section".
A span whose alias is refused is now remembered, and its ranges are
imported one at a time as before spans; a single range that is refused
still throws.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
A shader store through the BDA table records its 4 KiB page in the fault
buffer, and the driver marks those pages written after the use. The page
number was truncated to 32 bits, so a store above 16 TiB marked a page at
another address. And the driver scanned all 4096 slots after every use,
even when nothing was stored.
Slots now hold a 128 KiB chunk (its number plus one, 32 bits for any
address below 512 TiB) and a mask of its 4 KiB pages. A shader that
claims a slot appends it to a list, with a count. The driver reads the
count, decodes only the claimed slots, marks each run of pages once and
clears only those slots. A writable BDA range past 512 TiB is refused
when the table is built.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since #1454, a libSceLibcInternal import that no native library exports
on Windows falls back to the title's own libc.prx. The loader shim that
ps5-native-app-boilerplate builds into homebrew titles exports 1874
functions at three addresses whose code is `xor eax, eax; ret` or
`pxor xmm0, xmm0; xor eax, eax; ret`. Such an import then binds to a
function that returns 0, and the title starts instead of stopping with
`unresolved ELF import`.
The Windows guest module writer records the exported functions whose
code is one of these two sequences, and the entry stub leaves the guest
libc.prx out of the search for those names. A guest export with code of
its own still provides a missing libSceLibcInternal function, and native
libraries keep their priority.
A draw or dispatch whose shaders have guarded descriptor slots (or a rect
list) but no BDA table gets a fault buffer, and with it NeedsCompletion(),
which kept its resources out of the resource cache, and NeverReusable
skipped the cache lookup for it. In PPSA36082 nearly every draw in the hub
is such a build, so the cache served none of them.
Such an object has no table to revalidate, no stores through BDA and no
copied writes, so it is now reusable. Each use of a reused object checks
the fault record when its batch completes (CheckFaults) instead of
running the once-only write-back; draw recipes over such a template
register the same check, and dispatch recipes still skip templates with
completion work. NeverReusable now refuses what noteReusable refuses: a
BDA table, or a fault buffer with APS5_LOOP_GUARD set (its fault resets
the buffer).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Extend the independently Windows-tested WAIT_REG_MEM candidate 056534a9 with a focused shutdown regression. Keep labels unsatisfied on graphics and two compute queues beyond the configured diagnostic, then require prompt shutdown without executing the commands or pending submissions behind those waits.
The production cancellation path already calls CheckFailure/checkStopping and catches ProcessShutdown in the worker; no runtime behavior is changed. This test does not explicitly open a Vulkan device.
Validation: Linux/GCC 13.3, C++20, -O2 -Wall -Wextra -Werror object compilation against the production headers; conventions and whitespace pass. CMake configuration, runtime linking and execution, and native Windows execution were not performed in this environment. Runtime verification remains pending. Depends on the persistent-wait candidate for #2832/#2985; maintainer policy approval is pending.
AI-assisted: yes.
With the f32 denormal field of FLOAT_MODE set to keep, v_ldexp_f32
flushed subnormal inputs and results to zero. Keep subnormal inputs and
round a result that lands in the subnormal range in the f32 round mode
of FLOAT_MODE. An overflow follows the round mode as well, with or
without denormals kept: the largest finite value instead of infinity
when the mode rounds that sign toward zero. This matches RDNA2
(gfx1036) on 2163 rows in each round mode. Denormal modes 1 and 2 now
throw.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Unsubscribe an event queue from the output mode events that
sceVideoOutAddOutputModeEvent registers, the same way the flip and
vblank events are removed. Deleting a registration that does not exist
succeeds, and a null queue or an invalid handle throws.
Co-Authored-By: Gemini 3.8 Flash <noreply@google.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The bundled libc.prx of PPSA21703 calls sceKernelGetModuleInfoFromAddr
with a 0x1A8-byte stack buffer and never sets st_size, so the field holds
whatever was on the stack (983040 in one run) and the call threw. The
console cannot require the field on input. The full size is still
written back. The flags check is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
vkQuake's NGG culling vertex shaders load only the vertex ID
(SPI_SHADER_PGM_RSRC2_GS ES_VGPR_COMP_CNT 0), and DecodeShaderStages still
required 3 for a program routed to the mesh path. The merged ES inputs keep
their positions whatever the count, and the mesh entry loads all of them,
so the count is now checked only for real geometry programs, as the GS
VGPR count already was.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Apply the three-case test correction offered by Scramblrrrr in 36b26122a5458028f76fe7c7710333fcfca80f6d on issue #2832. Preserve all current-main test additions outside those three functions. Credit the supplied implementation to its contributor.
Independent Windows Vulkan results and the timeout-return negative control are reported in issue #2832. Local validation: exact three-function source match, preservation of unrelated Submit.cpp content, conventions and whitespace. Native Windows/GPU execution was not performed here. Shutdown cancellation and persistent-wait policy remain unresolved.
AI-assisted: yes.
Co-authored-by: Scramblrrrr <70121301+Scramblrrrr@users.noreply.github.com>
Retain the original WAIT_REG_MEM fix from #2985 and resolve its test-registration conflict with main d7cfa728. Preserve both upstream test blocks and the original commit ancestry.
Local conventions and whitespace checks pass. Windows/GPU results cited in issue #2832 belong to their reporting contributors. No local GPU execution.
The original fix predates llroundf, which was added in #2933. Use the same guest range and exception behavior for the fourth wrapper and include it in the edge-case checks.
FreeBSD amd64 converts the rounded value with a plain cast, so NaN, infinities and values outside the 64-bit range give 0x8000000000000000 with FE_INVALID raised. The Windows host llround returns 0 for them.
(cherry picked from commit 0ee2741534bdfcc9d818b1d93f2eb793a68f111e)
FreeBSD's memset_s returns EINVAL for every constraint violation and writes nothing when smax or n exceeds RSIZE_MAX. The guest returned ERANGE when n exceeded smax and wrote up to min(n, smax) bytes even when smax or n exceeded RSIZE_MAX.
(cherry picked from commit bf08837cf929e40f1b646df82e140a33076d5587)
Following the ImageRead case, every image operation was checked against
the two lists the masked-lane walk uses: accesses guarded by a trailing exec
operand, and lane-local values.
- ImageWrite carries its exec last like StoreBuffer, and EmitWriteOp stores
only inside an if on it (bindless tables and FMASK refuse the store). It
joins the exec-guarded list.
- A gather with an LOD of its own (gather4_l, gather4_lz) or outside a pixel
shader reads only its lane's address, like the explicit-LOD samples the
walk already follows. A pixel gather without one still keeps the select,
since on the GPU it takes its LOD from the quad.
- ImageQueryDimensions returns the size of its lane's level of the image and
is lane-local.
Left out on purpose:
- ImageQueryLod and implicit-LOD samples read their quad.
- ImageBvhIntersectRay has a trailing exec operand, but its emitter ignores
it and records faults in every lane.
- Image, buffer, shared and address atomics are guarded by their exec in the
emitter too, but the test from 60c65521 pins them as keeping the select,
and no refused shader has needed them so far.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An ImageRead carries a trailing exec argument like the buffer loads, but it
was in neither the read-only-where-active list nor the lane-local list, so a
masked select feeding its address was kept.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Kena: Bridge of Spirits (PPSA01802) refuses ten pixel shaders with
packed pixel ancillary input has an unsupported live use (used by SelectU32 ? ?)
at any visit limit. All ten have 30 to 33 phis on loop back edges (reported
on Linux with an RX 9070 XT).
Two rules stopped the proof at a loop, and the first one fires before the
second is ever asked:
- The walk from the select gave up at the first use inside a loop. A value
derived from the select reaches a loop phi as soon as its register is
written inside the loop, because the masked write becomes
Select(loopExec, new, phi). A loop that leaves exec alone needs nothing
more than this.
- An exec that a loop narrows and carries around its back edge is a phi
inside the loop, and such phis counted as not implying the write's exec.
The walk now follows uses into loops. A phi inside a loop implies the
write's exec when all of its incoming values do, where the phi itself counts
as implying it while its back-edge values are checked. This is sound because
each rule of the proof takes a lane bit of one value from a value computed
earlier: a phi's value on each iteration is the incoming value computed
before the edge was taken, a set lane of an And is set in both operands, a
set lane of a ballot is a lane whose predicate held. If any value in the
proof had a lane set where the write's exec is clear, the earliest such
value would take that lane from an earlier value of the proof that is set
there too, which contradicts it being the earliest. Two conditions keep this
true:
- The write's exec must be computed once, outside every loop. Otherwise a
phi can carry a ballot of it from an earlier iteration, which is not the
value the select used. Masks computed inside a loop keep the old rules.
- A result may only be kept if the assumptions it used held. When a value
that was assumed turns out not to be implied, every result recorded since
it was assumed is dropped. Results that say "not implied" are kept; they
only make the proof refuse more.
A lane that is turned back on inside the loop makes the back-edge exec wider
than the write's exec, so the phi is not implied and a read under it keeps
the select. A lane that is turned back on but never reads the derived value
does not.
New tests cover a loop that carries the write with exec saved, narrowed and
restored (read inside and after the loop), a loop that leaves exec alone,
and the helper-only ancillary select feeding such loops. The refusing tests
each fail when the matching check is removed: a revived lane that reads the
write inside the loop (ignoring back-edge values), a result proven under an
assumption that later fails (keeping it), and a write mask computed inside
an earlier loop (allowing such masks). Two existing tests expected a loop to
keep the select although every read is under the write's own exec; they now
expect it to go, next to new counterparts with a wider exec.
On the Taxi Chaos 2 requests, compute shaders with a loop inside a branch now
lose the branch's masked writes: 0x194000b400 removes 207 selects instead of
36, 0x19002bd200 111 instead of 28, and seven more gain 5 to 43. In
0x16400cf700 the 26 new removals are writes under the branch condition
tid < count; the loop's exec starts as a ballot of that condition, only loses
lanes at the latch, and every store and atomic inside the loop runs under it.
No proof on these requests had to drop a result.
Walks now go on into loops, so the pass takes longer on loop shaders, but it
stays below main. Time in MaskedSelectEliminator per prepare (median of 3,
the pass runs twice per prepare), 54 Taxi Chaos 2 requests, one run:
shader previous commit this commit main 5edae353
0x1ac0413000 334 ms 349 ms 424 ms
0x19002bd200 38 ms 103 ms 112 ms
0x19c016aa00 9 ms 74 ms 16 ms
0x194000b400 13 ms 31 ms 32 ms
all 54 547 ms 805 ms 977 ms
0x19c016aa00 removes nothing more; its walks only end later inside its
loops. The 144 requests of the NVIDIA dump go from 1283 to 1589 ms and gain
removals in the copies of the same shaders. 197 of the 198 requests prepare,
as before.
Related: #2548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LEGO Horizon Adventures (PPSA14632) still refuses a pixel shader with
packed pixel ancillary input has an unsupported live use (used by SelectU32 ? ?)
although it has the form the previous commit handles: the only whole-word
reader is SelectU32(IEqual32(HelperInvocation, 0), BitFieldUExtract(x, 0, 11),
ancillary), the exec words are Ballot(not helper) & Ballot(true), and there
are no image ops, stores or loop phis. About 10,250 IR values derive from the
word, so the walk from that select stops at its fixed limit of 4096 visited
values and the select is kept. With the limit raised to 8192 the shader
prepares (reported on Linux with an RX 9070 XT).
A walk may now take max(4096, 2 * instructions) steps. A step is a value
visited for the first time or an implication evaluated for the first time.
The walk only visits values derived from the select, at most one per
instruction, so a walk is only cut short when the implications it evaluates
cost as much as the program itself. On the 198 Taxi Chaos 2 requests no walk
needs more than 0.79 * instructions steps. One walk stays linear in the
program, O(instructions + operands): each value is expanded once, and with
the per-walk memo each implication is evaluated once.
The walk is also cheaper, so that the larger limit costs nothing:
- visited values are marked in a table indexed by value id instead of a hash
set, and the table, the work list and the memo are reused across walks;
- blocks outside loops are looked up in a table indexed by block id instead
of a hash set. This lookup ran once per use and was most of the pass's time.
Time in MaskedSelectEliminator per prepare (median of 3, the pass runs twice
per prepare), 54 Taxi Chaos 2 requests:
shader before 40a8a5a7 this commit main 5edae353
0x1ac0413000 689 ms 285 ms 401 ms
0x19002bd200 102 ms 34 ms 100 ms
0x18c07bf000 191 ms 52 ms (refused)
all 54 1294 ms 484 ms 919 ms
0x1ac0413000 and its three copies in the NVIDIA dump now remove 659 selects
instead of 625: their walks no longer stop at 4096. No other request changes
what it removes. 197 of the 198 requests prepare before and after; the other
one fails the same way in both (v_readlane_b32 of lane 63 on a 32-lane host
subgroup).
Related: #2548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Taxi Chaos 2 (PPSA30279) pixel shader 0x18c07bf000 is refused when it is
registered, and the title stops:
packed pixel ancillary input has an unsupported live use (used by SelectU32 ? ?)
The shader reads the ancillary VGPR (v2) once, with v_bfe_u32 v3, v2, 16, 11,
which #1904 lowers to Layer. The next write to v2, v_rcp_f32 v2, s33, runs
before any kill under the initial exec and becomes
SelectU32(IEqual32(HelperInvocation, 0), rcp, ancillary). The raw word
therefore survives only in helper lanes. The shader has no s_wqm, no DPP,
readlane or swizzle, no stores, and only *_lz samples and gathers. Every
helper-lane value that comes from v2 is dropped by a later exec select, a
compare that is ANDed with exec before its ballot, or an export whose exec
excludes helper lanes.
MaskedSelectEliminator exists to remove selects like this one, but it could
not prove the case. The kill paths save and restore exec, so the later execs
are phis at block merges, and the AND chains the kills build go deeper than
its eight-level limit.
When it checks whether a later exec implies the write's exec, it now:
- accepts a phi when the phi and each incoming value lie outside loops and
every incoming value implies that exec;
- pairs low and high exec words merged by phis in one block, predecessor by
predecessor;
- memoises results for each select and follows up to 64 levels. A cycle
counts as not implied.
Once the select is gone, nothing reads the ancillary word, so no bit of it
is produced. A select whose false lanes can still be observed is kept and
the shader is still refused. That covers a data test that leaves the word in
non-helper lanes that later export, an export under the full wave, and an
unmasked ballot.
Related: #2548
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the dynamic symbol count is absent from the SCE or SysV hash metadata, derive it from the GNU hash buckets and chain. Keep all table reads within mapped file-backed ranges.
Carried from issue #3224, commit 1a10126089882a2306318f18d19d05b04a764d6c; original author preserved.
The fp16 overflow shader translates to 64-bit float instructions, so a device without shaderFloat64 cannot run this test. Skip before recompiling on unsupported devices.
Carried from issue #3230, commit 922d4015e05c34f616912e1913854007a5d689bf; original author preserved.
SharedCodeHeaders draws on the initial context, whose PA_CL_CLIP_CNTL
selects the [-1, 1] clip space with viewport Z scale 1 and offset 0, so
the draw asks for a viewport depth range of [-1, 1]. Devices without
VK_EXT_depth_range_unrestricted, such as Intel GPUs on Windows, reject
that and the test fails before it reaches registration. Set
DX_CLIP_SPACE_DEF as the other draw tests do.
(cherry picked from commit 3c3bcf8f80cceaba4aa1f8742c32251fcc143e6b)
A 2D surface and a one-slice 2D array over the same memory are separate
storage images, since the storage key holds the dimension. pendingAlias
only took a pending image of the same dimension, so a refresh of one
view wrote the other view's results back and uploaded them again.
Sonic Origins Plus (PPSA05324) switches between the two views of the
same surfaces about 50 times a frame in its opening animation.
pendingAlias now counts a 2D surface and a one-slice 2D array as the
same dimension. The rest of the shape test (size, array range, mips,
tiling, element size, image layers and depth, tracked units) and the
conditions of the device copy (uncompressed keys, a host import, the
surface's write stamps) are unchanged.
agc_driver_storage_one_slice_alias stores through the 2D view twice and
loads through the array view after each store. It checks the texels
read through the array, that the 2D image is still pending after the
array image's refresh (where the imported block is write-watched), and
the guest memory after the final flush. Without the change the pending
check fails on lavapipe.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DT_SCE_STRTAB, DT_SCE_SYMTAB, DT_SCE_RELA and DT_SCE_JMPREL are offsets
into the PT_SCE_DYNLIBDATA segment, as the code analysis and the guest
image reader already read them, but the pipeline read them as file
offsets. That only works when the segment starts at file offset 0, as in
the test fixtures. A PS4 executable (CUSA00900) keeps the segment at
0x53e9b40, so the relinker read its relocations from the code and failed
with "Dynamic string offset is outside DT_STRSZ". Without the segment the
values are still read as file offsets.
(cherry picked from commit 5dbf89a57e096f2891ae786f44c3f57935af2285)
Shaders with f64 instructions now declare DenormPreserve for 64-bit floats
when the device reports shaderDenormPreserveFloat64. Without it Mesa ANV
flushed the subnormal result of v_fma_f64 to zero.
(cherry picked from commit 7df6c709de994cea0019707d45b6e7b96698fad1)
The buffer fill and copy fast paths returned before the partial last group
was applied, so a PARTIAL_TG_EN dispatch of the fill kernel filled whole
groups, and a zero last group did not throw. The partial sizes are now
decoded before the fast paths, which are skipped when the last group is
partial; the dispatch then runs the shader with the partial thread counts.
PartialGroupThreads validates all three axes before an empty dispatch
returns, and throws when (groups - 1) * full + last does not fit in 32 bits
instead of wrapping. Validate rejects PARTIAL_TG_EN combined with
USE_THREAD_DIMENSIONS (initiator 0x63).
agc_driver_pattern_buffer_fill now runs the fill kernel with a 16-of-64 last
group through the driver and checks that 80 dwords are written.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RADV sets the draw and dispatch indirect base with SET_BASE to the
argument buffer address plus the command's offset, which Vulkan requires
only to be a multiple of 4; vkQuake's base ends in 0x14. The validator
required 8-byte alignment. The records are read as 32-bit words, so a
4-byte-aligned base is now accepted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RADV emits 0 as the start-instance location of DRAW_INDIRECT and
DRAW_INDEX_INDIRECT when the vertex shader does not read the base instance,
and as the draw-index location of the multi forms when draw IDs are off.
The validator accepted only 0x280 as "no register". Location 0 is now
accepted and decoded as 0x280; vkQuake's draws use it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RADV clears ORDER_MODE in the dispatch initiator for a dispatch that
needs its waves launched in order, and the validator required it set.
In-order launch is the stricter guarantee and the driver does not reorder
waves either way, so a direct or indirect dispatch now needs only
COMPUTE_SHADER_EN; vkQuake's map load issues one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RADV writes every COPY_DATA memory destination with DST_SEL 5
(COPY_DATA_DST_MEM in Mesa's sid.h), the current form of the deprecated
GRBM-synchronized DST_SEL 1 that was already accepted. vkQuake's timestamp
writes use it. It is now validated, resolved and executed as memory like 1
and 2; GDS and performance-counter destinations are still rejected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Where the GPU has no LOAD_CONTEXT_REG_INDEX, RADV loads an image's clear
values into DB_STENCIL_CLEAR/DB_DEPTH_CLEAR and CB_COLORn_CLEAR_WORD0/1
with COPY_DATA from memory to a register (DST_SEL 0), one or two dwords.
The validator rejected every register destination.
A COPY_DATA from memory to one or two context registers is now accepted.
It is not a memory store, so the packet drains like other COPY_DATA the
driver cannot record on the GPU, and executing it reads the source then and
writes the registers. Register, GDS, immediate and clock sources, and
destinations outside the context registers, are still rejected.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RADV sets PARTIAL_TG_EN in the dispatch initiator for an unaligned direct
dispatch, with each axis's last-group size in the upper half of
COMPUTE_NUM_THREAD_X/Y/Z, and TUNNEL_ENABLE on every GFX10 dispatch. The
validator rejected PARTIAL_TG_EN, and DecodeComputeStageInfo read the
whole register, so a partial size would have widened the workgroup.
A direct dispatch with PARTIAL_TG_EN now runs (groups - 1) * full + last
threads on each axis through the dispatch-size limit USE_THREAD_DIMENSIONS
already uses, or plain groups when every last group is complete; a zero
or oversized last group throws. The workgroup size is NUM_THREAD_FULL.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The builder threw on a null indirect base, while the PM4 executor accepts
SET_BASE with address 0 (it only requires the alignment and address bits) and
an indirect draw that later reads the base is checked there. Titles clear the
base with 0, as with sceAgcDcbSetIndexBuffer, which already accepts 0 to
unbind. A non-null base is still checked for 8-byte alignment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A recorded draw counted as reading and writing any guest memory when its
resources needed completion work, which a fault buffer alone (guarded
descriptor slots, rect lists) also needs. Such a draw reaches no guest
memory through it: the buffer is the draw's own, written only on a fault.
Its pass hazards now come from its known ranges and images like any other
draw; address-based draws, draws through the BDA table and draws with
copied writes still count as touching any guest memory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Port FreeBSD 11 lib/libc/stdlib/random.c (freebsd-src 4ab2e064): the
additive feedback generator with the default 128-byte TYPE_3 table,
good_rand seeding, state sizes rounded down to 8, 32, 64, 128 or 256
bytes, setstate returning the previous state, and initstate returning
null below 8 bytes without setting errno. Calls are serialised by one
lock; a null state throws.
The import audit of the homebrew catalog on main 262c0316 lists them as
absent libSceLibcInternal imports:
- random: PPSA74126 Stremio Plus, PPSA85300 VLC, PPSA99169 RetroArch,
PPSA99177 PS5VR, PPSA99420 PS5MC, PPSA99505 Jelly5, PPSA99515 Emby5,
PPSA99810 PSBox Cloud Gaming
- srandom: PPSA85300, PPSA99169, PPSA99420, PPSA99810
- initstate, setstate: PPSA99177, PPSA99420, PPSA99505, PPSA99515
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An 8_8 target with SWAP_ALT keeps the x export in its first channel and
the alpha export in its second, and the color block blends that second
channel with the alpha equation. Blending into such a target threw.
With one equation for color and alpha the draw blends as before. With a
separate alpha equation the pipeline draws twice: the first pass writes
the first channel with the color equation, the second writes the second
channel with the alpha factors and operation moved into its color
equation. The split is refused, by throwing, for draws that write depth
or stencil, use per-face depth bias, or whose pixel shader writes memory,
since those would repeat; factors of the destination alpha throw since
the target keeps it in its second channel. Pipeline libraries are not
used for split pipelines.
A plain image_sample that says DIM 2D on a 1D T# takes its texel and its LOD
from the first coordinate alone, measured on RDNA2 (gfx1036) with the
hardware oracle. Astro's Playroom draws with such a pair.
The expected rows were measured with Vulkan alpha-to-coverage on a Radeon 660M (gfx1035) under RADV_DEBUG=noatocdithering, which programs DB_ALPHA_TO_MASK 0xaa01: an alpha of at least one half, 2 and +inf keep the pixel; below one half, -0, negatives, -inf and NaN drop it.
Covers code, stack and an anonymous page, and an unmapped address. On Linux the test runs again with APS5_NO_PROCMAP_QUERY so the line scan stays covered.
DB_ALPHA_TO_MASK 0xaa01, the enable bit with the four offsets at 2 and no OFFSET_ROUND, reaches the pixel stage, which keeps a pixel when its MRT0 alpha is at least one half. Dithered or rounded settings, and an MRT0 that is missing, swizzled, packed or exported without a floating-point alpha, still throw. God of War Sons of Sparta (PPSA28997) stopped on it a few minutes into play.
It carried its own copy of the /proc/self/maps line scan and is called on the same Unity stack walks as sceKernelGetModuleInfoForUnwind. It now uses the shared HostMapping lookup.
A pixel shader whose stage info asks for alpha-to-coverage declares SampleMask, starts with every sample covered and, at its MRT0 export, clears the mask when the alpha is below one half or NaN, running with late fragment tests. The shader keeps running, as a zero coverage mask does on the GPU. The flag is part of the static shader identity and of the request serialization, now version 16.
On Linux every call opened /proc/self/maps and scanned it line by line with sscanf. Unity titles call it on each stack walk, and with about 900 mappings God of War Sons of Sparta (PPSA28997) spent most of its main thread there. The covering mapping is now asked of the kernel with PROCMAP_QUERY (Linux 6.11), on one descriptor opened once, as GuestMemory does; older kernels and APS5_NO_PROCMAP_QUERY keep the line scan.
AnyPS5's libc heap registered every block with GPU access, so every
address-based build imported or mirrored each malloc block the title had
made. The console's libc.prx maps its heap with protection 3 (CPU read
and write, no GPU bits), so the GPU never sees it there either.
Heap blocks are now registered without GPU access; reallocating keeps the
block's access. The AvPlayer fallback for a title that passes no texture
allocator stands in for that allocator, which hands out memory the GPU
reads, so it allocates through a new GuestHeapAlignForGpu.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A title calls our exports on stacks it sized for the console. With lazy
binding the first call through a PLT slot runs the dynamic linker's symbol
lookup on that stack: on the 2 KiB fiber context Astro's Playroom uses it
wrote below the context into the neighbouring heap chunks.
The import probe only checked whether a GPU read of an imported range
reports its pages as written. lavapipe runs command buffers on CPU
threads, so its writes into an import go through the page tables and
are collected as CPU stores. In agc_unit_shadow the whole publish's
copy into the import is then collected as a CPU store over unit 3, and
the next partial publish drops the slab's newer bytes as stale: "the
partial publish did not copy exactly the partly covered unit".
The probe now also fills the scratch range on the GPU and collects
again. When the read or the fill reports pages as written, host and
dma-buf imports are compared. lavapipe reports 0 of 4 pages after the
read and 4 of 4 after the fill; NVIDIA reports 0 after the fill.
The probe change is the first commit of #1046.
The title id now comes from GetAppTitleId, so param.json has one parser and
its validation (a missing file or titleId throws). Another pid returns EPERM
and a null info returns 0 without writing, as shadPS4 does. app_id stays 1,
the running title's id in libSceSystemService, which is inferred, not
measured.
guest_app_info runs the call without param.json in a child process and
checks that the field between app_id and the title id is left alone.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceKernelGetAppInfo fills the app id and the title id of the calling
process: app id 1, the running title's id in libSceSystemService, and the
titleId of /app0/sce_sys/param.json at offset 0x10, up to 9 characters and a
terminator. Only those fields are written, since callers declare the
structure with different sizes. Another pid returns ESRCH and a null info
EFAULT.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The AMM ranges were reserved address-less on first use, so they took the
lowest free address of the arena, directly after the title's last mapping,
where the title's next hinted maps go. They are now reserved at a fixed
place, the last 64 GiB of the guest arena, which maps without MAP_FIXED and
the hints derived from them do not reach; a reservation that cannot be made
there throws.
Fixes#3146
Split at the first delimiter like FreeBSD's strsep: empty fields are
returned, and the cursor becomes null after the last token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Share rand's generator step with rand_r, which advances the caller's
state instead of the global one, as FreeBSD's rand and rand_r share
do_rand.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on a PS5 (firmware 13.60):
- sceRudpInit rejects a null pool or a size of 0 or less with
INVALID_ARGUMENT, and a pool under 0xC58 bytes or not 8-byte aligned
with MEMORY (0x80770007).
- sceRudpGetStatus rejects a null pointer or a byte count of 0 or above
0xF8 with INVALID_ARGUMENT and reports the live context count at 0x64.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Export sinh, cosh, coshf, asinh, asinhf, acosh, acoshf and atanh,
forwarding to the C++ standard library like tanh and tanhf.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
InitialContextRegisters built the clear-state context registers on every
call: about 200 emplace calls from its list and loops, with the register
vectors growing several times on the way to offset 0x3b8. Each
QueueState starts from it, so it ran for every CLEAR_STATE, and
ResolveShaderAbi and ResolveGraphicsStagesAbi construct a QueueState on
each call, which sceAgcLinkShaders, sceAgcCreatePrimState and
sceAgcCreateInterpolantMapping make while a title draws.
The registers are now built once into a table that is never destroyed,
so a queue cleared while the process exits still finds it, and each call
returns a copy of the table.
A call takes 71 ns instead of 746 ns, and constructing a QueueState 885
ns instead of 1378 ns (200000 calls each, GCC 13 -O2). On the title
screen of PPSA21402 (Balatro) the two functions took 44 to 47 of about
2300 CPU samples per 10 s and no longer appear in the profile.
sceNgs2ParseWaveformFile read everything from the offset to the end of
the file into a zero-filled buffer and parsed that, although parsing
uses only the RIFF chunk headers and the fmt, fact and smpl payloads.
A waveform at an offset in a sound bank cost a read of the rest of the
bank on each call.
ReadChunks now takes the bytes through an accessor. Waveform data passes
its buffer; a waveform file seeks to and reads the headers and those
payloads alone, with the same checks against the length from the offset
to the end of the file. The format dispatch both use moves to
ParseWaveform.
ngs2_parse adds a bank of two waveforms in one file, the second with a
chunk after its samples, and parses each at its offset.
PPSA21402 (Balatro) parses its five 33 MB music tracks at start: one
call took 8.3 to 26.7 ms and allocated 33 MB, and now takes 0.01 to
0.04 ms with the same result.
An unregistered graphics program is read raw only while the shader registry
is empty; with shaders registered it throws, as before. A draw that uses a
raw program is noted, and RegisterShader throws once that has happened,
since a title that registers shaders after drawing unregistered programs is
not understood. The draw cache revalidates a raw program by reading it raw.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The POSIX alias takes (attr, int), returns EINVAL (22) for a null or
uninitialized attribute like the other pthread_attr setters, and
stores the value through scePthreadAttrSetsolosched, returning its
result in errno form. The signature and the stored int follow the SCE
twin in KytyPS5 and sharpemu; PS5PCEM implements the alias the same
way. The value is not applied to scheduling, recorded in TechnicalDebt.
Returnal (PPSA01285 v03.000.005) stops on it after eager binding
(#2872, #1155):
what(): pthread_attr_setsolosched_np_nid_postfix not implemented
LEGO Horizon Adventures (PPSA14632, #734) and Palworld (PPSA23126,
#2598) import it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The register draw key identifies an unregistered program only by its
address, and a key hit reuses the entry's decode without reading the
program again. A different program uploaded to the same address with
the same registers would run the old compiled shader. A hit now checks
the decode's unregistered programs against guest memory and drops the
entry when one changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Use the upstream CMASK implementation and remove the import requirement from image clears. Propagate storage-image creation failures. Test repeated clears, copied write-back, and failure without changing guest data.
AI-assisted: yes.
guest_filesystem creates symbolic links for the Windows lstat checks.
Without administrator rights or Developer Mode, CreateSymbolicLinkW
fails with ERROR_PRIVILEGE_NOT_HELD even with
SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, and the test aborted there
(reported as a stale guest errno). Skip those checks with a note in that
case; any other failure to create the first link still fails the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#2839 decides f32 product tininess after rounding, in all four f32
round modes, from the exponents and a UMulHi and IMul of the
significands, and builds the flushed value from them. To nearest even,
the mode of shaders without a known float mode and of the 0xc0 most
titles use, one multiply decides it: one factor times 2^64, which is
exact below 2^64, times the other factor rounds the exact product once
to 24 bits in the normal range, and 0 < |that| < 2^-62 is tininess
after rounding. A product that rounds up to 2^-126 is then not tiny and
keeps the emitted product, which is 2^-126 there, so nothing has to be
built from the significands.
The flush of v_mul_f32 v5, v6, v7 takes 12 IR instructions instead of
34, counted as the instructions the flush select needs beyond the
product. vector_constant_factor_tininess pins 12 to nearest and 36 for
the directed path of the same instruction, and checks that the
directed modes and v_mad_legacy_f32 (before rounding) keep #2839's
check. Its round-to-nearest branches are removed as unreachable.
The f32 denormals entry says which rounding the check inherits: the
multiply is a plain OpFMul like the product, with no f32 rounding mode
requested, so it relies on the driver rounding f32 to nearest even, as
the products do. F32ConstantFactorTininess names the row a check before
rounding flushes, 0x3f7ffffe * 0x00800001, which keeps the emitted
0x00800000.
Against #2839 to nearest even, a model of both checks agrees on
1,074,977,546 operand pairs (every 24-bit significand at both rounding
thresholds, random pairs, specials) with each other and with an exact
reference. On an RTX 5070 the outputs of #2839's own tininess and
mixed-source shaders are bit-identical with either lowering over
165,298 and 164,008 generated rows in all six float modes tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A wait on an equeue that holds socket events now blocks in poll/WSAPoll on
those sockets plus a per-thread loopback wake socket, instead of rechecking
every millisecond. Adding, triggering or deleting an event and closing the
equeue wake the blocked waiters. Closing a socket removes its read and write
events, as on FreeBSD. A read event also reports EV_EOF once the peer has
shut down its sending side.
sceKernelAddReadEvent/DeleteReadEvent and sceKernelAddWriteEvent/
DeleteWriteEvent register EVFILT_READ and EVFILT_WRITE events on guest
sockets. The equeue polls the host socket for readiness on each wait
and reports the readable byte count or the send buffer size in data,
with EV_EOF on hang-up or error.
pthread_cond_wait and pthread_cond_timedwait, and their sce counterparts, threw a
host exception when the mutex was unlocked, locked by another thread, or not yet
initialized, and dereferenced the adaptive static initializer. FreeBSD returns
EPERM for these (EINVAL for a destroyed mutex) before touching the condition.
(cherry picked from commit a78bb8a855ca3b277f128c6a9e52ff3949b9cb80)
The elected lane's inner spin loop never ended on Mesa ANV with the LDS in
a storage buffer. It now tries once and the outer loop elects again; the
update is unchanged.
(cherry picked from commit e894f9092326a849ebe1ce46ff0ed3b25fbb7e9b)
sceVideoOutVrrPegToFixedRate and sceVideoOutVrrUnpegFromFixedRate were
NotImplemented without arguments. Both take the output handle; the peg
also takes two further arguments whose meaning is unknown. VRR is not
modelled and the output always runs at a fixed rate, so both validate
the handle and return 0; non-zero peg arguments throw. Recorded in
TechnicalDebt.
Uncharted: Legacy of Thieves Collection (PPSA05684) stops on main with
"sceVideoOutVrrUnpegFromFixedRate not implemented"; it passes its
output handle to both (and zeros to the peg) and ignores the results.
Since #2133, a 3D image_sample_lz or image_load bound to a 2D-array T#
decodes as 2D and matches the new 2D mode, so it reads layer 0 for any
r and returns zero for z != 0. adriadam10 measured on RDNA2 (gfx1035)
that dim 3D on a 2D-array T# behaves exactly like dim 2D-array, with r
or z as the layer. Decode a 2D-array T# bound to a 3D access as 2D-array
and add a 2D-array mode next to the 2D one.
The same measurement covers image_load_mip on both T# types and
image_get_resinfo, which returns (w, h, 1, levels) on a 2D T# and
(w, h, layers, levels) on a 2D-array T#. Allow those accesses and return
1 as the third resinfo component through the 2D mode. image_get_lod is
not measured and keeps throwing.
Pipeline destroyed its shader modules right after LinkPipelineFromLibraries returned, while the pre-rasterization and fragment shader library parts built from them stay cached and are relinked later with VK_PIPELINE_CREATE_LINK_TIME_OPTIMIZATION_BIT_EXT. On AMD's Windows driver (RX 6700 XT, Adrenalin 25.10.45.05) that link faults inside amdvlk64.dll at varying offsets, reading freed heap memory, in 22 agc tests on a cold driver cache; all of them pass with APS5_NO_PIPELINE_LTO=1. Running the optimized link synchronously or without a pipeline cache did not help, keeping the modules alive did.
When the optimized link is enabled, the library store takes over the modules of each newly built shader part and destroys them in ClearPipelineLibraries after the pending links have finished. Modules of a pipeline whose parts were all cached are destroyed as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Key 0x10 (GFX9_DCC_CLEAR_SINGLE in Mesa) marks a 256-byte block whose texels all equal its first texel. A CB DCC decompress pass over uniform comp-to-single keys now writes that texel over its block and stores uncompressed keys; a fast-clear eliminate over them throws.
When another readable allocation starts right after the buffer's allocation,
the bound range still ends at the first allocation: the bytes past it are
outside the buffer the descriptor's records describe, and binding into the
neighbour would send the range back to a copy on every draw.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RADV rounds a strided buffer descriptor's record count up, so stride * records
can run up to one stride past the end of the buffer's allocation. The guest
buffer region then no longer fits inside the allocation's host import and is
copied on the CPU, whole, for every draw that binds it.
When a strided descriptor ends less than one stride past the end of the
readable registered allocation that contains its base, the range is bound up
to the allocation's end. RetroArch's Dolphin core binds a 48 MiB stream buffer
with stride 20 (records 0x266667, 12 bytes past the allocation) on every draw.
With this change the buffer is bound in place from its import.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window offset register is already required to be zero, so the enable bit has no effect on a draw that gets this far.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Background registration preparations (#2583) copy the device's
SpirvTarget, whose capability and extension spans point into the
device's shader profile. Driver::stop reset the device without waiting
for the detached pool, so a preparation still queued or running read
freed extension names and agc_driver crashed at exit on RADV.
Driver::stop now closes the pool after joining the queue workers: it
stops queueing and waits until no task is queued or running. A
preparation submitted after that runs on the caller's thread, which
holds its own reference to the device, like the existing fallback for
a pool without threads.
Fixes#2891
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The driver's Log2 has an absolute error that does not shrink with the
result, so near 1.0 the relative error explodes: on an RTX 3080, v_log_f32
of 1 - 2^-24 is 23,947,836 ULP off. Measured on RDNA2 (gfx1036), the
hardware's log2 is within 1 ULP of the correctly rounded value, and an
eight-term series in x - 1 is within 2 ULP of both for |x - 1| <= 1/8.
The lowering keeps the driver's Log2 and takes the series only inside that
band when the two differ by more than 4 ULP, so RADV's native result stays
bit-exact and NVIDIA's is within 6 ULP inside the band.
agc_driver_log_near_one pins 64 inputs per type from the oracle with
per-lane tolerances.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GLfKH7Sqe51wX254k1XsV
Enable the existing input-only f32 flush policy for division scaling.
RDNA2 measurements pin all three sources and signed zeros; keep the
scaling rules unchanged and retain their separate technical debt.
Co-Authored-By: Codex gpt-6.1-sol <noreply@openai.com>
Resolves a guest path the way FreeBSD 11's realpath does: a relative
path starts from the guest working directory, "." and empty components
are skipped, ".." drops the last component and never climbs above "/",
every other component must exist, and one that is not a directory with
anything after it is ENOTDIR. Backslashes separate components as in the
rest of the path layer. The result is the guest path; the host is
consulted only for existence and type, so host symlinks are followed but
not expanded, and ".." is lexical, as in the rest of the path layer. A
null buffer is PATH_MAX bytes from the application heap, a null path is
EINVAL, an empty one ENOENT, and 1024 bytes or more in the input or the
result is ENAMETOOLONG. On failure the caller's buffer holds the path
that failed.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
Keep each registered header and its prepared shader state instead of
replacing every registration for the same code address. Resolve explicit
ABIs by header and programs by type, preserving re-registration order.
Co-Authored-By: GPT-6.1-Sol <noreply@openai.com>
The detached alarm thread waits on a condition variable that was a
namespace-scope static. At exit its destructor ran with the thread still
waiting, and glibc's pthread_cond_destroy blocks then, so a process that
called alarm never exited on Linux. The mutex, condition variable, deadline
and started flag now live in one structure allocated once and never
destroyed.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host import covered exactly one registered range, so a buffer crossing
two ranges had no import and was copied on the CPU at every use, under
the GPU mutex. Titles that map a GPU heap piece by piece (Uncharted maps
1 MiB direct-memory pieces) hit this for every buffer larger than a
piece.
An import now covers the run of address-adjacent registered ranges that
are readable with the same write and GPU access. Runs never overlap, so
imports do not either; an import is retired when any range of its run
changes, and a refused run is remembered by its base. Callers already
address imports relative to their base.
sceFontSetScriptLanguage, sceFontGetScriptLanguage,
sceFontSetTypographicDesign and sceFontGetTypographicDesign threw. With
the signatures SharpProspero declares, a font handle now keeps the
language set for each script and the feature set for each typographic
design, under the handle's lock, and returns them, with 0 for one never
set. They do not change shaping or rendering yet; TechnicalDebt records
the assumptions.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
(cherry picked from commit c4dcbe4403dc635fde2a823dec311a939816cefc)
RetroArch (PPSA99169) stops at alarm on main: `FAIL: unresolved ELF import DQXJraCc1rA from libSceLibcInternal.prx`.
alarm(seconds) arms one process-wide real-time alarm and returns the seconds left on the previous one, rounded up like FreeBSD's alarm.c (any partial second counts); alarm(0) cancels it. When it expires, SIGALRM's disposition as recorded by sigaction or signal applies: ignored, nothing happens; default, the process terminates as on FreeBSD (exit status 128 + 14); a guest handler throws, because asynchronous delivery to a guest handler is not implemented.
The timer lives in libkernel next to the signal dispositions (GuestAlarm_nid_no_patch); libSceLibcInternal, which the titles import alarm from, forwards to it.
AI-assisted (Claude Code).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The converted thread pointer was set to the TLS block plus the size rounded to max(p_align, 16), while the guest reads its first variable at tp minus the size rounded to p_align. For a PT_TLS with memsz 0x21 and p_align 8 (Resident Evil 4) that is 0x30 against 0x28, so the first variable was read from the second slot. The thread pointer now uses the size rounded to p_align; the block layout is unchanged. WIP: the test for this is still missing.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A PS4 executable (CUSA00900) keeps 37 MiB of read-only data in its
executable segment after the code. The syscall scan and the call-site
resolution decoded the whole segment linearly, reached a float table and
failed with "Unsupported 0F 79 opcode without SSE4A prefix". In
executables with a PT_SCE_DYNLIBDATA segment they now stop at the end of
the PLT that follows the last function of the unwind table. The PLT is
found by searching forward from that function for its first entry, which
pushes the GOT entry at PLTGOT + 8 and jumps through PLTGOT + 16, since
code without unwind entries can sit in between (680 bytes of start-up
code in an OpenOrbis-built sample). When the executable has PLT
relocations and no such entry follows, the relinker fails instead of
guessing. Executables without the segment are scanned as before.
(cherry picked from commit a21e8fe4c0e6c03189d559e992c0f037c076700d)
libSceCdlgG2P and libSceVrSetupDialog threw from every export. They now
run the common-dialog state machine libSceLoginDialog uses: Initialize,
Open, the status calls, GetResult, Close and Terminate follow it, the
dialog finishes at Open without showing anything, and GetResult reports
that the user cancelled, writing only the leading int32 result. The
signatures are assumed from the other common dialogs, as TechnicalDebt
records. 13 exports move from stubs to implemented.
Claude-Session: https://claude.ai/code/session_012SLQbhAiexxW6TtjYToW8a
(cherry picked from commit 07992adf80bae9b2d7da4b751cd21c040fa1c5b8)
A flat_atomic_* whose address lands in the LDS aperture updated the dword without the lock, so it could lose updates against a ds_add_u64 or a 64-bit flat atomic on the same dwords. It takes the lock like the 32-bit ds atomics when the program has a 64-bit LDS atomic; programs without one emit the same SPIR-V as before.
On RADV the race between ds_add_u64 under the workgroup lock and a
concurrent ds_add_u32 only shows when the lanes get their operands from
per-lane buffer loads: with v_mov_b32 constants or loads from one address
no increment is lost there. The new case has the split-lane kernel's
parity split and loads each lane's (0xffffffff, 0, 1, 0) row from the input
buffer at tid * 16 with buffer_load_dwordx4, so the input buffer now holds
256 rows. With SpirvMemoryInstructions.cpp reverted it fails on RADV and
on the RTX 3080; with the change it passes on both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
Even lanes run ds_add_u64 and odd lanes run ds_add_u32 on the slot's high
dword, one pass per dispatch, as in the reported kernel, and the dispatch
is repeated 32 times from one compiled shader.
With ANYPS5_ENABLE_SPIRV_TOOLS=ON the scalar loop around the ds_add_u64 lock loop made
SPIRV-Tools' block merge pass merge the loop header with its multi-block continue
target, and validation after optimization rejected the module. The 16 iterations are
now emitted inline; the expected sums and the divergent exec masks are unchanged.
The hardware serialises 32-bit and 64-bit LDS atomics on one address; the lock that makes the 64-bit forms atomic did not order 32-bit atomics, which lost increments between the two halves of a 64-bit update (#2297).
GuestMemory::WriteChanged stores only the bytes the GPU changed, so that CPU writes to the bytes
around them survive. It found them a byte at a time inside every changed 256-byte block and
stored each run with memcpy. Where changed and unchanged bytes interleave (the low bytes of
floats), every run is a few bytes long, and that loop took 43% of the GPU submission thread in
Disney Epic Mickey: Rebrushed's first level (0.9-1.0 ms per MiB).
Each 16-byte chunk is now compared with SSE2. A chunk with no change is skipped, a chunk that
changed entirely is stored whole, and a mixed one is stored with MASKMOVDQU, which writes only the
bytes selected by the mask. An SFENCE after the loop orders those non-temporal stores before the
stores that follow. Bytes the GPU did not change are still never written.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0134RWCyiyydJBbfKz8g9Bwe
An import whose module matches no needed library has no library name,
and the Windows startup stub counted it against every library, so a
missing library with nothing imported from it was still never skipped.
Every such import in the executables of Record of Lodoss War, Resident
Evil 4, PRAGMATA and Returnal declares a module that exists under
another file name (libSceJson for libSceJson2.prx, for example), and
none declares libSceXml or Party.
The relinker now keeps the declared module names of those imports, for
the executable and each guest module, and the stub counts a host
library as imported from when its name up to the first dot matches one
of them, ignoring case. A module declared as libGuest still keeps a
missing libGuest.xyz required, as guest_module_identity checks.
Imports without a module ID declare nothing and count against no
library. The rule and the title data are from xXPBSXx.
The Windows startup stub stopped at the first DT_NEEDED library it could
not load, even when no import is searched in that library. Titles list
libraries they import nothing from (Road of Death needs libSceXml.prx,
Resident Evil 4 needs Party.prx), so they stopped with GetLastError 126
before running. A host library that no import's search list contains is
now skipped with a message when it is not found; its handle stays null
and is never read. Missing libraries that something imports from, other
load errors and guest modules still stop the start.
AddWaveformBlocks rejects a null data pointer before reaching the silence
path, so TestSilenceBlock aborted and its dataless rejection passed for the
wrong reason.
PPSA11386 registers 3840x2160 tiled display buffers in pixel format
0xC001000622000000, R16G16B16A16 float in KytyPS5 and SharpProspero
(0xC001000600000000 is the blue-first variant), and stops on "VideoOut:
unsupported display pixel format". The buffer is eight bytes per texel; its
tiled layout is assumed to be the 8-byte SW_64KB_R_X equation applied to
full coordinates, as addrlib applies it. The values are taken as linear
light and encoded with the sRGB curve for the 8-bit output. The encoding
compares the raw half-float bits against two tables of 255 thresholds
(color and alpha) that the CPU decode and the color transfer shader share,
so both give the same bytes on any driver; negative and NaN values give 0.
Guest-memory detile, the resident image path and the CPU decode all handle
both formats; DCC metadata on a float buffer throws. Neither the layout nor
the transfer is verified against console frames.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fnrk5Dazjb2tLooVSpdRQq
A waveform block that declares samples with no data threw as not
fitting them. PPSA04867 adds such a block with flags 0x11 (data_size 0,
num_samples 16385) and terminates the process on the throw, followed by
a fully specified block under the same flags, which takes the ordinary
path. Accept flag 0x10, named SILENCE here: a dataless block under it
renders num_samples frames of silence and finishes like a complete
block, reporting its bytes through decoded_data_size. A dataless block
without the flag keeps throwing. No public reference names the flag;
the assumption is recorded in TechnicalDebt.
Co-Authored-By: Claude Code <noreply@anthropic.com>
The CFG used by unused-filter=1 followed only direct branches, so code reached
through lea, relative-relocated pointers or jump tables was unreachable and its
imports were dropped. It now roots RIP-relative lea targets, relative relocation
targets and relative jump tables, and counts any RIP-relative reference to a GOT
slot as an access. Address-taken targets are walked after the direct control
flow, and a target that does not decode as an instruction is not code.
Comments are reserved for technical debt (docs/dev/TechnicalDebt.md), so
the ones the previous commit added to the recorder, the snapshot keep and
the tests go.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZHkb8kUwnHAuBBbHywvJf
A ShaderResources from the resource caches is the same object for every
draw or dispatch that uses it, and Recorder::ReusableDrawSnapshot hands
the next draw the same snapshot buffer, so passing their bytes with each
Keep counted one allocation once per use and submitted the batch before
it held the budget. Recorder::KeepBytes counts an owner's bytes once per
batch, and the dispatch, draw and snapshot keeps use it.
The recorded kept bytes test runs a dispatch and a draw that copy a
guest buffer and two draws that snapshot one on the device, and fails
when any of the three sites stops counting or a reused snapshot counts
twice. The BdaResources check pins the copied region it sets up.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZHkb8kUwnHAuBBbHywvJf
The recorder bounds the bytes a batch keeps alive (KeptBytesBudget) and
waits for older batches past twice that. A recorded dispatch or draw kept
its ShaderResources, and a draw its snapshot buffers, with a size of 0, so
the host-visible copies of guest buffers they own never counted. A title
that binds a 124 MiB writable guest buffer in every draw then held about
ninety copies, 11 GB, until the GPU caught up, and aborted with
VK_ERROR_OUT_OF_HOST_MEMORY from vkAllocateMemory.
GuestBufferMemory reports the bytes of its copied regions, and the
dispatch and draw keeps and the draw snapshots pass their bytes to
Recorder::Keep, so the existing bound applies to them.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The lookback classifies preceding instructions at their decoded opcode
offset, the same place the forbidden-instruction check looks since #1935,
so a legacy-prefixed mov rax, imm or mov r10, rcx is recognized. The REX
byte stays part of the patterns, so mov r8 and mov r8d cannot masquerade
as rax loads. Anything unrecognized still stops the inference.
Adds prefixed fixtures to both test suites: a prefixed constant stub that
must report syscall number 9, and a prefixed clobbered stub that must
report no number.
AI-assisted: yes. The submitting author is responsible for every changed line.
The syscall check names only the code offset, so a title that links a FreeBSD
libc (the ps5-payload-dev SDK statically links one into every homebrew binary)
stops with no indication of which call it reached. Those stubs load the number
with a constant before the instruction (mov rax, 9 for link), so decode the
nearest constant load into rax and add it to the message:
Forbidden syscall instruction at code offset 0xa: syscall number 9
The number is reported only when it can be established. The constant load must
be the instruction directly before the syscall, or one mov r10, rcx (the SDK's
first-argument move, which does not write rax) in front of it; any other
instruction stops the inference instead of returning a stale value, so
mov rax, 9; add rax, 1; syscall reports no number rather than 9. Only the three
invocation opcodes report a number: sysret is a return and keeps the current
message, as does a stub that loads rax from a register, like the SDK's generic
syscall() wrapper. The check still throws; only the text changes.
Tested: relinker-only build, full ctest on Linux x86-64 (45/45). The C++
syscall_scanner test covers the opcode matrix and the review regression; the
Python test_syscall_number covers the same contract through the harness the
relinker lane already runs on Ubuntu, Windows and macOS, where the C++ fixture
helpers (sys/mman.h, posix_spawn) do not build.
AI-assisted: yes
The registry calls an observer on the mapping thread, after the registry
lock is released, with the ranges a kernel mapping gave GPU access:
direct and flexible maps with it, and fixed remaps with it over a
registered range (a reservation). The mutation looks them up while it
still holds the lock and hands them over weakly, with the registry
generation it ended at, so the observer does not keep a range alive.
The driver imports those ranges on its current device, so an
address-based build finds the import instead of pinning the memory on
the queue that first uses it. A range that gains GPU access through
sceKernelMprotect, and AnyPS5's own libc heap, are still imported on
first use.
The import at map does not reconcile the other imports with the
registry, so its cost does not grow with the number of imports. It skips
a range that is gone, and any range when the imports were reconciled
with a newer registry since; the next lookup imports those as before.
It installs the pin waiter as the other import paths do, so unmapping a
range imported at map retires its import. The live import bytes are kept as a
running total instead of being summed over every import. Once the import
table belongs to the current device the import takes only the table's
lock; the first import on a device (which runs the write-watch probe)
and a device that was just replaced go through the GPU lock. Clearing
the observer waits for calls already running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Linux every executable or guest-module import whose NID a guest
module exports was renamed to the guest-suffixed name, whatever module
the import declares. A title that bundles a module exporting system
NIDs then had those imports bound to it: the libc.prx loader shim of
ps5-native-app-boilerplate exports placeholder functions under the
libSceLibcInternal NIDs, so DOOM PPSA99666's snprintf, vsnprintf,
memcpy and malloc (declared from libSceLibcInternal) returned 0 and the
title never found its WAD (#1497).
An import now binds to a guest export only when the module it declares
is a guest module, as on Windows. Imports without import-module
metadata keep the previous behaviour, and a title's own libc.prx
imported from libc still binds to it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ProsperoRadio (PPSA99001) imports six sceOpusCeltDec functions from
libSceOpusCeltDec and fails to load because no such module exists. It
opens this decoder only as a fallback when libSceOpusDec reports a
retryable error for a CELT packet, which libSceOpusDec here never
returns, so the module is exported without an implementation and each
function throws if it is called, as for sceSaveDataCancel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on RDNA2 (gfx1035) in review:
- the shared and private apertures are 4 GB windows (the gfx9+ layout of kfd_flat_memory.c), so a flat
address routes by its whole high dword, 0x80000000 or 0x70000000, not by its top 16 bits;
- a dword or short access whose last byte is past the end of LDS reads 0 or writes nothing as a whole;
multi-dword accesses are checked per dword.
The aperture test gets rows that straddle and pass the end of LDS; the TechnicalDebt line names the
public source of the layout and keeps only the PS5's base values as inferred.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main's StoreAddressU32x2..x4 (one BDA lookup per wide store, b95842597) bypassed the aperture routing:
a flat_store_dwordx2 into the shared aperture went to BDA at 0x8000000000000000 and LDS kept its old
value. The wide store now stores each component through the aperture like the 32-bit store, and keeps
the single BDA lookup outside the apertures.
The aperture test's V# word 3 is 0x31016fac (OOB_SELECT 3, raw), as in main's other execution tests:
with OOB_SELECT 0 its output buffer read as out of range after #639, which was the failure reported on
four drivers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sub-dword stores and unaligned dword accesses go byte-wise; 32- and 64-bit FLAT atomics
route to LDS (64-bit under the workgroup lock), scratch (read-modify-write) or BDA.
agc_driver_flat_apertures covers them in 121 rows, with mixed-address waves and wave64.
The aperture bases and the unmodelled operands 235-238 are listed as inferred.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A compute FLAT access whose address has the console's shared aperture in its high dword (0x8000xxxx)
reads or writes the workgroup's LDS at its low dword, and one in the private aperture (0x7000xxxx)
the lane's scratch; other addresses stay guest memory through BDA. GTA V's ray traversal keeps its
stack in LDS and spills it to scratch through such addresses; treated as guest memory the pushes were
lost and the traversal never ended (device lost). An execution test round-trips both apertures.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
v_cvt_f32_i32 and v_cvt_f32_u32 used the host Convert which always
rounds to nearest even. When FLOAT_MODE bits 1:0 are directed, build
the f32 bits from the integer magnitude so discarded low bits follow
round toward +inf, -inf or zero, as measured on RDNA2 (gfx1036).
Co-Authored-By: Composer <noreply@cursor.com>
Twenty-five of the ninety-seven errno messages differed from FreeBSD releng/11.4 lib/libc/gen/errlst.c (for example errno 0, 30, 47, 62, 70 and 84).
(cherry picked from commit ce14c9ad7dc639335ff72b60122bae2bd0fee302)
Directed FLOAT_MODE modes for v_cvt_f16_f32, v_cvt_f16_i16,
v_cvt_f16_u16 and v_ldexp_f16 were ignored, so results stayed
round-to-nearest-even. Hardware uses FLOAT_MODE bits 3:2 (round16);
the directed path runs only when f16 denormals are preserved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
v_interp_p1_f32_e64, v_interp_p2_f32_e64 and v_interp_mov_f32_e64
(VOP3Interp opcodes 0x200, 0x201 and 0x202) reach the decoder as VOP3
and were refused as unimplemented VOP3 opcodes. Decode them into the
existing VInterpP1F32, VInterpP2F32 and VInterpMovF32 opcodes with the
VOP3Interp operand layout, so the interpolation translation already
used by the VINTRP encodings handles them.
Encodings and operand layout follow LLVM's gfx10 MC tests for
VOP3Interp_Real_gfx10 (D69348). The interpolation parameter is the low
six bits of src0, the channel bits 6:7, the coordinate (or the
interpolation parameter of the mov form) is src1, and clamp and omod
reach the destination. op_sel, the packed-half selector, a third source
and abs/neg on operands that do not exist throw.
The mesh stage was compiled at the driver's default subgroup width, while the translated mesh program lays wave64 lanes out on, and ballots over, a 32-lane host subgroup. Intel's Windows driver (Arrow Lake, 32.0.101.8991) reports subgroupSize 32 but prefers 16 for mesh shaders, and every agc_driver_mesh draw drew no triangle there. The mesh stage now gets VkPipelineShaderStageRequiredSubgroupSizeCreateInfo{32} when the program's hostSubgroupSize is 32 and the device lists the mesh stage in requiredSubgroupSizeStages, as compute pipelines already do.
Rosetta runs AVX2, FMA, BMI1/2, F16C and MOVBE, but, unlike an Intel
host, it has neither RDSEED, CLWB nor RDPID. --to-rosetta converts
everything --to-intel does and lowers these three as well:
- RDSEED becomes RDRAND (the /6 form of the same opcode), which also
returns a random value and sets CF.
- CLWB only writes a cache line back, so it becomes a NOP of the same
length.
- RDPID becomes a load of 0 through an optional stub: Rosetta's TSC_AUX
is a constant, not a processor index, so every thread reports
processor 0. A site with no movable instruction after it for the jump
is kept.
The flag implies --to-intel and conflicts with --windows. It is
independent of --macos: it only changes what the conversion produces, so
--macos alone leaves these instructions as they are, as on an Intel Mac,
and a title for Rosetta is relinked with --macos --to-rosetta. Intel
output is unchanged: the target is a parameter of the converter, Intel by
default.
Scalar f16 add/mul/sub ignore FLOAT_MODE denorm16 flush bits on main.
Flush inputs when bit 0 is clear and results when bit 1 is clear at
nearest-even. Multiply checks tininess after 11-bit rounding
(0x387ff000); add/sub use 0x38800000.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The relinker reads its arguments as narrow strings, so on a Windows
system whose ANSI code page cannot represent the directory it cannot
open the input, and the windows_working_directory case failed before
reaching the executable. Convert the case under an ASCII name and rename
the directory before starting the executable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The --windows-diagnostics walker looked up modules and directories with
GetModuleHandleA, LoadLibraryExA, GetFileAttributesA,
GetModuleFileNameA and GetSystemDirectoryA, so under a directory outside
the ANSI code page it failed with "dependency diagnostics Windows API
failed" instead of naming the missing dependency.
Keep the walker's paths in UTF-8 and convert them at the Windows API
calls, which now use the wide functions. The entry stub passes it, and
prints, its paths as UTF-8 instead of the ANSI code page. The dependency
diagnostics test now builds its fixtures under a non-ASCII directory.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Windows entry stub read its path with GetModuleFileNameA, which
turns every character the ANSI code page cannot represent into '?', and
then used it with SetCurrentDirectoryA and LoadLibraryExA. A converted
executable in such a directory (Japanese on a Western-locale system, an
emoji, a script without an ANSI code page) stopped at start-up with
ERROR_INVALID_NAME.
Read the path with GetModuleFileNameW and enter the directory and load
the libraries with SetCurrentDirectoryW and LoadLibraryExW, with library
paths stored as UTF-16. Messages and the --windows-diagnostics walker
still get the paths in the ANSI code page, as recorded in TechnicalDebt.
Fixes#2640
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep WAIT_REG_MEM diagnostic timeouts from completing unsatisfied waits.
Register the existing gated-dispatch capture test with a 1ms diagnostic
timeout and document the missing-producer limitation.
sceSystemServiceParamGetInt always reported English (United States) as
the console language, so a title that follows the console's language,
such as Jelly5 (PPSA99505) with its Arabic text, can only show English.
ANYPS5_LANGUAGE now selects a console language number from 0 to 30
(21 is Arabic). Unset or empty keeps English (United States); any other
value throws at the language query instead of being ignored.
(cherry picked from commit 9bcf196daa01b07a22be876a7f1143149503e054)
Submit the same vertex and pixel draw twice through the driver and
require the second one to hit the draw cache, with graphics pipeline
libraries and with APS5_NO_GPL=1. With pipeline libraries the pixel
stage is cached at its fixed push slot, so a lookup at the stacked
offset misses every draw on its layout. The driver exposes a copy of
its draw-cache counters for the test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AI-assisted: yes. Return AnyPS5 without reading the native machine name, hostname environment or network configuration. Preserve FreeBSD bounded-copy behavior, guest ENAMETOOLONG, null queries and success errno. The final tree passes a complete Windows Release build and guest_hostname/guest_errors repeated five times each on main 5edae3536. No new Linux or title test is claimed.
AI-assisted: yes. Verify AnyPS5 rather than the native PC hostname. Cover buffer capacities, guard bytes, maximum size_t capacity, null queries, success errno and thread-local isolation. The old implementation fails this regression; the final fixed tree passes the complete Windows build and five repeated runs each of guest_hostname and guest_errors on main 5edae3536. Linux and title tests of this new revision are pending.
AI-assisted: yes. Moves hostname test registration to avoid #2170 overlap. The full current-main build and focused Windows tests pass; implementation and test source are unchanged.
AI-assisted: yes. Add a deterministic backend that overwrites errno on success; verify restoration on success and retained failure errors. Extend native tests for current-time errno preservation. The new regression fails on original ca87abb and passes with this fix.
AI-assisted: yes. Restore guest errno only on successful utimes forwarding and retain delegated failure errors. Register the controlled-backend regression test. Windows library build and focused tests pass; Linux rerun pending.