SPIR-V translation of WGSL pointers and references

WGSL was updated to have two kinds of memory views: pointers and references. See https://github.com/gpuweb/gpuweb/pull/1569

In summary:

  • Reference types are never explicitly mentioned in WGSL source.
  • A use of a variable is a value of reference type corresponding to the reference memory view of the storage allocated for the variable.
  • Let-declared constants can be of pointer type, but not reference type.
  • Function parameter can be of pointer type, but not reference type.
  • A variable's store type is never a pointer type, and never a reference type.
  • The “Load Rule” allows a reference to decay to the underlying store type, by issuing a load of the value in the underlying memory.
  • For an assignment:
    • The right-hand side evaluates to a non-reference type (atomic-free plain type).
    • The left-hand side evaluates to a reference type, whose store type is the same as the result of evaluating the right hand side.
  • The address-of (unary &) operator converts a reference to a pointer.
  • The dereference (unary *) operator converts a pointer to a reference.

TODO: Passing textures and samplers to helper functions might be done by “handler value”, or by pointer-to-handle.

Writing SPIR-V from WGSL

The distinction in WGSL between reference and pointer disappears at the SPIR-V level. Both types map into pointer types in SPIR-V.

To translate a valid WGSL program to SPIR-V:

  • The dereference operator (unary *) is the identity operation.
  • The address-of operator (unary &) is the identity operation.
  • Assignment maps to OpStore.
  • The Load Rule translates to OpLoad.

Reading SPIR-V to create WGSL

The main changes to the SPIR-V reader are:

  • When translating a SPIR-V pointer expression, track whether the corresponding WGSL expression is of corresponding WGSL pointer type or correspoinding WGSL type.
  • Insert dereference (unary-*) or address-of (unary-&) operators as needed to generate valid WGSL expressions.

The choices can be made deterministic, as described below.

The SPIR-V reader only supports baseline functionality in Vulkan. Therefore we assume no VariablePointers or VariablePointersStorageBuffer capabilities. All pointers are SPIR-V logical pointers. The SPIR-V Universal Validation Rules specify where logical pointers can appear as results of instructions or operands of instructions.

Each SPIR-V pointer result expression is a logical pointer, and therefore is one of:

  • OpVariable: map to the reference type.
  • OpFunctionParameter: map to the pointer type.
  • OpCopyObject:
    • When these only have one use, then these often fold away. Otherwise, they map to a a let-declared constant.
    • Map to the pointer type.
  • OpAccessChain, OpInBoundsAccessChain:
    • This could map to either pointer or reference, and adjustments in other areas could make it work. However, we recommend mapping this to the reference type.
  • OpImageTexelPointer is not supported in WGSL. It is used to get a pointer into a storage texture, for use with atomic instructions. But image atomics is not supported in WebGPU/WGSL.

Each SPIR-V pointer operand is also a logical pointer, and is an operand to one of:

  • OpLoad Pointer operand:
    • Map to reference, inserting a dereference operator if needed.
  • OpStore Pointer operand:
    • Map to reference, inserting a dereference operator if needed.
  • OpStore Pointer operand:
  • OpAccessChain, OpInBoundsAccessChain Base operand:
    • WGSL array-access and subfield access only works on references.
    • Map to reference, inserting a dereference operator if needed.
  • OpFunctionCall function argument pointer operands
    • Function operands can't be references.
    • Map to pointer, inserting an address-of operator if needed.
  • OpAtomic instruction Pointer operand
    • These map to WGSL atomic builtins.
    • Map to pointer, inserting an address-of operator if needed.
    • Note: As of this writing, the atomic instructions are not supported by the SPIR-V reader.
  • OpCopyObject source operand
    • This could have been mapped either way, but it's easiest to map to pointer, to match the choice for OpCopyObject result type.
    • Map to pointer, inserting an address-of operator if needed.
  • OpCopyMemory, source and destination operands
    • This acts as an assignment.
    • Map both source and destination to reference, inserting dereference operators if needed.
    • Note: As of this writing, OpCopyMemory is not supported by the SPIR-V reader.
  • Extended instruction set instructions Modf and Frexp
    • These map to builtins.
    • Map the pointer operand to pointer, inserting an address-of operator if needed.