///|
fn wgsl_ir_switch_value_from_ast_expression(
lowerer : WgslIrLowerer,
function : Function,
scope : WgslIrFunctionScope,
selector : Handle,
expr_ref : @ast.WgslExprRef,
) -> SwitchValue raise WgslIrLowerError {
guard expr_ref.root() is Some(root) else {
raise UnsupportedExpression("empty switch case selector")
}
let selector_is_unsigned = lowerer.switch_selector_is_unsigned(
function, selector,
)
lowerer
.eval_ast_integer_expression(root, function, scope)
.to_switch_value(
selector_is_unsigned,
wgsl_ir_switch_case_label_style_from_ast_expression(root),
)
}
///|
fn wgsl_ir_switch_case_label_style_from_ast_expression(
node : @ast.WgslExprNode,
) -> SwitchCaseLabelStyle {
match node.kind() {
Group(inner) | Sequence([inner]) =>
wgsl_ir_switch_case_label_style_from_ast_expression(inner)
Literal(token) =>
match token.numeric_literal() {
Some({ category: AbstractIntLiteral, suffix: NoSuffix, .. }) =>
SourceAbstractInt
_ => Typed
}
_ => Typed
}
}
///|
fn WgslIrLowerer::switch_selector_is_unsigned(
self : WgslIrLowerer,
function : Function,
selector : Handle,
) -> Bool {
match self.function_expression_type(function, selector) {
Some(ty_handle) =>
match self.shader_module.types.get(ty_handle) {
Some(ty) =>
match ty.inner {
Scalar(scalar) => scalar.kind == Uint
_ => false
}
None => false
}
None => false
}
}
///|
fn WgslIrLowerer::eval_ast_integer_expression(
self : WgslIrLowerer,
node : @ast.WgslExprNode,
function : Function,
scope : WgslIrFunctionScope,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match node.kind() {
Literal(token) => wgsl_ir_eval_ast_integer_literal(token)
Identifier(name) =>
self.eval_ast_integer_identifier(name.name(), function, scope)
Unary(op, inner) =>
match wgsl_ir_unary_operator(op) {
Negate => {
let value = self
.eval_ast_integer_expression(inner, function, scope)
.to_signed_int()
Signed(-value)
}
BitwiseNot =>
match self.eval_ast_integer_expression(inner, function, scope) {
Unsigned(value) => Unsigned(value.lnot())
Signed(value) => Signed(value.lnot())
Abstract(value) => Abstract(value.lnot())
}
LogicalNot =>
raise UnsupportedExpression(
"switch case const selector uses non-integer unary operator",
)
}
Binary(op, left, right) =>
wgsl_ir_eval_integer_binary(
wgsl_ir_binary_operator(op),
self.eval_ast_integer_expression(left, function, scope),
self.eval_ast_integer_expression(right, function, scope),
)
Call(callee, arguments) => {
let arguments = wgsl_ir_call_arguments(arguments)
self.eval_ast_integer_call(callee, arguments, function, scope)
}
Group(inner) => self.eval_ast_integer_expression(inner, function, scope)
Sequence([single]) =>
self.eval_ast_integer_expression(single, function, scope)
_ =>
raise UnsupportedExpression(
"switch case const selector must initialize to an integer expression",
)
}
}
///|
fn wgsl_ir_eval_ast_integer_literal(
token : @ast.WgslExprToken,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match wgsl_ir_literal_from_token(token) {
I32(value) => Signed(value)
U32(value) => Unsigned(value)
AbstractInt(value) => Abstract(value)
_ =>
raise UnsupportedExpression(
"switch case const selector must initialize to an integer expression",
)
}
}
///|
fn WgslIrLowerer::eval_ast_integer_identifier(
self : WgslIrLowerer,
name : String,
function : Function,
scope : WgslIrFunctionScope,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match scope.local_constants.get(name) {
Some({ value: LazyConstant(local_constant), .. }) =>
match local_constant.expr_ref.root() {
Some(root) =>
return self.eval_ast_integer_expression(root, function, scope)
None => raise UnsupportedExpression("empty local const selector")
}
Some({ value: ExpressionAlias(handle, _), .. }) =>
return self.eval_function_integer_expression(function, handle)
None => ()
}
match self.ensure_constant_declaration_lowered(name) {
Some(handle) =>
match self.shader_module.constants.items.get(handle.index()) {
Some(constant) => self.eval_global_integer_expression(constant.init)
None =>
raise UnsupportedExpression(
"switch case selector references missing const",
)
}
None => raise UnknownIdentifier(name)
}
}
///|
fn WgslIrLowerer::eval_ast_integer_call(
self : WgslIrLowerer,
callee : @ast.WgslExprNode,
arguments : Array[@ast.WgslExprNode],
function : Function,
scope : WgslIrFunctionScope,
) -> WgslIrIntegerValue raise WgslIrLowerError {
let arguments = wgsl_ir_flatten_expression_arguments(arguments)
guard callee.kind() is Identifier(name) else {
raise UnsupportedExpression("switch case selector uses non-identifier call")
}
match name.name() {
"i32" => {
guard arguments is [argument] else {
raise UnsupportedExpression(
"i32 switch selector cast expects one argument",
)
}
Signed(
self
.eval_ast_integer_expression(argument, function, scope)
.to_signed_int(),
)
}
"u32" => {
guard arguments is [argument] else {
raise UnsupportedExpression(
"u32 switch selector cast expects one argument",
)
}
Unsigned(
self
.eval_ast_integer_expression(argument, function, scope)
.to_unsigned(),
)
}
_ =>
match wgsl_ir_math_function_from_name(name.name()) {
Some(Min) => {
guard arguments is [left, right] else {
raise UnsupportedExpression(
"min switch selector expects two arguments",
)
}
wgsl_ir_eval_integer_min(
self.eval_ast_integer_expression(left, function, scope),
self.eval_ast_integer_expression(right, function, scope),
)
}
Some(Max) => {
guard arguments is [left, right] else {
raise UnsupportedExpression(
"max switch selector expects two arguments",
)
}
wgsl_ir_eval_integer_max(
self.eval_ast_integer_expression(left, function, scope),
self.eval_ast_integer_expression(right, function, scope),
)
}
Some(Clamp) => {
guard arguments is [value, low, high] else {
raise UnsupportedExpression(
"clamp switch selector expects three arguments",
)
}
wgsl_ir_eval_integer_clamp(
self.eval_ast_integer_expression(value, function, scope),
self.eval_ast_integer_expression(low, function, scope),
self.eval_ast_integer_expression(high, function, scope),
)
}
_ =>
raise UnsupportedExpression(
"switch case selector uses non-integer const call",
)
}
}
}
///|
fn WgslIrLowerer::switch_case_constant_dependencies(
self : WgslIrLowerer,
expr_ref : @ast.WgslExprRef,
function : Function,
scope : WgslIrFunctionScope,
) -> Array[Handle] raise WgslIrLowerError {
let dependencies : Array[Handle] = []
match expr_ref.root() {
Some(root) =>
self.collect_ast_integer_constant_dependencies(
root, function, scope, dependencies,
)
None => ()
}
dependencies
}
///|
fn WgslIrLowerer::collect_ast_integer_constant_dependencies(
self : WgslIrLowerer,
node : @ast.WgslExprNode,
function : Function,
scope : WgslIrFunctionScope,
dependencies : Array[Handle],
) -> Unit raise WgslIrLowerError {
match node.kind() {
Identifier(name) =>
self.collect_ast_identifier_constant_dependencies(
name.name(),
function,
scope,
dependencies,
)
Unary(_, inner) | Group(inner) =>
self.collect_ast_integer_constant_dependencies(
inner, function, scope, dependencies,
)
Binary(_, left, right) => {
self.collect_ast_integer_constant_dependencies(
left, function, scope, dependencies,
)
self.collect_ast_integer_constant_dependencies(
right, function, scope, dependencies,
)
}
Call(_, arguments) =>
for argument in wgsl_ir_flatten_expression_arguments(arguments) {
self.collect_ast_integer_constant_dependencies(
argument, function, scope, dependencies,
)
}
Sequence(items) =>
for item in items {
self.collect_ast_integer_constant_dependencies(
item, function, scope, dependencies,
)
}
Literal(_) | ShaderDefTemplate(_) => ()
_ =>
raise UnsupportedExpression(
"switch case const selector must initialize to an integer expression",
)
}
}
///|
fn WgslIrLowerer::collect_ast_identifier_constant_dependencies(
self : WgslIrLowerer,
name : String,
function : Function,
scope : WgslIrFunctionScope,
dependencies : Array[Handle],
) -> Unit raise WgslIrLowerError {
match scope.local_constants.get(name) {
Some({ value: LazyConstant(local_constant), .. }) => {
match local_constant.expr_ref.root() {
Some(root) =>
self.collect_ast_integer_constant_dependencies(
root, function, scope, dependencies,
)
None => ()
}
return
}
Some({ value: ExpressionAlias(handle, _), .. }) => {
self.collect_function_expression_constant_dependencies(
function, handle, dependencies,
)
return
}
None => ()
}
match self.ensure_constant_declaration_lowered(name) {
Some(handle) => dependencies.push(handle)
None => ()
}
}
///|
fn WgslIrLowerer::collect_function_expression_constant_dependencies(
self : WgslIrLowerer,
function : Function,
handle : Handle,
dependencies : Array[Handle],
) -> Unit {
match function.expressions.items.get(handle.index()) {
Some(expression) =>
match expression {
Constant(handle) => dependencies.push(handle)
Unary(_, inner) | As(inner, _, _) | Bitcast(_, inner) =>
self.collect_function_expression_constant_dependencies(
function, inner, dependencies,
)
Binary(_, left, right) => {
self.collect_function_expression_constant_dependencies(
function, left, dependencies,
)
self.collect_function_expression_constant_dependencies(
function, right, dependencies,
)
}
Math(_, a, b, c, d) => {
self.collect_function_expression_constant_dependencies(
function, a, dependencies,
)
wgsl_ir_collect_optional_function_expression_constant_dependencies(
self, function, b, dependencies,
)
wgsl_ir_collect_optional_function_expression_constant_dependencies(
self, function, c, dependencies,
)
wgsl_ir_collect_optional_function_expression_constant_dependencies(
self, function, d, dependencies,
)
}
_ => ()
}
None => ()
}
}
///|
fn wgsl_ir_collect_optional_function_expression_constant_dependencies(
lowerer : WgslIrLowerer,
function : Function,
handle : Handle?,
dependencies : Array[Handle],
) -> Unit {
match handle {
Some(handle) =>
lowerer.collect_function_expression_constant_dependencies(
function, handle, dependencies,
)
None => ()
}
}
///|
fn WgslIrLowerer::eval_global_integer_expression(
self : WgslIrLowerer,
handle : Handle,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match self.shader_module.global_expressions.items.get(handle.index()) {
Some(expression) => self.eval_global_integer_expression_inner(expression)
None =>
raise UnsupportedExpression(
"switch case selector references missing expression",
)
}
}
///|
fn WgslIrLowerer::lower_function_constant_array_size_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> ArraySize raise WgslIrLowerError {
let evaluated = Some(self.eval_function_integer_expression(function, handle)) catch {
_ => None
}
match evaluated {
Some(value) => Constant(value.to_non_negative_int())
None =>
raise UnsupportedType(
"local array size must resolve to an integer const expression",
)
}
}
///|
fn WgslIrLowerer::eval_function_integer_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match function.expressions.items.get(handle.index()) {
Some(expression) =>
self.eval_function_integer_expression_inner(function, expression)
None =>
raise UnsupportedExpression("array size references missing expression")
}
}
///|
fn WgslIrLowerer::eval_function_integer_expression_inner(
self : WgslIrLowerer,
function : Function,
expression : Expression,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match expression {
Literal(I32(value)) => Signed(value)
Literal(U32(value)) => Unsigned(value)
Literal(AbstractInt(value)) => Abstract(value)
Constant(handle) =>
match self.shader_module.constants.items.get(handle.index()) {
Some(constant) => self.eval_global_integer_expression(constant.init)
None =>
raise UnsupportedExpression(
"array size references missing nested const",
)
}
Binary(op, left, right) => {
let left_value = self.eval_function_integer_expression(function, left)
let right_value = self.eval_function_integer_expression(function, right)
wgsl_ir_eval_integer_binary(op, left_value, right_value)
}
Unary(Negate, inner) => {
let value = self
.eval_function_integer_expression(function, inner)
.to_signed_int()
Signed(-value)
}
Unary(BitwiseNot, inner) =>
match self.eval_function_integer_expression(function, inner) {
Unsigned(value) => Unsigned(value.lnot())
Signed(value) => Signed(value.lnot())
Abstract(value) => Abstract(value.lnot())
}
_ =>
raise UnsupportedExpression(
"array size must initialize to an integer expression",
)
}
}
///|
fn WgslIrLowerer::eval_constant_integer_expression(
self : WgslIrLowerer,
handle : Handle,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match self.shader_module.constants.items.get(handle.index()) {
Some(constant) => self.eval_global_integer_expression(constant.init)
None =>
raise UnsupportedExpression(
"constant expression references missing const",
)
}
}
///|
fn WgslIrLowerer::eval_global_integer_expression_inner(
self : WgslIrLowerer,
expression : Expression,
) -> WgslIrIntegerValue raise WgslIrLowerError {
match expression {
Literal(I32(value)) => Signed(value)
Literal(U32(value)) => Unsigned(value)
Literal(AbstractInt(value)) => Abstract(value)
Constant(handle) =>
match self.shader_module.constants.items.get(handle.index()) {
Some(constant) => self.eval_global_integer_expression(constant.init)
None =>
raise UnsupportedExpression(
"switch case selector references missing nested const",
)
}
Binary(op, left, right) => {
let left_value = self.eval_global_integer_expression(left)
let right_value = self.eval_global_integer_expression(right)
wgsl_ir_eval_integer_binary(op, left_value, right_value)
}
Math(Min, a, Some(b), None, None) =>
wgsl_ir_eval_integer_min(
self.eval_global_integer_expression(a),
self.eval_global_integer_expression(b),
)
Math(Max, a, Some(b), None, None) =>
wgsl_ir_eval_integer_max(
self.eval_global_integer_expression(a),
self.eval_global_integer_expression(b),
)
Math(Clamp, value, Some(low), Some(high), None) =>
wgsl_ir_eval_integer_clamp(
self.eval_global_integer_expression(value),
self.eval_global_integer_expression(low),
self.eval_global_integer_expression(high),
)
Unary(Negate, inner) => {
let value = self.eval_global_integer_expression(inner).to_signed_int()
Signed(-value)
}
Unary(BitwiseNot, inner) =>
match self.eval_global_integer_expression(inner) {
Unsigned(value) => Unsigned(value.lnot())
Signed(value) => Signed(value.lnot())
Abstract(value) => Abstract(value.lnot())
}
_ =>
raise UnsupportedExpression(
"switch case const selector must initialize to an integer expression",
)
}
}
///|
priv enum WgslIrIntegerValue {
Signed(Int32)
Unsigned(UInt32)
Abstract(Int64)
}
///|
fn WgslIrIntegerValue::is_unsigned(self : WgslIrIntegerValue) -> Bool {
match self {
Unsigned(_) => true
Signed(_) | Abstract(_) => false
}
}
///|
fn WgslIrIntegerValue::is_abstract(self : WgslIrIntegerValue) -> Bool {
match self {
Abstract(_) => true
Signed(_) | Unsigned(_) => false
}
}
///|
fn WgslIrIntegerValue::to_abstract_int64(self : WgslIrIntegerValue) -> Int64 {
match self {
Abstract(value) => value
Signed(value) => Int64::from_int(value)
Unsigned(value) => value.to_uint64().reinterpret_as_int64()
}
}
///|
fn WgslIrIntegerValue::to_switch_value(
self : WgslIrIntegerValue,
selector_is_unsigned : Bool,
label_style : SwitchCaseLabelStyle,
) -> SwitchValue raise WgslIrLowerError {
if selector_is_unsigned {
U32(self.to_unsigned(), label_style)
} else {
match self {
Unsigned(value) =>
I32(WgslIrIntegerValue::Unsigned(value).to_signed_int(), label_style)
Signed(value) => I32(value, label_style)
Abstract(value) => I32(wgsl_ir_int64_to_i32(value), label_style)
}
}
}
///|
fn WgslIrIntegerValue::to_signed_int(
self : WgslIrIntegerValue,
) -> Int32 raise WgslIrLowerError {
match self {
Signed(value) => value
Abstract(value) => wgsl_ir_int64_to_i32(value)
Unsigned(value) =>
match wgsl_ir_uint_literal_to_small_int(value) {
Some(value) => value
None =>
raise UnsupportedExpression(
"integer expression is outside signed int range",
)
}
}
}
///|
fn WgslIrIntegerValue::to_unsigned(
self : WgslIrIntegerValue,
) -> UInt32 raise WgslIrLowerError {
match self {
Unsigned(value) => value
Signed(value) => wgsl_ir_non_negative_int_to_uint(value)
Abstract(value) => {
if value < Int64::from_int(0) {
raise UnsupportedExpression(
"unsigned integer expression contains a negative operand",
)
}
if value > 4294967295L {
raise UnsupportedExpression(
"integer expression is outside unsigned int range",
)
}
value.reinterpret_as_uint64().to_uint()
}
}
}
///|
fn wgsl_ir_non_negative_int_to_uint(
value : Int32,
) -> UInt32 raise WgslIrLowerError {
if value < 0 {
raise UnsupportedExpression(
"unsigned integer expression contains a negative operand",
)
}
value.reinterpret_as_uint()
}
///|
fn WgslIrIntegerValue::to_non_negative_int(
self : WgslIrIntegerValue,
) -> Int32 raise WgslIrLowerError {
let value = self.to_signed_int()
if value < 0 {
raise UnsupportedExpression("integer expression must be non-negative")
}
value
}
///|
fn WgslIrIntegerValue::to_signed_literal(
self : WgslIrIntegerValue,
width : Int32,
) -> Literal raise WgslIrLowerError {
let value = self.to_signed_int()
match width {
8 => I64(Int64::from_int(value))
_ => I32(value)
}
}
///|
fn WgslIrIntegerValue::to_unsigned_literal(
self : WgslIrIntegerValue,
width : Int32,
) -> Literal raise WgslIrLowerError {
let value = self.to_unsigned()
match width {
8 => U64(value.to_uint64())
_ => U32(value)
}
}
///|
fn WgslIrIntegerValue::to_abstract_int_literal(
self : WgslIrIntegerValue,
) -> Literal raise WgslIrLowerError {
match self {
Abstract(value) => AbstractInt(value)
Signed(value) => AbstractInt(Int64::from_int(value))
Unsigned(value) =>
AbstractInt(
Int64::from_int(WgslIrIntegerValue::Unsigned(value).to_signed_int()),
)
}
}
///|
fn wgsl_ir_eval_integer_binary(
op : BinaryOperator,
left : WgslIrIntegerValue,
right : WgslIrIntegerValue,
) -> WgslIrIntegerValue raise WgslIrLowerError {
if left.is_unsigned() || right.is_unsigned() {
let left = left.to_unsigned()
let right_value = right.to_unsigned()
let right_shift = WgslIrIntegerValue::Unsigned(right_value).to_non_negative_int()
return match op {
Add => Unsigned(left + right_value)
Subtract => Unsigned(left - right_value)
Multiply => Unsigned(left * right_value)
Divide => Unsigned(left / right_value)
Modulo => Unsigned(left % right_value)
And => Unsigned(left & right_value)
ExclusiveOr => Unsigned(left ^ right_value)
InclusiveOr => Unsigned(left | right_value)
ShiftLeft => Unsigned(left << right_shift)
ShiftRight => Unsigned(left >> right_shift)
_ =>
raise UnsupportedExpression(
"switch case const selector uses non-integer binary operator",
)
}
}
if left.is_abstract() || right.is_abstract() {
let left = left.to_abstract_int64()
let right_value = right.to_abstract_int64()
let right_shift = WgslIrIntegerValue::Abstract(right_value).to_non_negative_int()
return match op {
Add => Abstract(left + right_value)
Subtract => Abstract(left - right_value)
Multiply => Abstract(left * right_value)
Divide => Abstract(left / right_value)
Modulo => Abstract(left % right_value)
And => Abstract(left & right_value)
ExclusiveOr => Abstract(left ^ right_value)
InclusiveOr => Abstract(left | right_value)
ShiftLeft => Abstract(left << right_shift)
ShiftRight => Abstract(left >> right_shift)
_ =>
raise UnsupportedExpression(
"switch case const selector uses non-integer binary operator",
)
}
}
let left = left.to_signed_int()
let right = right.to_signed_int()
match op {
Add => Signed(left + right)
Subtract => Signed(left - right)
Multiply => Signed(left * right)
Divide => Signed(left / right)
Modulo => Signed(left % right)
And => Signed(left & right)
ExclusiveOr => Signed(left ^ right)
InclusiveOr => Signed(left | right)
ShiftLeft => Signed(left << right)
ShiftRight => Signed(left >> right)
_ =>
raise UnsupportedExpression(
"switch case const selector uses non-integer binary operator",
)
}
}
///|
fn wgsl_ir_eval_integer_min(
left : WgslIrIntegerValue,
right : WgslIrIntegerValue,
) -> WgslIrIntegerValue raise WgslIrLowerError {
if left.is_unsigned() || right.is_unsigned() {
let left = left.to_unsigned()
let right = right.to_unsigned()
Unsigned(if left < right { left } else { right })
} else {
let left = left.to_signed_int()
let right = right.to_signed_int()
Signed(if left < right { left } else { right })
}
}
///|
fn wgsl_ir_eval_integer_max(
left : WgslIrIntegerValue,
right : WgslIrIntegerValue,
) -> WgslIrIntegerValue raise WgslIrLowerError {
if left.is_unsigned() || right.is_unsigned() {
let left = left.to_unsigned()
let right = right.to_unsigned()
Unsigned(if left > right { left } else { right })
} else {
let left = left.to_signed_int()
let right = right.to_signed_int()
Signed(if left > right { left } else { right })
}
}
///|
fn wgsl_ir_eval_integer_clamp(
value : WgslIrIntegerValue,
low : WgslIrIntegerValue,
high : WgslIrIntegerValue,
) -> WgslIrIntegerValue raise WgslIrLowerError {
wgsl_ir_eval_integer_min(wgsl_ir_eval_integer_max(value, low), high)
}
///|
fn WgslIrLowerer::fold_function_constant_float_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Literal? {
match self.eval_function_float_expression(function, handle) {
Some(value) =>
Some(
self.folded_function_float_literal_for_expression(
function, handle, value,
),
)
None => None
}
}
///|
fn WgslIrLowerer::folded_function_float_literal_for_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
value : Double,
) -> Literal {
self.folded_function_float_literal_for_expression_with_expected(
function,
handle,
value,
None,
)
}
///|
fn WgslIrLowerer::folded_function_float_literal_for_expression_with_expected(
self : WgslIrLowerer,
function : Function,
handle : Handle,
value : Double,
expected_ty : Handle?,
) -> Literal {
let ty = match expected_ty {
Some(ty) => ty
None =>
match self.function_expression_type(function, handle) {
Some(ty) => ty
None => return AbstractFloat(value)
}
}
guard self.shader_module.types.get(ty) is Some(type_) else {
return AbstractFloat(value)
}
match type_.inner {
Scalar({ kind: Float, width: 4, }) =>
if wgsl_ir_function_expression_is_computed_float_fold(function, handle) {
F32Exact(Float::from_double(value))
} else {
F32(Float::from_double(value))
}
Scalar({ kind: Float, width, }) =>
wgsl_ir_float_literal_for_scalar_width(value, width)
_ =>
if wgsl_ir_function_expression_is_computed_float_fold(function, handle) &&
self.function_expression_tree_contains_concrete_f32_value(
function, handle,
) {
F32Exact(Float::from_double(value))
} else {
AbstractFloat(value)
}
}
}
///|
fn WgslIrLowerer::function_expression_tree_contains_concrete_f32_value(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Bool {
match function.expressions.items.get(handle.index()) {
Some(Literal(F32(_) | F32Exact(_))) => true
Some(Constant(constant)) => self.constant_is_concrete_f32(constant)
Some(Unary(_, inner)) | Some(Load(inner)) | Some(Splat(_, inner)) =>
self.function_expression_tree_contains_concrete_f32_value(function, inner)
Some(Binary(_, left, right)) =>
self.function_expression_tree_contains_concrete_f32_value(function, left) ||
self.function_expression_tree_contains_concrete_f32_value(function, right)
Some(Math(_, a, b, c, d)) =>
self.function_expression_tree_contains_concrete_f32_value(function, a) ||
self.optional_function_expression_tree_contains_concrete_f32_value(
function, b,
) ||
self.optional_function_expression_tree_contains_concrete_f32_value(
function, c,
) ||
self.optional_function_expression_tree_contains_concrete_f32_value(
function, d,
)
_ => false
}
}
///|
fn WgslIrLowerer::optional_function_expression_tree_contains_concrete_f32_value(
self : WgslIrLowerer,
function : Function,
handle : Handle?,
) -> Bool {
match handle {
Some(handle) =>
self.function_expression_tree_contains_concrete_f32_value(
function, handle,
)
None => false
}
}
///|
fn WgslIrLowerer::constant_is_concrete_f32(
self : WgslIrLowerer,
handle : Handle,
) -> Bool {
match self.shader_module.constants.items.get(handle.index()) {
Some(constant) =>
match self.shader_module.types.get(constant.ty) {
Some({ inner: Scalar({ kind: Float, width: 4, }), .. }) => true
_ => false
}
None => false
}
}
///|
fn wgsl_ir_function_expression_is_computed_float_fold(
function : Function,
handle : Handle,
) -> Bool {
match function.expressions.items.get(handle.index()) {
Some(Literal(_)) => false
Some(_) => true
None => false
}
}
///|
fn WgslIrLowerer::fold_function_constant_float_vector_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> (Handle, Array[Double])? {
guard self.function_expression_tree_contains_constant_reference(
function, handle,
) else {
return None
}
guard self.function_expression_type(function, handle) is Some(ty) else {
return None
}
guard self.shader_module.types.get(ty) is Some(type_) else { return None }
let (size, scalar) = match type_.inner {
Vector(size, scalar) => {
let scalar = match scalar.kind {
AbstractFloat => Scalar::{ kind: Float, width: 4, }
Float => scalar
_ => return None
}
(size, scalar)
}
_ => return None
}
guard self.eval_function_float_vector_expression(function, handle)
is Some(values) else {
return None
}
guard values.length() == size.width() else { return None }
Some((self.vector_type_handle(size, scalar), values))
}
///|
fn WgslIrLowerer::append_function_float_vector_literal_compose(
self : WgslIrLowerer,
function : Function,
ty : Handle,
values : Array[Double],
) -> Handle raise WgslIrLowerError {
guard self.shader_module.types.get(ty) is Some(type_) else {
raise UnsupportedExpression("folded float vector uses missing type")
}
guard type_.inner is Vector(_, scalar) else {
raise UnsupportedExpression("folded float vector uses non-vector type")
}
let components : Array[Handle] = []
for value in values {
components.push(
function.expressions.append_with_origin(
Literal(wgsl_ir_float_literal_for_scalar_width(value, scalar.width)),
FoldedLocalConstant,
),
)
}
function.expressions.append_with_origin(
Compose(ty, components),
FoldedLocalConstant,
)
}
///|
fn WgslIrLowerer::eval_function_float_vector_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Array[Double]? {
match function.expressions.items.get(handle.index()) {
Some(expression) =>
self.eval_function_float_vector_expression_inner(function, expression)
None => None
}
}
///|
fn WgslIrLowerer::eval_function_float_vector_expression_inner(
self : WgslIrLowerer,
function : Function,
expression : Expression,
) -> Array[Double]? {
match expression {
Literal(literal) =>
match wgsl_ir_float_value_from_literal_for_lowerer(literal) {
Some(value) => Some([value])
None => None
}
Constant(handle) =>
match self.eval_constant_float_expression(handle) {
Some(value) => Some([value])
None => {
let integer = Some(self.eval_constant_integer_expression(handle)) catch {
_ => None
}
match integer {
Some(value) => Some([value.to_double()])
None => None
}
}
}
Unary(Negate, inner) =>
match self.eval_function_float_vector_expression(function, inner) {
Some(values) => Some(values.map(fn(value) { -value }))
None => None
}
Splat(size, inner) =>
match self.eval_function_float_expression(function, inner) {
Some(value) => {
let values : Array[Double] = []
for _ in 0.. None
}
Compose(_, components) => {
let values : Array[Double] = []
for component in components {
match self.eval_function_float_expression(function, component) {
Some(value) => values.push(value)
None => return None
}
}
Some(values)
}
Binary(op, left, right) => {
guard self.eval_function_float_vector_expression(function, left)
is Some(left_values) else {
return None
}
guard self.eval_function_float_vector_expression(function, right)
is Some(right_values) else {
return None
}
wgsl_ir_eval_float_vector_binary_for_lowerer(
op, left_values, right_values,
)
}
_ => None
}
}
///|
fn wgsl_ir_float_value_from_literal_for_lowerer(literal : Literal) -> Double? {
match literal {
AbstractFloat(value) => Some(value)
F32(value) | F32Exact(value) => Some(value.to_double())
F16(value) | F64(value) => Some(value)
_ => None
}
}
///|
fn wgsl_ir_eval_float_vector_binary_for_lowerer(
op : BinaryOperator,
left : Array[Double],
right : Array[Double],
) -> Array[Double]? {
let width = if left.length() == right.length() {
left.length()
} else if left.length() == 1 {
right.length()
} else if right.length() == 1 {
left.length()
} else {
return None
}
let values : Array[Double] = []
for index in 0.. values.push(value)
None => return None
}
}
Some(values)
}
///|
fn WgslIrLowerer::function_expression_tree_contains_constant_reference(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Bool {
match function.expressions.items.get(handle.index()) {
Some(expression) =>
self.function_expression_contains_constant_reference(function, expression)
None => false
}
}
///|
fn WgslIrLowerer::function_expression_contains_constant_reference(
self : WgslIrLowerer,
function : Function,
expression : Expression,
) -> Bool {
match expression {
Constant(_) => true
Literal(_)
| Override(_)
| FunctionArgument(_)
| GlobalVariable(_)
| LocalVariable(_)
| Load(_)
| AddressOf(_, _)
| ZeroValue(_)
| FunctionCall(_, _)
| AtomicCall(_, _)
| ImageSample(_, _, _, _, _, _, _, _, _)
| ImageLoad(_, _, _, _, _)
| ImageQuery(_, _)
| CallResult(_)
| AtomicResult(_, _)
| WorkGroupUniformLoadResult(_)
| WorkGroupUniformLoad(_)
| ArrayLength(_)
| RayQueryInitialize(_, _, _)
| RayQueryProceed(_)
| RayQueryGenerateIntersection(_, _)
| RayQueryConfirmIntersection(_)
| RayQueryTerminate(_)
| RayQueryVertexPositions(_, _)
| RayQueryProceedResult
| RayQueryGetIntersection(_, _)
| SubgroupCall(_, _)
| SubgroupBallotResult
| SubgroupOperationResult(_)
| CooperativeLoad(_, _, _, _)
| CooperativeMultiplyAdd(_, _, _) => false
Unary(_, inner)
| Bitcast(_, inner)
| Relational(_, inner)
| As(inner, _, _)
| Splat(_, inner)
| Swizzle(_, inner, _)
| AccessIndex(inner, _)
| Component(inner, _)
| Derivative(_, _, inner) =>
self.function_expression_tree_contains_constant_reference(function, inner)
Access(base, index) | Binary(_, base, index) =>
self.function_expression_tree_contains_constant_reference(function, base) ||
self.function_expression_tree_contains_constant_reference(function, index)
Select(left, right, condition) =>
self.function_expression_tree_contains_constant_reference(function, left) ||
self.function_expression_tree_contains_constant_reference(function, right) ||
self.function_expression_tree_contains_constant_reference(
function, condition,
)
Compose(_, components) =>
components.any(fn(component) {
self.function_expression_tree_contains_constant_reference(
function, component,
)
})
Math(_, first, second, third, fourth) =>
self.function_expression_tree_contains_constant_reference(function, first) ||
self.optional_function_expression_contains_constant_reference(
function, second,
) ||
self.optional_function_expression_contains_constant_reference(
function, third,
) ||
self.optional_function_expression_contains_constant_reference(
function, fourth,
)
}
}
///|
fn WgslIrLowerer::function_binary_expression_should_retain_named_constant(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Bool {
match function.expressions.items.get(handle.index()) {
Some(Binary(Divide, left, right)) =>
self.function_expression_is_named_constant_reference(function, left) &&
wgsl_ir_expression_origin_retains_named_constant_denominator(
function.expressions.origin(right),
)
_ => false
}
}
///|
fn wgsl_ir_expression_origin_retains_named_constant_denominator(
origin : ExpressionOrigin?,
) -> Bool {
match origin {
Some(SourceLocalConstInitializer | RetainedSourceLetExpression) => true
_ => false
}
}
///|
fn WgslIrLowerer::function_expression_is_named_constant_reference(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Bool {
match function.expressions.items.get(handle.index()) {
Some(Constant(constant_handle)) =>
self.shader_module.constants.items.get(constant_handle.index()) != None
_ => false
}
}
///|
fn WgslIrLowerer::optional_function_expression_contains_constant_reference(
self : WgslIrLowerer,
function : Function,
handle : Handle?,
) -> Bool {
match handle {
Some(handle) =>
self.function_expression_tree_contains_constant_reference(
function, handle,
)
None => false
}
}
///|
fn WgslIrLowerer::fold_function_constant_literal_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Literal? raise WgslIrLowerError {
let integer = Some(self.eval_function_integer_expression(function, handle)) catch {
_ => None
}
match integer {
Some(value) => return Some(value.to_abstract_int_literal())
None => ()
}
self.fold_function_constant_float_expression(function, handle)
}
///|
fn WgslIrLowerer::fold_function_constant_typed_literal_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Literal? raise WgslIrLowerError {
let integer = Some(self.eval_function_integer_expression(function, handle)) catch {
_ => None
}
match integer {
Some(value) =>
match self.function_expression_type(function, handle) {
Some(ty) =>
match self.shader_module.types.get(ty) {
Some({ inner: Scalar(scalar), .. }) =>
match scalar.kind {
Sint => return Some(value.to_signed_literal(scalar.width))
Uint => return Some(value.to_unsigned_literal(scalar.width))
AbstractInt => return Some(value.to_abstract_int_literal())
_ => ()
}
_ => ()
}
None => ()
}
None => ()
}
self.fold_function_constant_float_expression(function, handle)
}
///|
fn WgslIrLowerer::inline_constant_literal_for_function(
self : WgslIrLowerer,
handle : Handle,
) -> Literal? raise WgslIrLowerError {
guard self.shader_module.constants.items.get(handle.index()) is Some(constant) else {
return None
}
guard self.shader_module.types.get(constant.ty) is Some(ty) else {
return None
}
guard ty.inner is Scalar(scalar) else { return None }
guard scalar.kind == AbstractInt || scalar.kind == AbstractFloat else {
return None
}
match scalar.kind {
Float =>
match self.eval_constant_float_expression(handle) {
Some(value) =>
if scalar.width == 4 {
Some(F32Exact(Float::from_double(value)))
} else {
Some(wgsl_ir_float_literal_for_scalar_width(value, scalar.width))
}
None => None
}
AbstractFloat =>
match self.eval_constant_float_expression(handle) {
Some(value) => Some(AbstractFloat(value))
None => None
}
Sint =>
Some(
self
.eval_constant_integer_expression(handle)
.to_signed_literal(scalar.width),
)
Uint =>
Some(
self
.eval_constant_integer_expression(handle)
.to_unsigned_literal(scalar.width),
)
AbstractInt =>
Some(
self.eval_constant_integer_expression(handle).to_abstract_int_literal(),
)
Bool => None
}
}
///|
fn WgslIrLowerer::normalized_local_constant_initializer(
self : WgslIrLowerer,
function : Function,
handle : Handle,
expected_ty : Handle?,
) -> Handle raise WgslIrLowerError {
match function.expressions.items.get(handle.index()) {
Some(Literal(literal)) => {
match expected_ty {
Some(ty) =>
match self.shader_module.types.get(ty) {
Some({ inner: Scalar(scalar), .. }) => {
let contextualized = match
(
function.expressions.origin(handle),
scalar.kind,
scalar.width,
literal,
) {
(Some(FoldedLocalConstant), Float, 4, AbstractFloat(value)) =>
F32Exact(Float::from_double(value))
_ => wgsl_ir_contextualize_literal(literal, scalar)
}
function.expressions.set(handle, Literal(contextualized))
}
_ => ()
}
None => ()
}
return handle
}
_ => ()
}
match self.eval_function_float_expression(function, handle) {
Some(value) => {
let literal = self.folded_function_float_literal_for_expression_with_expected(
function, handle, value, expected_ty,
)
return function.expressions.append_with_origin(
Literal(literal),
FoldedLocalConstant,
)
}
None => ()
}
match self.fold_function_constant_float_expression(function, handle) {
Some(literal) =>
function.expressions.append_with_origin(
Literal(literal),
FoldedLocalConstant,
)
None => handle
}
}
///|
fn WgslIrLowerer::fold_function_scalar_constructor(
self : WgslIrLowerer,
ty : Handle,
arguments : Array[Handle],
function : Function,
) -> Literal? raise WgslIrLowerError {
guard arguments is [argument] else { return None }
match self.shader_module.types.get(ty) {
Some({ inner: Scalar(scalar), .. }) =>
self.fold_function_scalar_constructor_argument(scalar, function, argument)
_ => None
}
}
///|
fn WgslIrLowerer::fold_function_scalar_constructor_argument(
self : WgslIrLowerer,
scalar : Scalar,
function : Function,
argument : Handle,
) -> Literal? raise WgslIrLowerError {
match scalar.kind {
Float =>
match self.eval_function_float_expression(function, argument) {
Some(value) =>
Some(wgsl_ir_float_literal_for_scalar_width(value, scalar.width))
None => {
let value = self.eval_function_integer_expression(function, argument)
Some(
wgsl_ir_float_literal_for_scalar_width(
value.to_double(),
scalar.width,
),
)
}
}
Sint =>
match self.eval_function_integer_expression(function, argument) {
value =>
Some(
match scalar.width {
8 => I64(Int64::from_int(value.to_signed_int()))
_ => I32(value.to_signed_int())
},
)
}
Uint =>
match self.eval_function_integer_expression(function, argument) {
value =>
Some(
match scalar.width {
8 => U64(value.to_unsigned().to_uint64())
_ => U32(value.to_unsigned())
},
)
}
Bool | AbstractInt | AbstractFloat => None
}
}
///|
fn WgslIrIntegerValue::to_double(self : WgslIrIntegerValue) -> Double {
match self {
Signed(value) => value.to_double()
Unsigned(value) => value.to_double()
Abstract(value) => value.to_double()
}
}
///|
fn wgsl_ir_float_literal_for_scalar_width(
value : Double,
width : Int32,
) -> Literal {
match width {
2 => F16(value)
8 => F64(value)
_ => F32(Float::from_double(value))
}
}
///|
fn WgslIrLowerer::eval_function_float_expression(
self : WgslIrLowerer,
function : Function,
handle : Handle,
) -> Double? {
match function.expressions.items.get(handle.index()) {
Some(expression) =>
self.eval_function_float_expression_inner(function, expression)
None => None
}
}
///|
fn WgslIrLowerer::eval_function_float_expression_inner(
self : WgslIrLowerer,
function : Function,
expression : Expression,
) -> Double? {
match expression {
Literal(AbstractFloat(value)) => Some(value)
Literal(F32(value) | F32Exact(value)) => Some(value.to_double())
Literal(F16(value)) | Literal(F64(value)) => Some(value)
Constant(handle) => self.eval_constant_float_expression(handle)
Binary(op, left, right) => {
guard self.eval_function_float_expression(function, left)
is Some(left_value) else {
return None
}
guard self.eval_function_float_expression(function, right)
is Some(right_value) else {
return None
}
wgsl_ir_eval_float_binary(op, left_value, right_value)
}
Math(Sqrt, value, None, None, None) => {
guard self.eval_function_float_expression(function, value) is Some(value) else {
return None
}
Some(value.sqrt())
}
_ => None
}
}
///|
fn WgslIrLowerer::eval_constant_float_expression(
self : WgslIrLowerer,
handle : Handle,
) -> Double? {
match self.shader_module.constants.items.get(handle.index()) {
Some(constant) => self.eval_global_float_expression(constant.init)
None => None
}
}
///|
fn WgslIrLowerer::eval_global_float_expression(
self : WgslIrLowerer,
handle : Handle,
) -> Double? {
match self.shader_module.global_expressions.items.get(handle.index()) {
Some(expression) => self.eval_global_float_expression_inner(expression)
None => None
}
}
///|
fn WgslIrLowerer::eval_global_float_expression_inner(
self : WgslIrLowerer,
expression : Expression,
) -> Double? {
match expression {
Literal(AbstractFloat(value)) => Some(value)
Literal(F32(value) | F32Exact(value)) => Some(value.to_double())
Literal(F16(value)) | Literal(F64(value)) => Some(value)
Constant(handle) => self.eval_constant_float_expression(handle)
Binary(op, left, right) => {
guard self.eval_global_float_expression(left) is Some(left_value) else {
return None
}
guard self.eval_global_float_expression(right) is Some(right_value) else {
return None
}
wgsl_ir_eval_float_binary(op, left_value, right_value)
}
_ => None
}
}
///|
fn wgsl_ir_eval_float_binary(
op : BinaryOperator,
left : Double,
right : Double,
) -> Double? {
match op {
Add => Some(left + right)
Subtract => Some(left - right)
Multiply => Some(left * right)
Divide => Some(left / right)
_ => None
}
}