D883 - A dispatch's global loads are bound from the addresses its waves form

Status: decided Date: 2026-10-14

A guest dispatch's global loads with no scalar base (global_load_dword* and the sub-word loads, D878) are traced as one buffer after the dispatch's other buffers, as a primitive shader's are (D758). The buffer is the range the loads' lanes read: from the word the lowest address is in to the end of the widest read from the highest.

The addresses come from the program through the last load, run on the host for every wave of every group the dispatch runs (D858), through the translator's own semantics. Each run starts as the wave does on the hardware: the user data from s0, then the workgroup id components COMPUTE_PGM_RSRC2 enables, and in each lane the thread id components TIDIG_COMP_CNT asks for, the wave's first thread plus the lane, x fastest. A wave's execution mask holds the lanes that have a thread; the last group of a partial dispatch (D855) runs only the threads it has. Every other vector register is zero. A branch whose condition is known is followed.

The module reads the range relative to its guest base, the address's low half, which the dispatch puts in its push block after the partial-group words. A load whose lanes' addresses or mask the host cannot know is refused by name, and so is a dispatch of more waves than a dispatch runs through the host in one preparation. Amended by D950: such a dispatch runs a sample of its groups, and a load outside the range it places is an escape.

Why: Grand Theft Auto V's first compute program loads bytes from addresses each lane forms, global_load_ubyte v0, v[1:2], off. A guest dispatch writes guest memory for real, so an access it cannot place exactly is refused rather than read through the window (D746), and the program stopped there. A global load has no descriptor, so nothing but the addresses the lanes form bounds it - the reason D758 gives for a primitive shader, which holds for a dispatch in the same way.

Rejected: