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

[naga] Built-in function database #6443

Open
jimblandy opened this issue Oct 22, 2024 · 5 comments
Open

[naga] Built-in function database #6443

jimblandy opened this issue Oct 22, 2024 · 5 comments

Comments

@jimblandy
Copy link
Member

jimblandy commented Oct 22, 2024

To solve #5523, Naga needs information about built-in function argument and result types in a form that the WGSL front end can consume to apply WGSL automatic type conversions.

At the moment, builtins appear in Naga in the following places:

  • The IR has enums like BuiltIn and MathFunction.
  • The typifier module knows the result types.
  • The valid module knows the argument types.
  • The WGSL front end and backend both know the function names.

But automatic conversions apply anywhere that they would turn an ill-typed program into a well-typed program, so one needs argument type information to apply them.

Ideally, the first three should be brought together, and the last two should be unified.

It may be the case that we can keep the WGSL front end innocent of these things, and handle automatic conversions with a processing phase.

@jimblandy
Copy link
Member Author

Note that the WGSL built-in functions that might require their arguments to be converted are a broader category than naga::BuiltIn plus naga::MathFunction. WGSL built-in functions cover the following Naga IR items:

  • Expression::ImageSample
  • Expression::ImageLoad
  • Expression::ImageQuery
  • Expression::Select
  • Expression::Derivative
  • Expression::Relational
  • Expression::Math
  • Statement::Atomic
  • Statement::WorkgroupUniformLoad

@jimblandy
Copy link
Member Author

jimblandy commented Oct 22, 2024

One way to look at this is to say that we need a mapping from these operations to function types. However:

  • Many of those functions are heavily overloaded, so there is not a simple map from identifier to type.
  • Much of the overloading is very regular: many math functions take an AbstractFloat, f16, or f32, or a vector of any size of those types. That would be twelve individual function types, but it's error-prone to express that way and slow to consume.
  • naga::TypeInner does not currently have a variant for function types.
  • naga::TypeInner uses Handle to refer to subtypes, and thus is only meaningful in the context of some specific Module.

I think this means that we want a separate FunctionPattern type that captures overloading in a way that is convenient for driving type-checking and conversion.

Here is a sketch of an argument type (I doubt it would survive an attempt to implement it unchanged):

enum ScalarArgumentPattern {
    /// A specific scalar type.
    Exact(Scalar),

    /// Any scalar of the given kind.
    Kind(ScalarKind),

    /// Any integral type, abstract or concrete.
    AnyInt,

    /// Any floating-point type, abstract or concrete.
    AnyFloat,

    /// Any numeric type, abstract or concrete.
    AnyNumeric,

    /// The same scalar as the n'th argument. Must be a lower-numbered argument.
    Same(usize),
}

enum ArgumentPattern {
    /// A scalar.
    Scalar(ScalarArgumentPattern),

    /// A vector of any length.
    VecN(ScalarArgumentPattern),

    /// A scalar or vector of any length.
    ScalarOrVecN(ScalarArgumentPattern),

    /// The same type as the n'th argument. Must be a lower-numbered argument.
    Same(usize),
}

The WGSL sin function's arguments could be described as:

[
    ArgumentPattern::ScalarOrVecN(ScalarArgumentPattern::AnyFloat)
]

The WGSL clamp function could be described as

[
    ArgumentPattern::ScalarOrVecN(ScalarArgumentPattern::AnyNumeric),
    ArgumentPattern::Same(0),
    ArgumentPattern::Same(0)
]

Using Same(0) for the second and third arguments means that in a specific call, the three arguments must have the same type. This is not the same as simply repeating ScalarOrVecN(AnyNumeric) three times, which would represent a function whose three arguments' types could vary independently.

These two forms both capture a lot of overloads in a single form, and I think it should be simple to consume.

Perhaps the return type could be described using this same type: sin and clamp would return Same(0), for example.

There are some functions with more complex overload patterns, like bitcast, which will probably need a list of permitted patterns.

@teoxoy
Copy link
Member

teoxoy commented Oct 23, 2024

The shape above looks good to me, there are probably more variants we'd need for example for transpose(e: matRxC<T>) -> matCxR<T> but those will come up naturally while building up the actual database.

@jrmuizel
Copy link
Contributor

Here's how they do it in dawn: https://github.com/google/dawn/blob/main/docs/tint/intrinsic_definition_files.md

@jimblandy
Copy link
Member Author

jimblandy commented Oct 23, 2024

Dawn's definition files are nice, but it seems like overkill. They have a whole separate language, which has its own maintenance cost, and build time cost.

If necessary, we could put my Rust types and the data in a separate crate, so that it could be used by other applications.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Todo
Development

No branches or pull requests

3 participants