Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce qptr ("quasi-pointer") type and associated lower->analyze->lift passes. #24

Merged
merged 5 commits into from
Apr 21, 2023

Conversation

eddyb
Copy link
Contributor

@eddyb eddyb commented Mar 15, 2023

qptr is the first major SPIR-T feature/departure from SPIR-V, and represents a vision for memory/pointers:

  • name stands for "quasi-pointer" (alternatively, "quantum"), in the sense of "not quite (a pointer)"
    • (primarily chosen to distinguish it from both SPIR-V pointers and LLVM-style untyped ptrs)
  • untyped as a design principle ("behavior given by operations, not types")
    • maximally so, not even tracking the address space ("Storage Class"), unlike e.g. LLVM ptrs
    • comparable to OpenCL Generic physical pointers (but more flexible and without runtime behavior)
    • for Rust-GPU, this allows faithfully representing Rust's untyped memory semantics
      • (Rust also lacks explicit address spaces, and qptr can replace Rust-GPU's "Storage Class" inference pass)
    • for legalization/optimization, existing approaches (from IRs with true pointers) can be employed
  • takes advantage of SPIR-V's "logical"/"physical" dichotomy to maximize flexibility and minimize effort:
    • "logical pointers": strictly typed, but extremely limited dataflow (without VariablePointers)
      • as they (without VariablePointers) can't be stored to memory, returned from functions or passed through OpPhis, each "logical pointer" is strictly a static alias of some variable, with a maybe-dynamic offset
        • (calls allow taking "logical pointer" arguments, but only shallowly, and the pointers have to point to a whole variable, so it's more like such functions are "templated" over the choice of variable)
      • the limitations allow guaranteed recovery of pointee types (and "Storage Class"es) for legal usage
        • this is because both forwards (def->use) and backwards (use->def) dataflow can connect each definition to its static "tree" of uses (with e.g. qptr.offset nodes, and qptr.load/qptr.store leaves), and all such "trees" are disjoint between pointer definitions - such a "tree"-like structure can be seen reified in the qptr.usage attribute (produced by qptr::analyze)
      • illegal usage, crucially, must be legalized away to ever produce legal SPIR-V, so there is no need to ever support recreating SPIR-V pointers from anything more than the (very restricted) legal subset
    • "physical pointers": still typed, but support casts and have no dataflow restrictions
      • SPIR-V copy of LLVM's old T addrspace(AS)* pointer types (via OpenCL SPIR)
      • can be treated as untyped by erasing the pointee type, and keeping only the "Storage Class"
        • (the "Storage Class" only needs to be tracked on int2ptr casts, loads of physical pointers etc. - not implemented yet)
  • dedicated operations (QPtrOp - shown as qptr.* below)
    • (as a fallback, for other SPIR-V instructions, attributes record original pointee types required for an input, or the output - for output pointers, the SPIR-V "Storage Class" is also kept)
  • basic pass pipeline:
    • qptr::lower: SPIR-V OpTypePointer types/instructions are lowered to qptr and qptr.* ops
      • OpAccessChains on composite types become pointer arithmetic (erasing those types)
      • (long-term Rust-GPU can skip this step and emit qptr operations directly)
    • [this is where any future legalization passes would go]
    • qptr::analyze: qptr uses are analyzed to generate qptr.usage attributes
      • (by merging compatible uses, this can itself legalize some union-like uses that were illegal in the SPIR-V input of qptr::lower - Rust-GPU could use this to support many more shapes of enums)
    • qptr::lift: qptr.* ops are lifted back to SPIR-V OpTypePointer types/instructions
      • qptr.usage attributes are used to generate pointee types that support all (transitive) uses
      • (by propagating accurate "Storage Class"es, this can itself legalize some incorrect "Storage Class"es in the SPIR-V input of qptr::lower - Rust-GPU could use this to replace its "Storage Class" inference pass)

qptrs can point to either:

  • handles: one or more textures, samplers, or buffers
    • name inspired by WGSL (which sadly only uses it for textures/samplers)
    • handle arrays use qptr.handle_array_index to select a single handle (i.e. "descriptor indexing")
    • buffers are opaque handles, and use qptr.buffer_data to obtain a qptr to the memory contents
      • (comparable to using an OpImageTexelPointer in SPIR-V, on a pointer to an OpTypeImage)
      • qptr.buffer_dyn_len can query the size of dynamically-sized buffers (replacing OpArrayLength)
      • intentionally replacing SPIR-V's choice of using a mix of Block-decorated OpTypeStructs in specific "Storage Class"es (Uniform, StorageBuffer, ShaderRecordBufferKHR) to encode buffers "syntactically" (i.e. mimicking their GLSL declaration syntax)
  • memory: byte-addressable untyped memory
    • the memory is either defined in the shader (global/local vars), or obtained from handles etc.
    • pointer arithmetic uses qptr.offset and qptr.dyn_offset
      • again more untyped than LLVM (whose GEP was copied by SPIR-V into OpAccessChain)
      • to avoid separate multiplications, qptr.dyn_offset includes an immediate stride (in the extreme this could be generalized to a kind of "integer dot product", if N-dimensional arrays ever require it)
    • reading/writing typed values from/to memory uses qptr.load/qptr.store

Example (qptr passes on kajiya's assets/shaders/wrc/wrc_see_through.rgen.hlsl):

image
(type information like OpMemberName can be seen to be erased, and only used offsets are recreated)

image
(qptr.* ops can be seen as blue against the backdrop of orange spv.* ops, thanks to #21)


FIXME(@eddyb): move/copy some of this description into the library documentation (maybe a design document?)

@eddyb eddyb force-pushed the qptr branch 2 times, most recently from 2661ecd to f30dd2e Compare March 31, 2023 13:58
@eddyb eddyb self-assigned this Mar 31, 2023
eddyb added a commit that referenced this pull request Apr 21, 2023
This is an easy way to annotate the IR (anything that supports
attributes) with diagnostics that would otherwise have a hard time being
reported in a way that correlates with the definition they relate to
(without a full-on diagnostic system).

Example from an WIP version of #24:

![image](https://user-images.githubusercontent.com/77424/233684908-283697c2-f367-4c84-b0be-be8b5fbf6016.png)
@eddyb eddyb marked this pull request as ready for review April 21, 2023 16:50
@eddyb eddyb enabled auto-merge April 21, 2023 16:50
@eddyb
Copy link
Contributor Author

eddyb commented Apr 21, 2023

In the interest of releasing Rust-GPU 0.7 today, we'll try to get this merged as-is, and defer further polish.

@eddyb eddyb disabled auto-merge April 21, 2023 16:57
@eddyb eddyb marked this pull request as draft April 21, 2023 16:58
oisyn
oisyn previously approved these changes Apr 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants