diff --git a/naga/src/back/glsl/keywords.rs b/naga/src/back/glsl/keywords.rs index 1edd7baacf..b8f620daed 100644 --- a/naga/src/back/glsl/keywords.rs +++ b/naga/src/back/glsl/keywords.rs @@ -1,3 +1,7 @@ +use std::sync::LazyLock; + +use hashbrown::HashSet; + pub const RESERVED_KEYWORDS: &[&str] = &[ // // GLSL 4.6 keywords, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L2004-L2322 @@ -490,3 +494,16 @@ pub const RESERVED_KEYWORDS: &[&str] = &[ super::FREXP_FUNCTION, super::FIRST_INSTANCE_BINDING, ]; + +/// The above set of reserved keywords, turned into a cached HashSet. This saves +/// significant time during [`Namer::reset`](crate::proc::Namer::reset). +/// +/// See for benchmarks. +pub static RESERVED_KEYWORD_SET: LazyLock> = LazyLock::new(|| { + let mut set = HashSet::default(); + set.reserve(RESERVED_KEYWORDS.len()); + for &word in RESERVED_KEYWORDS { + set.insert(word); + } + set +}); diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index 26e7e496e3..e517e84911 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -629,8 +629,7 @@ impl<'a, W: Write> Writer<'a, W> { let mut namer = proc::Namer::default(); namer.reset( module, - keywords::RESERVED_KEYWORDS, - &[], + &keywords::RESERVED_KEYWORD_SET, &[], &[ "gl_", // all GL built-in variables diff --git a/naga/src/back/hlsl/keywords.rs b/naga/src/back/hlsl/keywords.rs index e5be7b6d81..ac28797d8b 100644 --- a/naga/src/back/hlsl/keywords.rs +++ b/naga/src/back/hlsl/keywords.rs @@ -1,3 +1,7 @@ +use std::sync::LazyLock; + +use hashbrown::HashSet; + // When compiling with FXC without strict mode, these keywords are actually case insensitive. // If you compile with strict mode and specify a different casing like "Pass" instead in an identifier, FXC will give this error: // "error X3086: alternate cases for 'pass' are deprecated in strict mode" @@ -912,6 +916,22 @@ pub const TYPES: &[&str] = &{ res }; +/// The above set of reserved keywords, turned into a cached HashSet. This saves +/// significant time during [`Namer::reset`](crate::proc::Namer::reset). +/// +/// See for benchmarks. +pub static RESERVED_SET: LazyLock> = LazyLock::new(|| { + let mut set = HashSet::default(); + set.reserve(RESERVED.len() + TYPES.len()); + for &word in RESERVED { + set.insert(word); + } + for &word in TYPES { + set.insert(word); + } + set +}); + pub const RESERVED_PREFIXES: &[&str] = &[ "__dynamic_buffer_offsets", super::help::IMAGE_STORAGE_LOAD_SCALAR_WRAPPER, diff --git a/naga/src/back/hlsl/writer.rs b/naga/src/back/hlsl/writer.rs index 58fb3fdf70..6cbba65f95 100644 --- a/naga/src/back/hlsl/writer.rs +++ b/naga/src/back/hlsl/writer.rs @@ -140,8 +140,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { self.names.clear(); self.namer.reset( module, - super::keywords::RESERVED, - super::keywords::TYPES, + &super::keywords::RESERVED_SET, super::keywords::RESERVED_CASE_INSENSITIVE, super::keywords::RESERVED_PREFIXES, &mut self.names, diff --git a/naga/src/back/msl/keywords.rs b/naga/src/back/msl/keywords.rs index 674d37067e..2228ef1764 100644 --- a/naga/src/back/msl/keywords.rs +++ b/naga/src/back/msl/keywords.rs @@ -1,3 +1,7 @@ +use std::sync::LazyLock; + +use hashbrown::HashSet; + // MSLS - Metal Shading Language Specification: // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf // @@ -347,3 +351,16 @@ pub const RESERVED: &[&str] = &[ super::writer::NEG_FUNCTION, super::writer::ARGUMENT_BUFFER_WRAPPER_STRUCT, ]; + +/// The above set of reserved keywords, turned into a cached HashSet. This saves +/// significant time during [`Namer::reset`](crate::proc::Namer::reset). +/// +/// See for benchmarks. +pub static RESERVED_SET: LazyLock> = LazyLock::new(|| { + let mut set = HashSet::default(); + set.reserve(RESERVED.len()); + for &word in RESERVED { + set.insert(word); + } + set +}); diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs index 8d2f445c95..8d7fab3dd1 100644 --- a/naga/src/back/msl/writer.rs +++ b/naga/src/back/msl/writer.rs @@ -3798,8 +3798,7 @@ impl Writer { self.names.clear(); self.namer.reset( module, - super::keywords::RESERVED, - &[], + &super::keywords::RESERVED_SET, &[], &[CLAMPED_LOD_LOAD_PREFIX], &mut self.names, diff --git a/naga/src/back/wgsl/writer.rs b/naga/src/back/wgsl/writer.rs index c936244dec..6af268a89c 100644 --- a/naga/src/back/wgsl/writer.rs +++ b/naga/src/back/wgsl/writer.rs @@ -99,10 +99,9 @@ impl Writer { self.names.clear(); self.namer.reset( module, - crate::keywords::wgsl::RESERVED, + &crate::keywords::wgsl::RESERVED_SET, // an identifier must not start with two underscore &[], - &[], &["__", "_naga"], &mut self.names, ); diff --git a/naga/src/keywords/wgsl.rs b/naga/src/keywords/wgsl.rs index 683840dc1f..82be25ed4d 100644 --- a/naga/src/keywords/wgsl.rs +++ b/naga/src/keywords/wgsl.rs @@ -4,6 +4,10 @@ Keywords for [WGSL][wgsl] (WebGPU Shading Language). [wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html */ +use std::sync::LazyLock; + +use hashbrown::HashSet; + // https://gpuweb.github.io/gpuweb/wgsl/#keyword-summary // last sync: https://github.com/gpuweb/gpuweb/blob/39f2321f547c8f0b7f473cf1d47fba30b1691303/wgsl/index.bs pub const RESERVED: &[&str] = &[ @@ -229,3 +233,16 @@ pub const RESERVED: &[&str] = &[ "writeonly", "yield", ]; + +/// The above set of reserved keywords, turned into a cached HashSet. This saves +/// significant time during [`Namer::reset`](crate::proc::Namer::reset). +/// +/// See for benchmarks. +pub static RESERVED_SET: LazyLock> = LazyLock::new(|| { + let mut set = HashSet::default(); + set.reserve(RESERVED.len()); + for &word in RESERVED { + set.insert(word); + } + set +}); diff --git a/naga/src/lib.rs b/naga/src/lib.rs index 8b74c00a92..03587a5fc5 100644 --- a/naga/src/lib.rs +++ b/naga/src/lib.rs @@ -251,7 +251,18 @@ An override expression can be evaluated at pipeline creation time. )] #![no_std] -#[cfg(any(test, spv_out, feature = "spv-in", feature = "wgsl-in"))] +#[cfg(any( + test, + spv_out, + + // Need OnceLock + hlsl_out, + msl_out, + wgsl_out, + + feature = "spv-in", + feature = "wgsl-in" +))] extern crate std; extern crate alloc; diff --git a/naga/src/proc/namer.rs b/naga/src/proc/namer.rs index e7f3974a44..6b831cce79 100644 --- a/naga/src/proc/namer.rs +++ b/naga/src/proc/namer.rs @@ -5,6 +5,7 @@ use alloc::{ vec::Vec, }; use core::hash::{Hash, Hasher}; +use hashbrown::HashSet; use crate::{arena::Handle, FastHashMap, FastHashSet}; @@ -27,15 +28,29 @@ pub enum NameKey { /// This processor assigns names to all the things in a module /// that may need identifiers in a textual backend. -#[derive(Default)] pub struct Namer { /// The last numeric suffix used for each base name. Zero means "no suffix". unique: FastHashMap, - keywords: FastHashSet<&'static str>, + keywords: &'static HashSet<&'static str>, keywords_case_insensitive: FastHashSet>, reserved_prefixes: Vec<&'static str>, } +#[cfg(any(wgsl_out, glsl_out, msl_out, hlsl_out, test))] +impl Default for Namer { + fn default() -> Self { + use std::sync::LazyLock; + static DEFAULT_KEYWORDS: LazyLock> = LazyLock::new(HashSet::default); + + Self { + unique: Default::default(), + keywords: &DEFAULT_KEYWORDS, + keywords_case_insensitive: Default::default(), + reserved_prefixes: Default::default(), + } + } +} + impl Namer { /// Return a form of `string` suitable for use as the base of an identifier. /// @@ -157,8 +172,7 @@ impl Namer { pub fn reset( &mut self, module: &crate::Module, - reserved_keywords: &[&'static str], - extra_reserved_keywords: &[&'static str], + reserved_keywords: &'static HashSet<&'static str>, reserved_keywords_case_insensitive: &[&'static str], reserved_prefixes: &[&'static str], output: &mut FastHashMap, @@ -167,9 +181,7 @@ impl Namer { self.reserved_prefixes.extend(reserved_prefixes.iter()); self.unique.clear(); - self.keywords.clear(); - self.keywords.extend(reserved_keywords.iter()); - self.keywords.extend(extra_reserved_keywords.iter()); + self.keywords = reserved_keywords; debug_assert!(reserved_keywords_case_insensitive .iter()