Documentation
¶
Overview ¶
Package consteval evaluates WGSL const-expressions: the shader-creation-time evaluation that the WGSL specification requires for const declarations and that naga also applies to constant operands in function bodies.
It is the only constant evaluator in naga, used by every lowering context, like Rust naga's proc::ConstantEvaluator (ADR-007). It has two layers:
- The numeric kernel works on scalars and vectors of ir.LiteralValue: Math, Binary, Unary, Cast, Relational and Select. It has no arena.
- The Evaluator applies the kernel to an expression arena, appending the evaluated result, as Rust's try_eval_and_append does.
A result is always finite and representable: an operation whose result would overflow, leave its function's domain, divide by zero or not fit the target type returns an *Error instead, as the specification requires ("A shader-creation error results if any const-expression of floating-point type overflows or evaluates to NaN or infinity"). Arithmetic on concrete integer types wraps modulo 2^bitwidth, also as specified.
Index ¶
- Variables
- func FallsBack(err error) bool
- func RoundF16(x float64) (float32, bool)
- func RustEvaluates(fun ir.MathFunction) bool
- func RustEvaluatesRelational(fun ir.RelationalFunction) bool
- func ScalarOf(lit ir.LiteralValue) ir.ScalarType
- type Arena
- type Behavior
- type Error
- type Evaluator
- func (e *Evaluator) CastArray(h ir.ExpressionHandle, to ir.ScalarType) (ir.ExpressionHandle, error)
- func (e *Evaluator) ForceRuntime(h ir.ExpressionHandle)
- func (e *Evaluator) IsConst(h ir.ExpressionHandle) bool
- func (e *Evaluator) IsConstOrOverride(h ir.ExpressionHandle) bool
- func (e *Evaluator) Literal(h ir.ExpressionHandle) (lit ir.LiteralValue, ok bool)
- func (e *Evaluator) TryEvalAndAppend(kind ir.ExpressionKind) (ir.ExpressionHandle, error)
- type Types
- type Value
- func Binary(op ir.BinaryOperator, a, b Value) (Value, error)
- func Cast(v Value, to ir.ScalarType) (Value, error)
- func Math(fun ir.MathFunction, args ...Value) (Value, error)
- func Relational(fun ir.RelationalFunction, v Value) (Value, error)
- func Scalar(lit ir.LiteralValue) Value
- func Select(reject, accept, cond Value) (Value, error)
- func Unary(op ir.UnaryOperator, v Value) (Value, error)
- func Vector(comps ...ir.LiteralValue) Value
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotImplemented: the operation is legal but cannot be evaluated // here. A function body keeps it as a run-time expression. ErrNotImplemented = errors.New("not implemented") // ErrInvalidBinaryArgs: operand types a binary operator does not accept // here. A function body keeps the expression and leaves it to // validation (Rust InvalidBinaryOpArgs). ErrInvalidBinaryArgs = errors.New("invalid binary operator arguments") // ErrInvalidOperand: an operand a math, conversion, unary or relational // operation does not accept (Rust InvalidMathArg, InvalidCastArg, // InvalidUnaryOpArg, InvalidRelationalArg). Unlike invalid binary // arguments it is an error in every context. ErrInvalidOperand = errors.New("invalid operand") // ErrNotConst: an operand is not a const-expression. ErrNotConst = errors.New("not a const-expression") // ErrOverrideInConst: an override-expression where a const-expression // is required. ErrOverrideInConst = errors.New("override-expression in a const-expression") // ErrOverflow: the result is not finite or does not fit its type. ErrOverflow = errors.New("overflow") // ErrOutOfDomain: an argument is outside the function's domain. ErrOutOfDomain = errors.New("argument out of domain") // ErrDivisionByZero: integer division by zero, or signed MIN / -1. ErrDivisionByZero = errors.New("division by zero") // ErrRemainderByZero: integer remainder by zero, or signed MIN % -1. ErrRemainderByZero = errors.New("remainder by zero") // ErrInvalidClamp: clamp with low > high. ErrInvalidClamp = errors.New("invalid clamp") // ErrBitRange: extractBits/insertBits offset + count exceeds the width. ErrBitRange = errors.New("bit range out of bounds") // ErrShiftOverflow: a shift amount or shifted value out of range. ErrShiftOverflow = errors.New("shift overflow") // ErrNotRepresentable: a value cannot be converted to the target type. ErrNotRepresentable = errors.New("value not representable") )
Sentinel errors classify evaluation failures; every error the package returns is an *Error that wraps one of them, so callers test the class with errors.Is(err, consteval.ErrOverflow). They mirror the variants of Rust naga's ConstantEvaluatorError that a WGSL const-expression can produce.
Functions ¶
func FallsBack ¶
FallsBack reports whether, in a function body, an expression whose evaluation failed with err is kept as a run-time expression instead of being a shader-creation error. It is Rust naga's rule in try_eval_and_append: only NotImplemented and InvalidBinaryOpArgs fall back; overflow, domain, division and range errors are errors wherever the operands are constant.
func RoundF16 ¶ added in v0.20.1
RoundF16 rounds x to the nearest binary16 value as roundF16 does; ok is false for NaN, infinities and values beyond the finite binary16 range.
func RustEvaluates ¶
func RustEvaluates(fun ir.MathFunction) bool
RustEvaluates reports whether Rust naga v30 evaluates the math function. A function body keeps the others as run-time expressions, as Rust does, so that its IR matches Rust's; a const-expression evaluates them anyway.
func RustEvaluatesRelational ¶
func RustEvaluatesRelational(fun ir.RelationalFunction) bool
RustEvaluatesRelational reports whether Rust naga v30 evaluates the function; a function body keeps isNan and isInf as run-time expressions to match Rust's IR.
func ScalarOf ¶
func ScalarOf(lit ir.LiteralValue) ir.ScalarType
ScalarOf returns the scalar type of a literal. Abstract types have width 8, the width of their representation (int64 and float64).
Types ¶
type Arena ¶
type Arena interface {
Expression(h ir.ExpressionHandle) ir.ExpressionKind
// Append adds an expression and returns its handle. In a function
// arena it keeps expressions that need no Emit (Literal, Constant,
// ZeroValue, ...) out of the pending Emit range, as Rust's append_expr.
Append(kind ir.ExpressionKind) ir.ExpressionHandle
// IsGlobal reports whether the arena is Module.GlobalExpressions.
IsGlobal() bool
}
Arena is the expression arena the evaluator reads operands from and appends to: a function's expressions or Module.GlobalExpressions.
type Behavior ¶
type Behavior int
Behavior selects what the evaluator may leave unevaluated, like Rust naga's Behavior / WgslRestrictions.
const ( // BehaviorConst: a const-expression (module or function-local const). // Every expression must evaluate; anything else is an error. BehaviorConst Behavior = iota + 1 // BehaviorRuntime: a function body. Expressions with constant operands // are evaluated; the rest, and operations Rust naga does not evaluate, // are appended as run-time expressions. BehaviorRuntime // BehaviorOverride: an override initializer. Constant operands are // evaluated; override-expressions are appended as they are. BehaviorOverride )
type Error ¶
Error is an evaluation error: a class (one of the Err sentinels) and the full message, worded like Rust naga's where Rust reports the condition.
type Evaluator ¶
type Evaluator struct {
Module *ir.Module
Arena Arena
Types Types
Behavior Behavior
// contains filtered or unexported fields
}
Evaluator evaluates expressions over an arena, as Rust naga's ConstantEvaluator::try_eval_and_append does, and tracks the evaluation stage of every expression in the arena (Rust's ExpressionKindTracker). One Evaluator serves one arena for its whole lifetime.
func (*Evaluator) CastArray ¶
func (e *Evaluator) CastArray(h ir.ExpressionHandle, to ir.ScalarType) (ir.ExpressionHandle, error)
CastArray converts the leaf scalars of the const-expression h to the scalar type to, as Rust naga's cast_array does for WGSL's automatic conversions and concretization: an array constructor is converted element by element into a new constructor of the converted array type, a zero array becomes a zero value of the converted type, and anything else is an As conversion.
func (*Evaluator) ForceRuntime ¶
func (e *Evaluator) ForceRuntime(h ir.ExpressionHandle)
ForceRuntime marks h as a run-time expression although its operands are constant: WGSL says an expression referring to a let-bound value is not a const-expression (Rust force_non_const).
func (*Evaluator) IsConst ¶
func (e *Evaluator) IsConst(h ir.ExpressionHandle) bool
IsConst reports whether h is a const-expression.
func (*Evaluator) IsConstOrOverride ¶
func (e *Evaluator) IsConstOrOverride(h ir.ExpressionHandle) bool
IsConstOrOverride reports whether h is a const- or override-expression.
func (*Evaluator) Literal ¶
func (e *Evaluator) Literal(h ir.ExpressionHandle) (lit ir.LiteralValue, ok bool)
Literal returns the value of the scalar const-expression h, looking through constants and zero values (Rust get_const_val). ok is false when h is not a const-expression or not a scalar.
func (*Evaluator) TryEvalAndAppend ¶
func (e *Evaluator) TryEvalAndAppend(kind ir.ExpressionKind) (ir.ExpressionHandle, error)
TryEvalAndAppend appends kind to the arena, evaluated if it can be, and returns the handle of the result.
- Every operand a const-expression: kind is evaluated and its value (Literal, Compose, Splat, ZeroValue, or for a constant in the module arena the constant's initializer) is the result.
- An operand an override or run-time expression: kind is appended unchanged when the behavior allows that, else it is an error.
- In a function body an operation Rust naga does not evaluate is appended unchanged as a run-time expression (Rust's IR shape), after it is checked: a value the specification makes a shader-creation error is reported, which Rust does not do.
The error says why kind is not a valid expression here.
type Types ¶
type Types interface {
// Type returns the handle of the anonymous type inner, adding it if
// the module has none.
Type(inner ir.TypeInner) ir.TypeHandle
// Array returns the handle of the array of base with the given size,
// with the stride of base's layout.
Array(base ir.TypeHandle, size ir.ArraySize) ir.TypeHandle
}
Types registers result types.
type Value ¶
type Value struct {
// contains filtered or unexported fields
}
Value is the operand and result of the numeric kernel: a scalar, or a vector of 2 to 4 components that all have the same scalar type.
func Binary ¶
func Binary(op ir.BinaryOperator, a, b Value) (Value, error)
Binary evaluates a binary operator on scalars or vectors.
Both operands must have the same scalar type, except the right operand of a shift, which is u32. Vector operands are combined component-wise; a vector and a scalar combine only with + - * / %, the scalar staying on its side. Comparisons yield bool components.
Concrete integer + - * wrap; abstract integer arithmetic is overflow-checked; float results must be finite. Invalid operand types are ErrInvalidBinaryArgs, which a function body keeps as a run-time expression.
func Cast ¶
func Cast(v Value, to ir.ScalarType) (Value, error)
Cast converts each component of v to the scalar type to, as the WGSL conversion built-ins (value constructors such as i32(e)) and automatic conversions of abstract values do:
- integer to integer of the same width reinterprets the bits;
- float to integer truncates toward zero and saturates to the value of the target closest to the result that the source float type can represent exactly (WGSL "scalar floating point to integral conversion");
- integer or float to float rounds to nearest, ties to even;
- anything to bool is "not zero", bool to a number is 0 or 1.
Converting an abstract value is range-checked: a value the target cannot represent is a NotRepresentable error, with Rust naga's message. A conversion whose float result would be infinite is the same error for every source type (the specification forbids a non-finite const-expression; Rust naga v30 instead yields an f16 infinity from integer and f32 sources). Concrete values never convert to abstract types (ErrInvalidOperand).
func Math ¶
func Math(fun ir.MathFunction, args ...Value) (Value, error)
Math evaluates a math built-in function on constant arguments.
Component-wise functions take scalars or vectors; every argument must have the same shape and scalar type (the caller converts abstract arguments and splats scalars first, as the WGSL overload rules require).
Float results are computed in float64 and rounded once to the argument's type. Rust naga computes f32 and f16 in f32 libm; a transcendental result can therefore differ from Rust's in the last place, never by more.
func Relational ¶
func Relational(fun ir.RelationalFunction, v Value) (Value, error)
Relational evaluates all, any, isNan and isInf. all and any reduce a bool vector to a bool and return a bool scalar unchanged. isNan and isInf are false for every component: a constant is always finite (Rust v30 does not evaluate them; see RustEvaluatesRelational).
func Select ¶
Select is select(reject, accept, condition): accept where the condition is true. A bool scalar condition chooses for every component; a bool vector chooses per component. As in Rust naga, accept is first converted to reject's scalar type, and a failed conversion is an error even where the condition picks reject.
func Unary ¶
func Unary(op ir.UnaryOperator, v Value) (Value, error)
Unary evaluates a unary operator component-wise. Negation wraps for signed integers (-MIN == MIN, as defined) and flips the sign of floats, including zero; unsigned and bool operands are invalid.
func Vector ¶
func Vector(comps ...ir.LiteralValue) Value
Vector returns a vector value. It panics unless there are 2 to 4 components of one scalar type: a malformed vector is a bug in the caller, not a shader error.
func (Value) Component ¶
func (v Value) Component(i int) ir.LiteralValue
Component returns component i.
func (Value) Components ¶
func (v Value) Components() []ir.LiteralValue
Components returns the components; the caller must not modify the slice.
func (Value) Scalar ¶
func (v Value) Scalar() ir.ScalarType
Scalar returns the scalar type of the components.
func (Value) Size ¶
func (v Value) Size() ir.VectorSize
Size returns the vector size; it is only meaningful for a vector.