Expand description
The Intermediate Representation shared by all frontends and backends.
The central structure of the IR, and the crate, is Module. A Module contains:
-
Functions, which have arguments, a return type, local variables, and a body, -
EntryPoints, which are specialized functions that can serve as the entry point for pipeline stages like vertex shading or fragment shading, -
Constants andGlobalVariables used byEntryPoints andFunctions, and -
Types used by the above.
The body of an EntryPoint or Function is represented using two types:
-
An
Expressionproduces a value, but has no side effects or control flow.Expressionsinclude variable references, unary and binary operators, and so on. -
A
Statementcan have side effects and structured control flow.Statements do not produce a value, other than by storing one in some designated place.Statementsinclude blocks, conditionals, and loops, but also operations that have side effects, like stores and function calls.
Statements form a tree, with pointers into the DAG of Expressions.
Restricting side effects to statements simplifies analysis and code generation.
A Naga backend can generate code to evaluate an Expression however and
whenever it pleases, as long as it is certain to observe the side effects of all
previously executed Statements.
Many Statement variants use the Block type, which is Vec<Statement>,
with optional span info, representing a series of statements executed in order. The body of an
EntryPoints or Function is a Block, and Statement has a
Block variant.
§Function Calls
Naga’s representation of function calls is unusual. Most languages treat
function calls as expressions, but because calls may have side effects, Naga
represents them as a kind of statement, Statement::Call. If the function
returns a value, a call statement designates a particular Expression::CallResult
expression to represent its return value, for use by subsequent statements and
expressions.
§Expression evaluation time
It is essential to know when an Expression should be evaluated, because its
value may depend on previous Statements’ effects. But whereas the order of
execution for a tree of Statements is apparent from its structure, it is not
so clear for Expressions, since an expression may be referred to by any number
of Statements and other Expressions.
Naga’s rules for when Expressions are evaluated are as follows:
-
Literal,Constant, andZeroValueexpressions are considered to be implicitly evaluated before execution begins. -
FunctionArgumentandLocalVariableexpressions are considered implicitly evaluated upon entry to the function to which they belong. Function arguments cannot be assigned to, andLocalVariableexpressions produce a pointer to the variable’s value (for use withLoadandStore). Neither varies while the function executes, so it suffices to consider these expressions evaluated once on entry. -
Similarly,
GlobalVariableexpressions are considered implicitly evaluated before execution begins, since their value does not change while code executes, for one of two reasons:-
Most
GlobalVariableexpressions produce a pointer to the variable’s value, for use withLoadandStore, asLocalVariableexpressions do. Although the variable’s value may change, its address does not. -
A
GlobalVariableexpression referring to a global in theAddressSpace::Handleaddress space produces the value directly, not a pointer. Such global variables hold opaque types like shaders or images, and cannot be assigned to.
-
-
A
CallResultexpression that is theresultof aStatement::Call, representing the call’s return value, is evaluated when theCallstatement is executed. -
Similarly, an
AtomicResultexpression that is theresultof anAtomicstatement, representing the result of the atomic operation, is evaluated when theAtomicstatement is executed. -
A
RayQueryProceedResultexpression, which is a boolean indicating if the ray query is finished, is evaluated when theRayQuerystatement whoseProceed::resultpoints to it is executed. -
All other expressions are evaluated when the (unique)
Statement::Emitstatement that covers them is executed.
Now, strictly speaking, not all Expression variants actually care when they’re
evaluated. For example, you can evaluate a BinaryOperator::Add expression
any time you like, as long as you give it the right operands. It’s really only a
very small set of expressions that are affected by timing:
-
Load,ImageSample, andImageLoadexpressions are influenced by stores to the variables or images they access, and must execute at the proper time relative to them. -
Derivativeexpressions are sensitive to control flow uniformity: they must not be moved out of an area of uniform control flow into a non-uniform area. -
More generally, any expression that’s used by more than one other expression or statement should probably be evaluated only once, and then stored in a variable to be cited at each point of use.
Naga tries to help back ends handle all these cases correctly in a somewhat
circuitous way. The ModuleInfo structure returned by Validator::validate
provides a reference count for each expression in each function in the module.
Naturally, any expression with a reference count of two or more deserves to be
evaluated and stored in a temporary variable at the point that the Emit
statement covering it is executed. But if we selectively lower the reference
count threshold to one for the sensitive expression types listed above, so
that we always generate a temporary variable and save their value, then the
same code that manages multiply referenced expressions will take care of
introducing temporaries for time-sensitive expressions as well. The
Expression::bake_ref_count method (private to the back ends) is meant to help
with this.
§Expression scope
Each Expression has a scope, which is the region of the function within
which it can be used by Statements and other Expressions. It is a validation
error to use an Expression outside its scope.
An expression’s scope is defined as follows:
-
The scope of a
Constant,GlobalVariable,FunctionArgumentorLocalVariableexpression covers the entireFunctionin which it occurs. -
The scope of an expression evaluated by an
Emitstatement covers the subsequent expressions in thatEmit, the subsequent statements in theBlockto which thatEmitbelongs (if any) and their sub-statements (if any). -
The
resultexpression of aCallorAtomicstatement has a scope covering the subsequent statements in theBlockin which the statement occurs (if any) and their sub-statements (if any).
For example, this implies that an expression evaluated by some statement in a
nested Block is not available in the Block’s parents. Such a value would
need to be stored in a local variable to be carried upwards in the statement
tree.
§Constant expressions
A Naga constant expression is one of the following Expression
variants, whose operands (if any) are also constant expressions:
LiteralConstant, forConstantsZeroValue, for fixed-size typesComposeAccessAccessIndexSplatSwizzleUnaryBinarySelectRelationalMathAs
A constant expression can be evaluated at module translation time.
§Override expressions
A Naga override expression is the same as a constant expression,
except that it is also allowed to reference other Overrides.
An override expression can be evaluated at pipeline creation time.
Structs§
- Barrier
- Memory barrier flags.
- Block
- A code block is a vector of statements, with maybe a vector of spans.
- Constant
- Constant value.
- DocComments
- Doc comments preceding items.
- Entry
Point - The main function for a pipeline stage.
- Function
- A function defined in the module.
- Function
Argument - A function argument.
- Function
Result - A function result.
- Global
Variable - Variable defined at module level.
- Local
Variable - Variable defined at function level.
- Module
- Shader module.
- Override
- Pipeline-overridable constant.
- RayFlag
- Ray flags used when casting rays. Matching vulkan constants can be found in https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/ray_common/ray_flags_section.txt
- Resource
Binding - Pipeline binding information for global resources.
- Scalar
- Characteristics of a scalar type.
- Special
Types - Set of special types that can be optionally generated by the frontends.
- Storage
Access - Flags describing an image.
- Struct
Member - Member of a user-defined structure.
- Switch
Case - A case for a switch statement.
- Type
- A data type declared in the module.
Enums§
- Address
Space - Addressing space of variables.
- Array
Size - Size of an array.
- Atomic
Function - Function on an atomic value.
- Binary
Operator - Operation that can be applied on two values.
- Binding
- Describes how an input/output variable is to be bound.
- BuiltIn
- Built-in inputs and outputs.
- Collective
Operation - Conservative
Depth - Enables adjusting depth without disabling early Z.
- Derivative
Axis - Axis on which to compute a derivative.
- Derivative
Control - Hint at which precision to compute a derivative.
- Direction
- Early
Depth Test - Explicitly allows early depth/stencil tests.
- Expression
- An expression that can be evaluated to obtain a value.
- Gather
Mode - The specific behavior of a
SubgroupGatherstatement. - Image
Class - Sub-class of the image type.
- Image
Dimension - The number of dimensions an image has.
- Image
Query - Type of an image query.
- Interpolation
- The interpolation qualifier of a binding or struct field.
- Literal
- Math
Function - Built-in shader function for math.
- Predeclared
Type - Return types predeclared for the frexp, modf, and atomicCompareExchangeWeak built-in functions.
- RayQuery
Function - An operation that a
RayQuerystatement applies to itsqueryoperand. - RayQuery
Intersection - Type of a ray query intersection. Matching vulkan constants can be found in https://github.com/KhronosGroup/SPIRV-Registry/blob/main/extensions/KHR/SPV_KHR_ray_query.asciidoc but the actual values are different for candidate intersections.
- Relational
Function - Built-in shader function for testing relation between values.
- Sample
Level - Sampling modifier to control the level of detail.
- Sampling
- The sampling qualifiers of a binding or struct field.
- Scalar
Kind - Primitive type for a scalar.
- Shader
Stage - Stage of the programmable pipeline.
- Statement
- Instructions which make up an executable block.
- Storage
Format - Image storage format.
- Subgroup
Operation - Switch
Value - The value of the switch case.
- Swizzle
Component - Component selection for a vector swizzle.
- Type
Inner - Enum with additional information, depending on the kind of type.
- Unary
Operator - Operation that can be applied on a single value.
- Vector
Size - Number of components in a vector.
Type Aliases§
- Bytes
- Number of bytes per scalar.