From ba8630c4ca4231558307fdc2b90baa02dc08e30f Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 4 Aug 2026 13:56:59 +0200 Subject: [PATCH 1/3] spirv-std-nightly: add crate `spirv-std-nightly` --- Cargo.lock | 9 +++++++++ Cargo.toml | 2 ++ crates/spirv-std/nightly/Cargo.toml | 23 +++++++++++++++++++++++ crates/spirv-std/nightly/README.md | 5 +++++ crates/spirv-std/nightly/src/lib.rs | 2 ++ tests/compiletests/deps-helper/Cargo.toml | 1 + tests/compiletests/src/main.rs | 1 + 7 files changed, 43 insertions(+) create mode 100644 crates/spirv-std/nightly/Cargo.toml create mode 100644 crates/spirv-std/nightly/README.md create mode 100644 crates/spirv-std/nightly/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index d42d6f26b2a..b221c0cbd3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -749,6 +749,7 @@ name = "compiletests-deps-helper" version = "0.0.0" dependencies = [ "spirv-std", + "spirv-std-nightly", ] [[package]] @@ -4670,6 +4671,14 @@ dependencies = [ "syn 3.0.3", ] +[[package]] +name = "spirv-std-nightly" +version = "0.10.0-alpha.1" +dependencies = [ + "spirv-std", + "spirv-std-macros", +] + [[package]] name = "spirv-std-types" version = "0.10.0-alpha.1" diff --git a/Cargo.toml b/Cargo.toml index b566b0f6d32..6d26ed1804c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ members = [ "crates/rustc_codegen_spirv-types", "crates/spirv-builder", "crates/spirv-std", + "crates/spirv-std/nightly", "crates/spirv-std/shared", "crates/spirv-std/macros", "crates/cargo-gpu", @@ -57,6 +58,7 @@ unexpected_cfgs = { level = "allow", check-cfg = ['cfg(target_arch, values("spir [workspace.dependencies] spirv-builder = { path = "./crates/spirv-builder", version = "=0.10.0-alpha.1", default-features = false } spirv-std = { path = "./crates/spirv-std", version = "=0.10.0-alpha.1" } +spirv-std-nightly = { path = "./crates/spirv-std/nightly", version = "=0.10.0-alpha.1" } spirv-std-types = { path = "./crates/spirv-std/shared", version = "=0.10.0-alpha.1" } spirv-std-macros = { path = "./crates/spirv-std/macros", version = "=0.10.0-alpha.1" } spirv-tools = { version = "0.13.0", default-features = false } diff --git a/crates/spirv-std/nightly/Cargo.toml b/crates/spirv-std/nightly/Cargo.toml new file mode 100644 index 00000000000..60974f49b5e --- /dev/null +++ b/crates/spirv-std/nightly/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "spirv-std-nightly" +description = "spirv-std but with nightly-only features like adt_const_params" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[dependencies] +spirv-std.workspace = true +spirv-std-macros.workspace = true + +[features] +default = ["spirv-std/default"] +bytemuck = ["spirv-std/bytemuck"] +glam_0_33 = ["spirv-std/glam_0_33"] +glam_0_32 = ["spirv-std/glam_0_32"] +glam_0_31 = ["spirv-std/glam_0_31"] +glam_0_30 = ["spirv-std/glam_0_30"] diff --git a/crates/spirv-std/nightly/README.md b/crates/spirv-std/nightly/README.md new file mode 100644 index 00000000000..56fc698960d --- /dev/null +++ b/crates/spirv-std/nightly/README.md @@ -0,0 +1,5 @@ +# `spirv-std-nightly` + +Like [`spirv-std`](https://crates.io/crates/spirv-std), exposes the "standard library" for [rust-gpu](https://github.com/rust-gpu/rust-gpu#readme) SPIR-V shaders, but replaces most functions to use the nightly-only [`adt_const_params`](https://doc.rust-lang.org/beta/unstable-book/language-features/adt-const-params.html) feature. + +Since rust-gpu requires you to use a specific nightly anyway, this isn't much of an issue for crates that are only used within shaders. However, it may cause trouble in setups where crates are shared between GPU and CPU and the CPU side compiled with stable rustc, so const generics have been moved to this separate crate. diff --git a/crates/spirv-std/nightly/src/lib.rs b/crates/spirv-std/nightly/src/lib.rs new file mode 100644 index 00000000000..c90805d60bd --- /dev/null +++ b/crates/spirv-std/nightly/src/lib.rs @@ -0,0 +1,2 @@ +#![no_std] +pub use spirv_std::*; diff --git a/tests/compiletests/deps-helper/Cargo.toml b/tests/compiletests/deps-helper/Cargo.toml index 4b4e2d44425..a75b625694f 100644 --- a/tests/compiletests/deps-helper/Cargo.toml +++ b/tests/compiletests/deps-helper/Cargo.toml @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] spirv-std = { workspace = true } +spirv-std-nightly = { workspace = true } [package.metadata.release] release = false diff --git a/tests/compiletests/src/main.rs b/tests/compiletests/src/main.rs index 1b3a37defd7..0b203cd503f 100644 --- a/tests/compiletests/src/main.rs +++ b/tests/compiletests/src/main.rs @@ -207,6 +207,7 @@ impl Runner { self.find_lib("core", DepKind::SpirvLib, target)? .no_prelude(), self.find_lib("spirv-std", DepKind::SpirvLib, target)?, + self.find_lib("spirv-std-nightly", DepKind::SpirvLib, target)?, self.find_lib("glam", DepKind::SpirvLib, target)?, self.find_lib("spirv-std-macros", DepKind::ProcMacro, target)?, ]) From 34778befe2edd87697f036b20d2e8d483e797ee2 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 4 Aug 2026 13:56:59 +0200 Subject: [PATCH 2/3] spirv-std-nightly: impl intrinsics with const generics, `generic_const_exprs` feature is failing --- crates/spirv-std/Cargo.toml | 3 +- crates/spirv-std/nightly/Cargo.toml | 2 +- crates/spirv-std/nightly/src/arch.rs | 28 +++ crates/spirv-std/nightly/src/arch/atomics.rs | 227 ++++++++++++++++++ crates/spirv-std/nightly/src/arch/barrier.rs | 20 ++ crates/spirv-std/nightly/src/lib.rs | 4 + crates/spirv-std/src/lib.rs | 1 + crates/spirv-std/src/memory.rs | 33 ++- .../memory_barrier_invalid_ordering.rs | 19 ++ .../memory_barrier_invalid_ordering.stderr | 26 ++ .../ui/spirv-std-nightly/spirv-std-nightly.rs | 24 ++ 11 files changed, 379 insertions(+), 8 deletions(-) create mode 100644 crates/spirv-std/nightly/src/arch.rs create mode 100644 crates/spirv-std/nightly/src/arch/atomics.rs create mode 100644 crates/spirv-std/nightly/src/arch/barrier.rs create mode 100644 tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.rs create mode 100644 tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.stderr create mode 100644 tests/compiletests/ui/spirv-std-nightly/spirv-std-nightly.rs diff --git a/crates/spirv-std/Cargo.toml b/crates/spirv-std/Cargo.toml index 1a8e3785db0..a16e326c907 100644 --- a/crates/spirv-std/Cargo.toml +++ b/crates/spirv-std/Cargo.toml @@ -37,8 +37,9 @@ glam_0_30 = { package = "glam", version = "0.30.8", optional = true, default-fea [features] default = ["glam_0_33"] -bytemuck = ["dep:bytemuck", "bitflags/bytemuck", "glam_0_30?/bytemuck", "glam_0_31?/bytemuck", "glam_0_32?/bytemuck", "glam_0_33?/bytemuck"] +bytemuck = ["dep:bytemuck", "glam_0_30?/bytemuck", "glam_0_31?/bytemuck", "glam_0_32?/bytemuck", "glam_0_33?/bytemuck"] glam_0_33 = ["dep:glam_0_33"] glam_0_32 = ["dep:glam_0_32"] glam_0_31 = ["dep:glam_0_31"] glam_0_30 = ["dep:glam_0_30"] +adt_const_params = [] diff --git a/crates/spirv-std/nightly/Cargo.toml b/crates/spirv-std/nightly/Cargo.toml index 60974f49b5e..370bbd1bfea 100644 --- a/crates/spirv-std/nightly/Cargo.toml +++ b/crates/spirv-std/nightly/Cargo.toml @@ -11,7 +11,7 @@ repository.workspace = true workspace = true [dependencies] -spirv-std.workspace = true +spirv-std = { workspace = true, features = ["adt_const_params"] } spirv-std-macros.workspace = true [features] diff --git a/crates/spirv-std/nightly/src/arch.rs b/crates/spirv-std/nightly/src/arch.rs new file mode 100644 index 00000000000..09b522e6ba4 --- /dev/null +++ b/crates/spirv-std/nightly/src/arch.rs @@ -0,0 +1,28 @@ +mod atomics; +mod barrier; + +// name overwritten functions explicitly to overrule glob import of the rest +pub use atomics::{ + atomic_compare_exchange, atomic_exchange, atomic_f_add, atomic_f_max, atomic_f_min, + atomic_i_add, atomic_i_decrement, atomic_i_increment, atomic_i_sub, atomic_load, atomic_s_max, + atomic_s_min, atomic_store, atomic_u_max, atomic_u_min, +}; +pub use barrier::{control_barrier, memory_barrier}; +pub use spirv_std::arch::*; + +use crate::glam::UVec2; +use spirv_std::memory::Scope; + +/// See [`spirv_std::arch::read_clock_khr`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpReadClockKHR")] +pub fn read_clock_khr() -> u64 { + spirv_std::arch::read_clock_khr::<{ SCOPE as u32 }>() +} + +/// See [`spirv_std::arch::read_clock_uvec2_khr`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpReadClockKHR")] +pub fn read_clock_uvec2_khr() -> UVec2 { + spirv_std::arch::read_clock_uvec2_khr::<{ SCOPE as u32 }>() +} diff --git a/crates/spirv-std/nightly/src/arch/atomics.rs b/crates/spirv-std/nightly/src/arch/atomics.rs new file mode 100644 index 00000000000..52915f07def --- /dev/null +++ b/crates/spirv-std/nightly/src/arch/atomics.rs @@ -0,0 +1,227 @@ +use crate::memory::{Scope, Semantics}; +use crate::{Float, Integer, Number, SignedInteger, UnsignedInteger}; + +/// See [`spirv_std::arch::atomic_load`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicLoad")] +#[inline] +pub unsafe fn atomic_load(ptr: &N) -> N { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_load::(ptr) +} + +/// See [`spirv_std::arch::atomic_store`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicStore")] +#[inline] +pub unsafe fn atomic_store( + ptr: &mut N, + value: N, +) { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_store::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_exchange`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicExchange")] +#[inline] +pub unsafe fn atomic_exchange( + ptr: &mut N, + value: N, +) -> N { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_exchange::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_compare_exchange`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicCompareExchange")] +#[inline] +pub unsafe fn atomic_compare_exchange< + I: Integer, + const SCOPE: Scope, + const EQUAL: Semantics, + const UNEQUAL: Semantics, +>( + ptr: &mut I, + value: I, + comparator: I, +) -> I { + const { + EQUAL.assert_valid(); + UNEQUAL.assert_valid(); + } + spirv_std::arch::atomic_compare_exchange::< + I, + { SCOPE as u32 }, + { EQUAL as u32 }, + { UNEQUAL as u32 }, + >(ptr, value, comparator) +} + +/// See [`spirv_std::arch::atomic_i_increment`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicIIncrement")] +#[inline] +pub unsafe fn atomic_i_increment( + ptr: &mut I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_i_increment::(ptr) +} + +/// See [`spirv_std::arch::atomic_i_decrement`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicIDecrement")] +#[inline] +pub unsafe fn atomic_i_decrement( + ptr: &mut I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_i_decrement::(ptr) +} + +/// See [`spirv_std::arch::atomic_i_add`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicIAdd")] +#[inline] +pub unsafe fn atomic_i_add( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_i_add::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_i_sub`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicISub")] +#[inline] +pub unsafe fn atomic_i_sub( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_i_sub::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_s_min`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicSMin")] +#[inline] +pub unsafe fn atomic_s_min( + ptr: &mut S, + value: S, +) -> S { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_s_min::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_u_min`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicUMin")] +#[inline] +pub unsafe fn atomic_u_min( + ptr: &mut U, + value: U, +) -> U { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_u_min::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_s_max`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicSMax")] +#[inline] +pub unsafe fn atomic_s_max( + ptr: &mut S, + value: S, +) -> S { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_s_max::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_u_max`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicUMax")] +#[inline] +pub unsafe fn atomic_u_max( + ptr: &mut U, + value: U, +) -> U { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_u_max::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_and`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicAnd")] +#[inline] +pub unsafe fn atomic_and( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_and::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_or`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicOr")] +#[inline] +pub unsafe fn atomic_or( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_or::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_xor`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicXor")] +#[inline] +pub unsafe fn atomic_xor( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_xor::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_f_min`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicFMinEXT")] +#[inline] +pub unsafe fn atomic_f_min( + ptr: &mut F, + value: F, +) -> F { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_f_min::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_f_max`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicFMaxEXT")] +#[inline] +pub unsafe fn atomic_f_max( + ptr: &mut F, + value: F, +) -> F { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_f_max::(ptr, value) +} + +/// See [`spirv_std::arch::atomic_f_add`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicFAddEXT")] +#[inline] +pub unsafe fn atomic_f_add( + ptr: &mut F, + value: F, +) -> F { + const { SEMANTICS.assert_valid() } + spirv_std::arch::atomic_f_add::(ptr, value) +} diff --git a/crates/spirv-std/nightly/src/arch/barrier.rs b/crates/spirv-std/nightly/src/arch/barrier.rs new file mode 100644 index 00000000000..d417fa0e1f9 --- /dev/null +++ b/crates/spirv-std/nightly/src/arch/barrier.rs @@ -0,0 +1,20 @@ +use spirv_std::memory::{Scope, Semantics}; + +/// See [`spirv_std::arch::control_barrier`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpControlBarrier")] +#[inline] +pub fn control_barrier() { + const { SEMANTICS.assert_valid() } + spirv_std::arch::control_barrier::<{ EXECUTION as u32 }, { MEMORY as u32 }, { SEMANTICS.bits() }>( + ) +} + +/// See [`spirv_std::arch::memory_barrier`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpMemoryBarrier")] +#[inline] +pub fn memory_barrier() { + const { SEMANTICS.assert_valid() } + spirv_std::arch::memory_barrier::<{ MEMORY as u32 }, { SEMANTICS.bits() }>() +} diff --git a/crates/spirv-std/nightly/src/lib.rs b/crates/spirv-std/nightly/src/lib.rs index c90805d60bd..c8f93494fa5 100644 --- a/crates/spirv-std/nightly/src/lib.rs +++ b/crates/spirv-std/nightly/src/lib.rs @@ -1,2 +1,6 @@ #![no_std] +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] + +pub mod arch; pub use spirv_std::*; diff --git a/crates/spirv-std/src/lib.rs b/crates/spirv-std/src/lib.rs index 288432b2913..4b451c9abc1 100644 --- a/crates/spirv-std/src/lib.rs +++ b/crates/spirv-std/src/lib.rs @@ -4,6 +4,7 @@ allow(internal_features), feature(asm_experimental_arch, lang_items) )] +#![cfg_attr(feature = "adt_const_params", feature(adt_const_params))] // FIXME(eddyb) update/review these lints. // // BEGIN - Embark standard lints v0.4 diff --git a/crates/spirv-std/src/memory.rs b/crates/spirv-std/src/memory.rs index e9fbc4c5819..5496819ec21 100644 --- a/crates/spirv-std/src/memory.rs +++ b/crates/spirv-std/src/memory.rs @@ -6,6 +6,8 @@ /// Specification for how large of a scope some instructions should operate on - used when calling /// functions that take a configurable scope. #[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "adt_const_params", derive(core::marker::ConstParamTy))] +#[repr(u32)] pub enum Scope { /// Crosses multiple devices. CrossDevice = 0, @@ -26,13 +28,16 @@ pub enum Scope { QueueFamily = 5, } +/// Memory semantics to determine how some operations should function - used when calling such +/// configurable operations. +#[repr(transparent)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))] +#[cfg_attr(feature = "adt_const_params", derive(core::marker::ConstParamTy))] +pub struct Semantics(u32); + bitflags::bitflags! { - /// Memory semantics to determine how some operations should function - used when calling such - /// configurable operations. - #[repr(transparent)] - #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] - #[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))] - pub struct Semantics: u32 { + impl Semantics: u32 { /// No memory semantics. const NONE = 0; @@ -102,6 +107,22 @@ bitflags::bitflags! { } } +impl Semantics { + const ORDERING_MASK: Semantics = Self::ACQUIRE + .union(Self::RELEASE) + .union(Self::ACQUIRE_RELEASE) + .union(Self::SEQUENTIALLY_CONST); + + /// Verify whether the [`Semantics`] flags are valid + pub const fn assert_valid(self) { + assert!( + self.intersection(Self::ORDERING_MASK).bits().count_ones() <= 1, + "at most one memory-ordering flag (ACQUIRE, RELEASE, ACQUIRE_RELEASE or \ + SEQUENTIALLY_CONST) may be set" + ); + } +} + #[cfg(all(test, feature = "bytemuck"))] mod test_bytemuck { fn is_pod() {} diff --git a/tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.rs b/tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.rs new file mode 100644 index 00000000000..2fe68c355ad --- /dev/null +++ b/tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.rs @@ -0,0 +1,19 @@ +// build-fail +// normalize-stderr-test "\S*crates/spirv-std/" -> "$$SPIRV_STD/" +// normalize-stderr-test "\S*/lib/rustlib/" -> "$$SYSROOT/lib/rustlib/" +// normalize-stderr-test "\$SYSROOT/lib/rustlib/src/rust/library/core/src/panic.rs:\d+:\d+" -> "$$SYSROOT/lib/rustlib/src/rust/library/core/src/panic.rs:LL:CC" + +#![feature(adt_const_params)] + +use spirv_std_nightly::arch::memory_barrier; +use spirv_std_nightly::memory::{Scope, Semantics}; +use spirv_std_nightly::spirv; + +#[spirv(fragment)] +pub fn main() { + unsafe { + // ACQUIRE and RELEASE are both memory-ordering flags, and the SPIR-V spec allows at + // most one to be set at the same time. + memory_barrier::<{ Scope::Subgroup }, { Semantics::ACQUIRE.union(Semantics::RELEASE) }>(); + } +} diff --git a/tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.stderr b/tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.stderr new file mode 100644 index 00000000000..85cf8bce857 --- /dev/null +++ b/tests/compiletests/ui/spirv-std-nightly/memory_barrier_invalid_ordering.stderr @@ -0,0 +1,26 @@ +error[E0080]: evaluation panicked: at most one memory-ordering flag (ACQUIRE, RELEASE, ACQUIRE_RELEASE or SEQUENTIALLY_CONST) may be set + --> $SPIRV_STD/nightly/src/arch/barrier.rs:30:12 + | + = note: evaluation of `spirv_std_nightly::arch::memory_barrier::::{constant#0}` failed inside this call +note: inside `Semantics::assert_valid` + --> $SYSROOT/lib/rustlib/src/rust/library/core/src/panic.rs:LL:CC + | +LL | $crate::panicking::panic_fmt($crate::const_format_args!($($t)+)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + --> $SPIRV_STD/src/memory.rs:118:8 + ::: $SPIRV_STD/src/memory.rs:122:9 + | + = note: in this macro invocation + +note: erroneous constant encountered + --> $SPIRV_STD/nightly/src/arch/barrier.rs:30:4 + +note: the above error was encountered while instantiating `fn memory_barrier::` + --> $DIR/memory_barrier_invalid_ordering.rs:17:9 + | +LL | memory_barrier::<{ Scope::Subgroup }, { Semantics::ACQUIRE.union(Semantics::RELEASE) }>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/compiletests/ui/spirv-std-nightly/spirv-std-nightly.rs b/tests/compiletests/ui/spirv-std-nightly/spirv-std-nightly.rs new file mode 100644 index 00000000000..f9ed63572fa --- /dev/null +++ b/tests/compiletests/ui/spirv-std-nightly/spirv-std-nightly.rs @@ -0,0 +1,24 @@ +// build-pass +// compile-flags: -C target-feature=+VulkanMemoryModelDeviceScopeKHR,+ext:SPV_KHR_vulkan_memory_model + +#![feature(adt_const_params)] + +use spirv_std_nightly::arch::atomic_i_add; +use spirv_std_nightly::arch::memory_barrier; +use spirv_std_nightly::memory::{Scope, Semantics}; +use spirv_std_nightly::spirv; + +#[spirv(fragment)] +pub fn main(#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] atomic_place: &mut u32) { + unsafe { + memory_barrier::< + { Scope::Subgroup }, + { + Semantics::ACQUIRE + .union(Semantics::IMAGE_MEMORY) + .union(Semantics::UNIFORM_MEMORY) + }, + >(); + atomic_i_add::<_, { Scope::Device }, { Semantics::NONE }>(atomic_place, 12); + } +} From c2fc182fa0544ade8e221c9b3e61dee290053f1c Mon Sep 17 00:00:00 2001 From: firestar99 Date: Tue, 4 Aug 2026 14:54:25 +0200 Subject: [PATCH 3/3] spirv-std-nightly: emit inline asm directly, to work around failing `generic_const_exprs` --- crates/spirv-std/nightly/src/arch.rs | 27 +- crates/spirv-std/nightly/src/arch/atomics.rs | 326 +++++++++++++++++-- crates/spirv-std/nightly/src/arch/barrier.rs | 27 +- crates/spirv-std/nightly/src/lib.rs | 2 +- 4 files changed, 353 insertions(+), 29 deletions(-) diff --git a/crates/spirv-std/nightly/src/arch.rs b/crates/spirv-std/nightly/src/arch.rs index 09b522e6ba4..09835c417f3 100644 --- a/crates/spirv-std/nightly/src/arch.rs +++ b/crates/spirv-std/nightly/src/arch.rs @@ -11,18 +11,41 @@ pub use barrier::{control_barrier, memory_barrier}; pub use spirv_std::arch::*; use crate::glam::UVec2; +#[cfg(target_arch = "spirv")] +use core::arch::asm; use spirv_std::memory::Scope; /// See [`spirv_std::arch::read_clock_khr`] #[spirv_std_macros::gpu_only] #[doc(alias = "OpReadClockKHR")] pub fn read_clock_khr() -> u64 { - spirv_std::arch::read_clock_khr::<{ SCOPE as u32 }>() + unsafe { + let mut result: u64; + asm! { + "%uint = OpTypeInt 32 0", + "%scope = OpConstant %uint {scope}", + "{result} = OpReadClockKHR typeof*{result} %scope", + result = out(reg) result, + scope = const SCOPE as u32, + }; + result + } } /// See [`spirv_std::arch::read_clock_uvec2_khr`] #[spirv_std_macros::gpu_only] #[doc(alias = "OpReadClockKHR")] pub fn read_clock_uvec2_khr() -> UVec2 { - spirv_std::arch::read_clock_uvec2_khr::<{ SCOPE as u32 }>() + unsafe { + let mut result = UVec2::default(); + asm! { + "%uint = OpTypeInt 32 0", + "%scope = OpConstant %uint {scope}", + "%result = OpReadClockKHR typeof*{result} %scope", + "OpStore {result} %result", + result = in(reg) &mut result, + scope = const SCOPE as u32, + }; + result + } } diff --git a/crates/spirv-std/nightly/src/arch/atomics.rs b/crates/spirv-std/nightly/src/arch/atomics.rs index 52915f07def..79498ffcb1d 100644 --- a/crates/spirv-std/nightly/src/arch/atomics.rs +++ b/crates/spirv-std/nightly/src/arch/atomics.rs @@ -1,5 +1,7 @@ use crate::memory::{Scope, Semantics}; use crate::{Float, Integer, Number, SignedInteger, UnsignedInteger}; +#[cfg(target_arch = "spirv")] +use core::arch::asm; /// See [`spirv_std::arch::atomic_load`] #[spirv_std_macros::gpu_only] @@ -7,7 +9,21 @@ use crate::{Float, Integer, Number, SignedInteger, UnsignedInteger}; #[inline] pub unsafe fn atomic_load(ptr: &N) -> N { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_load::(ptr) + unsafe { + let mut result = N::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%result = OpAtomicLoad _ {ptr} %scope %semantics", + "OpStore {result} %result", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + result = in(reg) &mut result + } + result + } } /// See [`spirv_std::arch::atomic_store`] @@ -19,7 +35,19 @@ pub unsafe fn atomic_store(ptr, value) + unsafe { + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "OpAtomicStore {ptr} %scope %semantics %value", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + value = in(reg) &value + } + } } /// See [`spirv_std::arch::atomic_exchange`] @@ -31,7 +59,23 @@ pub unsafe fn atomic_exchange N { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_exchange::(ptr, value) + unsafe { + let mut old = N::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicExchange _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_compare_exchange`] @@ -52,12 +96,27 @@ pub unsafe fn atomic_compare_exchange< EQUAL.assert_valid(); UNEQUAL.assert_valid(); } - spirv_std::arch::atomic_compare_exchange::< - I, - { SCOPE as u32 }, - { EQUAL as u32 }, - { UNEQUAL as u32 }, - >(ptr, value, comparator) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%equal = OpConstant %u32 {equal}", + "%unequal = OpConstant %u32 {unequal}", + "%value = OpLoad _ {value}", + "%comparator = OpLoad _ {comparator}", + "%old = OpAtomicCompareExchange _ {ptr} %scope %equal %unequal %value %comparator", + "OpStore {old} %old", + scope = const SCOPE as u32, + equal = const EQUAL.bits(), + unequal = const UNEQUAL.bits(), + ptr = in(reg) ptr, + value = in(reg) &value, + comparator = in(reg) &comparator, + old = in(reg) &mut old, + } + old + } } /// See [`spirv_std::arch::atomic_i_increment`] @@ -68,7 +127,21 @@ pub unsafe fn atomic_i_increment I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_i_increment::(ptr) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%old = OpAtomicIIncrement _ {ptr} %scope %semantics", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old + } + old + } } /// See [`spirv_std::arch::atomic_i_decrement`] @@ -79,7 +152,21 @@ pub unsafe fn atomic_i_decrement I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_i_decrement::(ptr) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%old = OpAtomicIDecrement _ {ptr} %scope %semantics", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old + } + old + } } /// See [`spirv_std::arch::atomic_i_add`] @@ -91,7 +178,23 @@ pub unsafe fn atomic_i_add I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_i_add::(ptr, value) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicIAdd _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_i_sub`] @@ -103,7 +206,23 @@ pub unsafe fn atomic_i_sub I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_i_sub::(ptr, value) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicISub _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_s_min`] @@ -115,7 +234,23 @@ pub unsafe fn atomic_s_min S { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_s_min::(ptr, value) + unsafe { + let mut old = S::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicSMin _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_u_min`] @@ -127,7 +262,23 @@ pub unsafe fn atomic_u_min U { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_u_min::(ptr, value) + unsafe { + let mut old = U::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicUMin _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_s_max`] @@ -139,7 +290,23 @@ pub unsafe fn atomic_s_max S { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_s_max::(ptr, value) + unsafe { + let mut old = S::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicSMax _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_u_max`] @@ -151,7 +318,23 @@ pub unsafe fn atomic_u_max U { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_u_max::(ptr, value) + unsafe { + let mut old = U::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicUMax _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_and`] @@ -163,7 +346,23 @@ pub unsafe fn atomic_and I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_and::(ptr, value) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicAnd _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_or`] @@ -175,7 +374,23 @@ pub unsafe fn atomic_or I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_or::(ptr, value) + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicOr _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_xor`] @@ -187,7 +402,24 @@ pub unsafe fn atomic_xor I { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_xor::(ptr, value) + + unsafe { + let mut old = I::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicXor _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_f_min`] @@ -199,7 +431,23 @@ pub unsafe fn atomic_f_min F { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_f_min::(ptr, value) + unsafe { + let mut old = F::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicFMinEXT _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_f_max`] @@ -211,7 +459,23 @@ pub unsafe fn atomic_f_max F { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_f_max::(ptr, value) + unsafe { + let mut old = F::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicFMaxEXT _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } /// See [`spirv_std::arch::atomic_f_add`] @@ -223,5 +487,21 @@ pub unsafe fn atomic_f_add F { const { SEMANTICS.assert_valid() } - spirv_std::arch::atomic_f_add::(ptr, value) + unsafe { + let mut old = F::default(); + asm! { + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%value = OpLoad _ {value}", + "%old = OpAtomicFAddEXT _ {ptr} %scope %semantics %value", + "OpStore {old} %old", + scope = const SCOPE as u32, + semantics = const SEMANTICS.bits(), + ptr = in(reg) ptr, + old = in(reg) &mut old, + value = in(reg) &value + } + old + } } diff --git a/crates/spirv-std/nightly/src/arch/barrier.rs b/crates/spirv-std/nightly/src/arch/barrier.rs index d417fa0e1f9..ee2981d9650 100644 --- a/crates/spirv-std/nightly/src/arch/barrier.rs +++ b/crates/spirv-std/nightly/src/arch/barrier.rs @@ -1,3 +1,5 @@ +#[cfg(target_arch = "spirv")] +use core::arch::asm; use spirv_std::memory::{Scope, Semantics}; /// See [`spirv_std::arch::control_barrier`] @@ -6,8 +8,18 @@ use spirv_std::memory::{Scope, Semantics}; #[inline] pub fn control_barrier() { const { SEMANTICS.assert_valid() } - spirv_std::arch::control_barrier::<{ EXECUTION as u32 }, { MEMORY as u32 }, { SEMANTICS.bits() }>( - ) + unsafe { + asm! { + "%u32 = OpTypeInt 32 0", + "%execution = OpConstant %u32 {execution}", + "%memory = OpConstant %u32 {memory}", + "%semantics = OpConstant %u32 {semantics}", + "OpControlBarrier %execution %memory %semantics", + execution = const EXECUTION as u32, + memory = const MEMORY as u32, + semantics = const SEMANTICS.bits(), + } + } } /// See [`spirv_std::arch::memory_barrier`] @@ -16,5 +28,14 @@ pub fn control_barrier() { const { SEMANTICS.assert_valid() } - spirv_std::arch::memory_barrier::<{ MEMORY as u32 }, { SEMANTICS.bits() }>() + unsafe { + asm! { + "%u32 = OpTypeInt 32 0", + "%memory = OpConstant %u32 {memory}", + "%semantics = OpConstant %u32 {semantics}", + "OpMemoryBarrier %memory %semantics", + memory = const MEMORY as u32, + semantics = const SEMANTICS.bits(), + } + } } diff --git a/crates/spirv-std/nightly/src/lib.rs b/crates/spirv-std/nightly/src/lib.rs index c8f93494fa5..120b172c23b 100644 --- a/crates/spirv-std/nightly/src/lib.rs +++ b/crates/spirv-std/nightly/src/lib.rs @@ -1,6 +1,6 @@ #![no_std] #![feature(adt_const_params)] -#![feature(generic_const_exprs)] +#![cfg_attr(target_arch = "spirv", feature(asm_experimental_arch))] pub mod arch; pub use spirv_std::*;