diff --git a/Cargo.lock b/Cargo.lock index 9ba291e1fbb..bc71a2a5e21 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -612,6 +612,7 @@ name = "compiletests-deps-helper" version = "0.0.0" dependencies = [ "spirv-std", + "spirv-std-nightly", ] [[package]] @@ -3680,6 +3681,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 0fc2aa0198f..7a87d0b5743 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/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 new file mode 100644 index 00000000000..370bbd1bfea --- /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, features = ["adt_const_params"] } +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/arch.rs b/crates/spirv-std/nightly/src/arch.rs new file mode 100644 index 00000000000..09835c417f3 --- /dev/null +++ b/crates/spirv-std/nightly/src/arch.rs @@ -0,0 +1,51 @@ +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; +#[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 { + 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 { + 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 new file mode 100644 index 00000000000..79498ffcb1d --- /dev/null +++ b/crates/spirv-std/nightly/src/arch/atomics.rs @@ -0,0 +1,507 @@ +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] +#[doc(alias = "OpAtomicLoad")] +#[inline] +pub unsafe fn atomic_load(ptr: &N) -> N { + const { SEMANTICS.assert_valid() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicStore")] +#[inline] +pub unsafe fn atomic_store( + ptr: &mut N, + value: N, +) { + const { SEMANTICS.assert_valid() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicExchange")] +#[inline] +pub unsafe fn atomic_exchange( + ptr: &mut N, + value: N, +) -> N { + const { SEMANTICS.assert_valid() } + 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`] +#[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(); + } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicIIncrement")] +#[inline] +pub unsafe fn atomic_i_increment( + ptr: &mut I, +) -> I { + const { SEMANTICS.assert_valid() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicIDecrement")] +#[inline] +pub unsafe fn atomic_i_decrement( + ptr: &mut I, +) -> I { + const { SEMANTICS.assert_valid() } + 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`] +#[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() } + 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`] +#[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() } + 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`] +#[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() } + 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`] +#[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() } + 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`] +#[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() } + 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`] +#[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() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicAnd")] +#[inline] +pub unsafe fn atomic_and( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicOr")] +#[inline] +pub unsafe fn atomic_or( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicXor")] +#[inline] +pub unsafe fn atomic_xor( + ptr: &mut I, + value: I, +) -> I { + const { SEMANTICS.assert_valid() } + + 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`] +#[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() } + 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`] +#[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() } + 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`] +#[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() } + 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 new file mode 100644 index 00000000000..ee2981d9650 --- /dev/null +++ b/crates/spirv-std/nightly/src/arch/barrier.rs @@ -0,0 +1,41 @@ +#[cfg(target_arch = "spirv")] +use core::arch::asm; +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() } + 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`] +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpMemoryBarrier")] +#[inline] +pub fn memory_barrier() { + const { SEMANTICS.assert_valid() } + 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 new file mode 100644 index 00000000000..120b172c23b --- /dev/null +++ b/crates/spirv-std/nightly/src/lib.rs @@ -0,0 +1,6 @@ +#![no_std] +#![feature(adt_const_params)] +#![cfg_attr(target_arch = "spirv", feature(asm_experimental_arch))] + +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/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 23407eb9e51..d50fcd7c8a7 100644 --- a/tests/compiletests/src/main.rs +++ b/tests/compiletests/src/main.rs @@ -121,6 +121,10 @@ impl Runner { deps.spirv_std_macros.display() ), &*format!("--extern spirv_std={}", deps.spirv_std.display()), + &*format!( + "--extern spirv_std_nightly={}", + deps.spirv_std_nightly.display() + ), &*format!("--extern glam={}", deps.glam.display()), "--crate-type dylib", "-Zunstable-options", @@ -224,6 +228,7 @@ impl Runner { let spirv_std = self.find_lib("spirv_std", DepKind::SpirvLib, target); let glam = self.find_lib("glam", DepKind::SpirvLib, target); let spirv_std_macros = self.find_lib("spirv_std_macros", DepKind::ProcMacro, target); + let spirv_std_nightly = self.find_lib("spirv_std_nightly", DepKind::SpirvLib, target); let all_libs = [ &compiler_builtins, @@ -231,6 +236,7 @@ impl Runner { &spirv_std, &glam, &spirv_std_macros, + &spirv_std_nightly, ]; if all_libs.iter().any(|r| r.is_err()) { // FIXME(eddyb) `missing_count` should always be `0` anyway. @@ -256,6 +262,7 @@ impl Runner { compiler_builtins: compiler_builtins.ok().unwrap(), spirv_std: spirv_std.ok().unwrap(), spirv_std_macros: spirv_std_macros.ok().unwrap(), + spirv_std_nightly: spirv_std_nightly.ok().unwrap(), } } } @@ -345,6 +352,7 @@ struct TestDeps { compiler_builtins: PathBuf, spirv_std: PathBuf, spirv_std_macros: PathBuf, + spirv_std_nightly: PathBuf, glam: PathBuf, } diff --git a/tests/compiletests/ui/arch/memory_barrier_invalid_ordering.rs b/tests/compiletests/ui/arch/memory_barrier_invalid_ordering.rs new file mode 100644 index 00000000000..6615e47abe3 --- /dev/null +++ b/tests/compiletests/ui/arch/memory_barrier_invalid_ordering.rs @@ -0,0 +1,20 @@ +// 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)] +#![allow(incomplete_features)] + +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/arch/memory_barrier_invalid_ordering.stderr b/tests/compiletests/ui/arch/memory_barrier_invalid_ordering.stderr new file mode 100644 index 00000000000..09738c243d6 --- /dev/null +++ b/tests/compiletests/ui/arch/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:18: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`.