Skip to main content

GPU program lowering

Overview

GPUProgram is semantic. A backend compiler owns every decision that turns those semantics into executable work.

At a glance

QuestionAnswer
ProblemMap semantic operation types to backend-specific executable realizations.
Reads / writesResolves logical values into backend views and preserves operation resource intent.
OwnershipThe backend compiler owns its registry, capabilities, and lowering decisions.
Output contractA command graph plus immutable value, provenance, and decision reports.
Expected workVisits the bounded semantic tree and emits the selected command nodes.
ChunksLowerers must explicitly preserve or transform logical storage boundaries.
Conditions / budgetsWebGPU lowering uses command nodes; another backend may choose native graph forms.
NeighborhoodGPUProgram + bindings + capabilities → lowering registry → executable graph.
GPUProgram
GPUOperation
Composite
Conditional
Loop
|
| backend compiler
v
execution graph

Lowering registry

GPUOperationLoweringRegistry maps semantic operation types to backend implementations. Operations do not import WebGPU APIs or mutate a command graph.

A WebGPU compiler can flatten a composite because WebGPU has no child-graph primitive. A future CUDA compiler could preserve the same composite as a CUDA child graph when that improves execution.

The compiler records each choice in compilation.lowering.decisions so an inspector can answer not only what nodes were emitted? but why was this realization selected?

Backend capabilities

Lowerers receive a compact capability contract rather than inspecting backend internals:

backend
GPU-resident conditionals
native semantic loops
child executable graphs

The initial WebGPU compiler advertises GPU-resident conditional dispatch but no native loop or child-graph primitive. This is intentionally different from CUDA Graphs, which can provide native conditional WHILE nodes and executable child graphs.

Control flow

A semantic loop does not prescribe how iteration occurs:

GPULoopOperation
|
+-- WebGPU: bounded expansion + GPU-written indirect dispatch gates
|
+-- CUDA: possible native conditional WHILE graph node

Loops without runtime predicates can already lower by bounded unrolling. Runtime predicates remain explicit compiler errors until the WebGPU compiler has a concrete predicate binding plus per-node indirect-dispatch geometry. This is preferable to silently inserting CPU readback or pretending a single dispatch gate can condition an arbitrary composite.

Execution primitives

Semantic operations use registered backend lowerers. Concrete GPUProgramPrimitive objects return nodes through getCommandNodes(graph); their decision is recorded as explicit-command-nodes. Construction may allocate graph-owned scratch but cannot schedule work. There is no legacy mutation fallback.

Why record decisions?

Eventually an inspector should be able to explain a compilation such as:

PCG
loop
lowering: gpu-gated bounded sequence
reason: WebGPU has no native graph loop

SpMV
lowering: subgroup-row
reason: short-row CSR workload + subgroup support

reduction
lowering: hierarchical
reason: N exceeds single-workgroup capacity

This decision record is the beginning of the planner/compiler interface that Ploor can later own without changing GPUProgram or GPUCommandGraph.