Skip to content

Builtin function database, for automatic conversions #6833

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

Merged
merged 10 commits into from
Apr 2, 2025
126 changes: 126 additions & 0 deletions naga/src/common/diagnostic_debug.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//! Displaying Naga IR terms in debugging output.

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
use crate::common::wgsl::TypeContext;

use crate::proc::TypeResolution;
use crate::{Handle, Scalar, Type, TypeInner, UniqueArena};

use core::fmt;

/// A wrapper for displaying Naga IR terms in debugging output.
///
/// This is like [`DiagnosticDisplay`], but requires weaker context
/// and produces correspondingly lower-fidelity output. For example,
/// this cannot show the override names for override-sized array
/// lengths.
///
/// [`DiagnosticDisplay`]: super::DiagnosticDisplay
pub struct DiagnosticDebug<T>(pub T);

impl fmt::Debug for DiagnosticDebug<(Handle<Type>, &UniqueArena<Type>)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (handle, ctx) = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
ctx.write_type(handle, f)?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
{
let _ = ctx;
write!(f, "{handle:?}")?;
}

Ok(())
}
}

impl fmt::Debug for DiagnosticDebug<(&TypeInner, &UniqueArena<Type>)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (inner, ctx) = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
ctx.write_type_inner(inner, f)?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
{
let _ = ctx;
write!(f, "{inner:?}")?;
}

Ok(())
}
}

impl fmt::Debug for DiagnosticDebug<(&TypeResolution, &UniqueArena<Type>)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (resolution, ctx) = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
ctx.write_type_resolution(resolution, f)?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
{
let _ = ctx;
write!(f, "{resolution:?}")?;
}

Ok(())
}
}

impl fmt::Debug for DiagnosticDebug<Scalar> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let scalar = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
f.write_str(&crate::common::wgsl::TryToWgsl::to_wgsl_for_diagnostics(
scalar,
))?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
write!(f, "{scalar:?}")?;

Ok(())
}
}

pub trait ForDebug: Sized {
/// Format this type using [`core::fmt::Debug`].
///
/// Return a value that implements the [`core::fmt::Debug`] trait
/// by displaying `self` in a language-appropriate way. For
/// example:
///
/// # use naga::common::ForDebug;
/// # let scalar: naga::Scalar = naga::Scalar::F32;
/// log::debug!("My scalar: {:?}", scalar.for_debug());
fn for_debug(self) -> DiagnosticDebug<Self> {
DiagnosticDebug(self)
}
}

impl ForDebug for Scalar {}

pub trait ForDebugWithTypes: Sized {
/// Format this type using [`core::fmt::Debug`].
///
/// Given an arena to look up type handles in, return a value that
/// implements the [`core::fmt::Debug`] trait by displaying `self`
/// in a language-appropriate way. For example:
///
/// # use naga::{Span, Type, TypeInner, Scalar, UniqueArena};
/// # use naga::common::ForDebugWithTypes;
/// # let mut types = UniqueArena::<Type>::default();
/// # let inner = TypeInner::Scalar(Scalar::F32);
/// # let span = Span::UNDEFINED;
/// # let handle = types.insert(Type { name: None, inner }, span);
/// log::debug!("My type: {:?}", handle.for_debug(&types));
fn for_debug(self, types: &UniqueArena<Type>) -> DiagnosticDebug<(Self, &UniqueArena<Type>)> {
DiagnosticDebug((self, types))
}
}

impl ForDebugWithTypes for Handle<Type> {}
impl ForDebugWithTypes for &TypeInner {}
impl ForDebugWithTypes for &TypeResolution {}
117 changes: 117 additions & 0 deletions naga/src/common/diagnostic_display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//! Displaying Naga IR terms in diagnostic output.

use crate::proc::{GlobalCtx, Rule};
use crate::{Handle, Scalar, Type, TypeInner};

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
use crate::common::wgsl::TypeContext;

use core::fmt;

/// A wrapper for displaying Naga IR terms in diagnostic output.
///
/// For some Naga IR type `T`, `DiagnosticDisplay<T>` implements
/// [`core::fmt::Display`] in a way that displays values of type `T`
/// appropriately for diagnostic messages presented to human readers.
///
/// For example, the implementation of [`Display`] for
/// `DiagnosticDisplay<Scalar>` formats the type represented by the
/// given [`Scalar`] appropriately for users.
///
/// Some types like `Handle<Type>` require contextual information like
/// a type arena to be displayed. In such cases, we implement [`Display`]
/// for a type like `DiagnosticDisplay<(Handle<Type>, GlobalCtx)>`, where
/// the [`GlobalCtx`] type provides the necessary context.
///
/// If you only need debugging output, [`DiagnosticDebug`] uses
/// easier-to-obtain context types but still does a good enough job
/// for logging or debugging.
///
/// [`Display`]: core::fmt::Display
/// [`Scalar`]: crate::Scalar
/// [`GlobalCtx`]: crate::proc::GlobalCtx
/// [`DiagnosticDebug`]: super::DiagnosticDebug
///
/// ## Language-sensitive diagnostics
///
/// Diagnostic output ought to depend on the source language from
/// which the IR was produced: diagnostics resulting from processing
/// GLSL code should use GLSL type syntax, for example. That means
/// that `DiagnosticDisplay` ought to include some indication of which
/// notation to use.
///
/// For the moment, only WGSL output is implemented, so
/// `DiagnosticDisplay` lacks any support for this (#7268). However,
/// the plan is that all language-independent code in Naga should use
/// `DiagnosticDisplay` wherever appropriate, such that when its
/// definition is expanded to include some indication of the right
/// source language to use, any use site that does not supply this
/// indication will provoke a compile-time error.
pub struct DiagnosticDisplay<T>(pub T);

impl fmt::Display for DiagnosticDisplay<(Handle<Type>, GlobalCtx<'_>)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (handle, ref ctx) = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
ctx.write_type(handle, f)?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
{
let _ = ctx;
write!(f, "{handle:?}")?;
}

Ok(())
}
}

impl fmt::Display for DiagnosticDisplay<(&TypeInner, GlobalCtx<'_>)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (inner, ref ctx) = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
ctx.write_type_inner(inner, f)?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
{
let _ = ctx;
write!(f, "{inner:?}")?;
}

Ok(())
}
}

impl fmt::Display for DiagnosticDisplay<(&str, &Rule, GlobalCtx<'_>)> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let (name, rule, ref ctx) = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
ctx.write_type_rule(name, rule, f)?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
{
let _ = ctx;
write!(f, "{name}({:?}) -> {:?}", rule.arguments, rule.conclusion)?;
}

Ok(())
}
}

impl fmt::Display for DiagnosticDisplay<Scalar> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let scalar = self.0;

#[cfg(any(feature = "wgsl-in", feature = "wgsl-out"))]
f.write_str(&crate::common::wgsl::TryToWgsl::to_wgsl_for_diagnostics(
scalar,
))?;

#[cfg(not(any(feature = "wgsl-in", feature = "wgsl-out")))]
write!(f, "{scalar:?}")?;

Ok(())
}
}
6 changes: 6 additions & 0 deletions naga/src/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
//! Code common to the front and backends for specific languages.

mod diagnostic_debug;
mod diagnostic_display;
pub mod predeclared;
pub mod wgsl;

pub use diagnostic_debug::{DiagnosticDebug, ForDebug, ForDebugWithTypes};
pub use diagnostic_display::DiagnosticDisplay;

/// Helper function that returns the string corresponding to the [`VectorSize`](crate::VectorSize)
pub const fn vector_size_str(size: crate::VectorSize) -> &'static str {
match size {
Expand Down
33 changes: 33 additions & 0 deletions naga/src/common/predeclared.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//! Generating names for predeclared types.

use crate::ir;

use alloc::format;
use alloc::string::String;

impl ir::PredeclaredType {
pub fn struct_name(&self) -> String {
use crate::PredeclaredType as Pt;
match *self {
Pt::AtomicCompareExchangeWeakResult(scalar) => {
format!(
"__atomic_compare_exchange_result<{:?},{}>",
scalar.kind, scalar.width,
)
}
Pt::ModfResult { size, scalar } => frexp_mod_name("modf", size, scalar),
Pt::FrexpResult { size, scalar } => frexp_mod_name("frexp", size, scalar),
}
}
}

fn frexp_mod_name(function: &str, size: Option<ir::VectorSize>, scalar: ir::Scalar) -> String {
let bits = 8 * scalar.width;
match size {
Some(size) => {
let size = size as u8;
format!("__{function}_result_vec{size}_f{bits}")
}
None => format!("__{function}_result_f{bits}"),
}
}
4 changes: 2 additions & 2 deletions naga/src/common/wgsl/to_wgsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,9 @@ impl TryToWgsl for crate::Scalar {
use crate::Scalar;

Some(match self {
Scalar::F64 => "f64",
Scalar::F32 => "f32",
Scalar::F16 => "f16",
Scalar::F32 => "f32",
Scalar::F64 => "f64",
Scalar::I32 => "i32",
Scalar::U32 => "u32",
Scalar::I64 => "i64",
Expand Down
Loading