Source-to-Source Transformations for GPU Code Generation
(authored by agents unless marked đ§) Tag: castelnau2026source, paper
system and verification idea
This thesis presents OptiGPU, an extension of OptiTrust that derives CUDA GPU programs from a verified C-like CPU program through proof-preserving source transformations. The user proves a simple functional implementation, writes an optimization/offload script, and receives host code, a kernel, and a proof certificate. The thesis states, âRather than requiring the user to verify an already-optimized GPU kernel, OptiGPU lets the user write and verify a simple CPU program, and then apply correctness-preserving transformations to produce an optimized GPU kernel.â (Chapter 1, PDF pp. 4â5).
The starting logic is separation logic for imperative memory and concurrency. OptiGPUâs typechecker checks the proof-carrying intermediate language. The script applies loop tiling, reordering, fusion, array hoisting, GPU offload, and memory refinement. After each transformation the typechecker checks that the proof still holds. The final target is data-race-free and deadlock-free optimized GPU code, but the thesis also says its GPU operations are admitted axiomatic functions and that a formal operational semantics and soundness proof are future work (Chapter 4, PDF p. 32; Chapter 1, PDF p. 6). Thus the claimed guarantees depend on the implementation and axioms.
technique 1: proof-preserving source transformation
Use this when an optimized implementation would be much harder to verify than a clear reference program. First write the pure specification and an initial imperative C-like implementation. Add contracts, permissions, loop invariants, and ghost proof steps until the typechecker verifies it. Then write an OCaml transformation script. Apply one transformation at a time; the framework carries contracts, resources, and proof terms through the AST and rejects a rewrite that breaks the proof. If automation fails, insert âAdditional Proofsâ or another transformation as an escape hatch. The thesis explains, âOptiGPU verifies the proof still holds after every step; this is what provides the final guarantee of correctness.â (Chapter 1, PDF p. 5).
OptiTrust transformations are ordinary OCaml functions and can be composed from basic transformations. A loop-tiling transformation assumes divisibility constraints, creates nested tile loops, and transports the original invariant instead of asking the user to reprove every generated loop. Resource rewrites update permissions when variables are inlined or moved. The transformation script is itself part of the trusted workflow, so AST rewrites must be checked by the typechecker.
Inference: Verus can use the same staging pattern: verify a straightforward Rust function first, then apply source rewrites that preserve requires, ensures, loop invariants, and ownership facts. Verus does not provide OptiTrustâs AST/resource framework; this mapping requires a separate transformation tool and a Verus run after each rewrite. Do not write modifies or unsupported proof syntax; retain valid Verus contracts and proof functions as documented by VL.
technique 2: model GPU execution with transition contracts and thread-for
OptiGPU represents host/device transitions with kernel_launch, kernel_setup_end, kernel_teardown_begin, and kernel_kill. Contracts consume and produce resources such as HostCtx, KernelSetupCtx, ThreadsCtx, KernelParams, and shared-memory allowances. The setup contract requires bpg * tpb = grid_sz; the teardown contract requires all thread resources to be consumed before returning to host context (Chapter 4, PDF pp. 32â34).
The thread for construct turns nested serial loops into collectively executed thread loops. A transformation changes a loopâs mode from sequential to thread, and the framework inserts or checks barriers (blocksync) where shared-memory communication requires them. The thesis says, âOptiGPU instead treats barriers semantically as operations executed collectively by some group of threads.â (Chapter 3, PDF p. 27). For a tiled transpose, the script allocates global input/output and shared tile memory, loads a tile, executes blocksync, and stores the transposed tile. Distributed dimensions and DMINDEX expose block coordinates for reasoning and are erased or simplified during CUDA printing.
Inference: a Verus analogue would model grid/block indices as ghost state, use explicit ownership or permissions for shared buffers, and prove a barrier protocol as a separate invariant. Verusâs sequential Rust model does not automatically establish GPU deadlock freedom; a backend-specific semantics and trusted kernel contract would be required.
technique 3: refine memory and generate CUDA
Start with generic memory operations in the CPU-like language. Use a conversion transformation to change allocations and accesses to global memory, shared memory, or thread-register memory. For transpose, create device copies, hoist tile allocation into kernel setup, map loads/stores to __gmem_get/__gmem_set, and erase distributed dimensions when printing CUDA. The thesis notes, âIf those operations appeared in the correct context to begin withâfor example, the allocation for tile appearing in the setup portion of the kernel launchâthen no extra proof steps are needed.â (Chapter 3, PDF p. 30).
CUDA generation lowers transition intrinsics and GPU memory operations to CUDA built-ins and dereferences. Generated host code performs allocation, copies, launch, and teardown. The typechecker checks the intermediate proof; the printer is trusted to preserve the checked meaning. This boundary is a material assumption for VL.
evidence and limits
Evaluation uses matrix transpose and tree reduction from Nvidia cuda-samples, RTX 5060, CUDA 13.0, nvcc -O3, and 1,000 device-runtime runs. Effective bandwidth is (read bytes + written bytes) / runtime; host allocation and copy time is excluded (Chapter 7.1, PDF pp. 59â60). Table 7.4 reports transpose 297.7 GB/s OptiGPU versus 340.6 reference; reduction 297.9 versus 349.4 reference and 376.8 optimal. The thesis attributes the gaps to unoptimized indexing and lack of transformed thread-register accumulators. The denominator is 1,000 runs per kernel, not a count of programs.
Table 7.5 reports input/output lines. Transpose uses 9 C-code, 9 annotation, and 57 transformation-script lines; the fully transformed output has 359 annotation lines. Reduction uses 8+24 C-code and 14+71 annotation lines plus 143 transformation lines; output has 180 annotation lines. These are two case studies, so they do not establish general proof-effort ratios. The thesis explicitly leaves a formal semantics and soundness proof for future work. Inference: threat to VeruLaw is high for verified GPU ambitions and moderate for ordinary Rust optimization. The useful idea is proof-carrying rewrites that make agents verify a simple kernel and then preserve its contracts; the untrusted printer, admitted GPU operations, and missing semantics must be closed before treating generated CUDA as formally verified. VL should not redo the thesisâs two benchmarks except when validating a Verus backend.
techniques for the catalog
- proof-preserving rewriting â transform a verified reference AST while checking contracts and resources after every step
- GPU transition contracts â model launch, setup, teardown, threads, and shared-memory ownership as explicit resources
- thread-for lowering â convert serial loops to collective thread loops with barrier obligations
- memory refinement â convert generic allocations to global/shared/register memory through checked transformations
- proof-carrying CUDA generation â print host and device code only after the intermediate proof certificate passes Checked 2026-10-13: audit record castelnau2026source; paper-study comparison complete; gate executed; experiments not rerun
Last edited: