The Two Contracts a GPU Fuzzer Must Satisfy
FuzzGPU's design becomes clearest when its generator is viewed together with the GPU execution model it is trying to exercise. This checkpoint connects divergence, barriers, memory interference, and completion into one judgment: a test is useful only when it both reaches the behavior the hardware should be checked against and leaves enough interpretable evidence to say what happened. That distinction lets us state exactly what FuzzGPU has solved so far—and what a differential oracle must still decide.
One test, several constraints
Imagine a generated kernel with two cooperating warps. Each warp begins with a common instruction stream, reaches a conditional branch, and divides its active threads between two paths. One path performs arithmetic; another performs a load from an address that a different warp may store to. After the paths reconverge, both warps are expected to pass through a shared barrier before continuing.
This is not a special corner case invented to make the design look complicated. It combines the ordinary sources of GPU verification difficulty: per-thread control flow inside a warp, synchronization across warps, and memory communication whose visibility may depend on timing. The value of a test like this depends on preserving all three relationships at once. If any one is broken during generation, the test may be long on paper but shallow in execution.
FuzzGPU assigns the relationships to different levels of its generator. The top-level loop decides how many work groups and warps participate, where synchronization can occur, and where memory operations from different warps should be interleaved. The per-warp loop constructs basic blocks and connects them into a control-flow graph. It can vary arithmetic and memory instructions inside those blocks, but it has to preserve the path relationships that make the blocks reachable.
At a branch, the generation stack carries the local obligation. An instruction-set simulator predicts the next active mask for each thread. If some threads take the branch and others do not, the generator records the deferred path and its reconvergence point, then emits one path before returning to the other. The resulting linearized order is not a claim that the hardware will write back in that order. It is a construction order in which active threads can eventually encounter the emitted instructions without leaving an unmodeled path behind.
At the barrier, the obligation becomes global. The planner has already created an ordered set of barrier identifiers and participant scopes. Each warp consumes the records in that order. A warp can arrive early, but it cannot release the barrier by itself; the other participants must reach the matching record. This prevents the generator from inventing a circular wait while still allowing synchronization to be part of the stress rather than something removed for convenience.
At the memory operation, the top-level loop can pause one warp and let another emit its own access. Reusing recent effective addresses increases the chance that a load and a store concern the same location. The point is not to force an arbitrary collision.