diff --git a/crates/spirv-std/src/arch/atomics.rs b/crates/spirv-std/src/arch/atomics.rs index bb0f75992e..88f51eb683 100644 --- a/crates/spirv-std/src/arch/atomics.rs +++ b/crates/spirv-std/src/arch/atomics.rs @@ -506,6 +506,64 @@ pub unsafe fn atomic_xor( old } +/// Atomically sets the flag value pointed to by Pointer to the set state. +/// Pointer must be a pointer to a 32-bit integer type representing an atomic flag. +/// The instruction’s result is true if the flag was in the set state or false if the flag was in the clear state immediately before the operation. +/// **Important:** Kernel capabilities have to be set beforehand. +/// +/// Result Type must be a Boolean type. +/// The resulting values are undefined if an atomic flag is modified by an instruction other than OpAtomicFlagTestAndSet or OpAtomicFlagClear. +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicFlagTestAndSet")] +#[inline] +pub unsafe fn atomic_flag_test_and_set( + ptr: &mut I, +) -> bool { + let mut old: bool = false; + + asm! { + "%bool = OpTypeBool", + "%u32 = OpTypeInt 32 0", + "%scope = OpConstant %u32 {scope}", + "%semantics = OpConstant %u32 {semantics}", + "%old = OpAtomicFlagTestAndSet %bool {ptr} %scope %semantics", + "OpStore {old} %old", + scope = const SCOPE, + semantics = const SEMANTICS, + ptr = in(reg) ptr, + old = in(reg) &mut old + } + + old +} + +/// Atomically sets the flag value pointed to by Pointer to the clear state. +/// +/// Pointer must be a pointer to a 32-bit integer type representing an atomic flag. +/// **Important:** Memory Semantics must not be Acquire or AcquireRelease +/// +/// The resulting values are undefined if an atomic flag is modified by an instruction other than OpAtomicFlagTestAndSet or OpAtomicFlagClear. +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpAtomicFlagClear")] +#[inline] +pub unsafe fn atomic_flag_clear(ptr: &mut I) { + // Ensure the memory semantic is not Acquire or AcquireRelease + assert!( + SEMANTICS + != (crate::memory::Semantics::ACQUIRE.bits() as u32 + | crate::memory::Semantics::ACQUIRE_RELEASE.bits() as u32) + ); + asm! { + "%u32 = OpTypeInt 32 0", + "%semantics = OpConstant %u32 {semantics}", + "%scope = OpConstant %u32 {scope}", + "OpAtomicFlagClear {ptr} %scope %semantics", + scope = const SCOPE, + semantics = const SEMANTICS, + ptr = in(reg) ptr + } +} + /// Perform the following steps atomically with respect to any other atomic /// accesses within Scope to the same location: /// diff --git a/crates/spirv-std/src/number.rs b/crates/spirv-std/src/number.rs index 20a195da1e..113d85ec43 100644 --- a/crates/spirv-std/src/number.rs +++ b/crates/spirv-std/src/number.rs @@ -1,6 +1,8 @@ -//! Traits and helper functions related to numbers. +//! SPIR-V Numerical Type +//! +//! Used to represent either an integer type or floating-point type. -/// Abstract trait representing a SPIR-V integer or floating-point type. +/// Abstract trait representing a SPIR-V numerical type. pub trait Number: crate::scalar::Scalar {} impl Number for u8 {} diff --git a/tests/ui/arch/atomics/atomic_and.rs b/tests/ui/arch/atomics/atomic_and.rs new file mode 100644 index 0000000000..fa63843418 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_and.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_and::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_compare_exchange.rs b/tests/ui/arch/atomics/atomic_compare_exchange.rs new file mode 100644 index 0000000000..bcdb976119 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_compare_exchange.rs @@ -0,0 +1,20 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + unsafe { + let old = spirv_std::arch::atomic_compare_exchange::< + _, + { Scope::CrossDevice as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5, 10); + } +} diff --git a/tests/ui/arch/atomics/atomic_exchange.rs b/tests/ui/arch/atomics/atomic_exchange.rs new file mode 100644 index 0000000000..966e4e726c --- /dev/null +++ b/tests/ui/arch/atomics/atomic_exchange.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + unsafe { + let old = spirv_std::arch::atomic_exchange::< + _, + { Scope::CrossDevice as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5); + } +} diff --git a/tests/ui/arch/atomics/atomic_flag_clear.rs b/tests/ui/arch/atomics/atomic_flag_clear.rs new file mode 100644 index 0000000000..f8f10e8102 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_flag_clear.rs @@ -0,0 +1,25 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[cfg(target_arch = "spirv")] +use core::arch::asm; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + // Ensure kernel capabilities. + unsafe { asm!("OpCapability Kernel") }; + + let old = unsafe { + spirv_std::arch::atomic_flag_clear::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0)) + }; +} diff --git a/tests/ui/arch/atomics/atomic_flag_test_and_set.rs b/tests/ui/arch/atomics/atomic_flag_test_and_set.rs new file mode 100644 index 0000000000..8d3ab6765f --- /dev/null +++ b/tests/ui/arch/atomics/atomic_flag_test_and_set.rs @@ -0,0 +1,25 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[cfg(target_arch = "spirv")] +use core::arch::asm; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + // Ensure kernel capabilities. + unsafe { asm!("OpCapability Kernel") }; + + let old = unsafe { + spirv_std::arch::atomic_flag_test_and_set::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0)) + }; +} diff --git a/tests/ui/arch/atomics/atomic_i_add.rs b/tests/ui/arch/atomics/atomic_i_add.rs new file mode 100644 index 0000000000..c4ee131c11 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_i_add.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_i_add::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_i_decrement.rs b/tests/ui/arch/atomics/atomic_i_decrement.rs new file mode 100644 index 0000000000..f1ca905375 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_i_decrement.rs @@ -0,0 +1,21 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let reference = unsafe { buffer.index_unchecked_mut(0) }; + + let old = unsafe { + spirv_std::arch::atomic_i_decrement::< + _, + { Scope::Workgroup as u32 }, + { Semantics::NONE.bits() as u32 }, + >(reference) + }; +} diff --git a/tests/ui/arch/atomic_i_increment.rs b/tests/ui/arch/atomics/atomic_i_increment.rs similarity index 57% rename from tests/ui/arch/atomic_i_increment.rs rename to tests/ui/arch/atomics/atomic_i_increment.rs index 3866324823..ec3fca8317 100644 --- a/tests/ui/arch/atomic_i_increment.rs +++ b/tests/ui/arch/atomics/atomic_i_increment.rs @@ -1,6 +1,11 @@ // build-pass -use spirv_std::arch::IndexUnchecked; +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; #[spirv(compute(threads(64)))] pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { @@ -9,9 +14,8 @@ pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &m let old = unsafe { spirv_std::arch::atomic_i_increment::< _, - { spirv_std::memory::Scope::Workgroup as u32 }, - { spirv_std::memory::Semantics::NONE.bits() as u32 }, + { Scope::Workgroup as u32 }, + { Semantics::NONE.bits() as u32 }, >(reference) }; - assert!(old == 0); } diff --git a/tests/ui/arch/atomics/atomic_i_sub.rs b/tests/ui/arch/atomics/atomic_i_sub.rs new file mode 100644 index 0000000000..7ea97f4046 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_i_sub.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_i_sub::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_load.rs b/tests/ui/arch/atomics/atomic_load.rs new file mode 100644 index 0000000000..fd1b1781f9 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_load.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + unsafe { + let output = spirv_std::arch::atomic_load::< + _, + { Scope::CrossDevice as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0)); + } +} diff --git a/tests/ui/arch/atomics/atomic_or.rs b/tests/ui/arch/atomics/atomic_or.rs new file mode 100644 index 0000000000..7d226dc82a --- /dev/null +++ b/tests/ui/arch/atomics/atomic_or.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_or::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_s_max.rs b/tests/ui/arch/atomics/atomic_s_max.rs new file mode 100644 index 0000000000..f4451cd54e --- /dev/null +++ b/tests/ui/arch/atomics/atomic_s_max.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [i32]) { + let old = unsafe { + spirv_std::arch::atomic_s_max::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_s_min.rs b/tests/ui/arch/atomics/atomic_s_min.rs new file mode 100644 index 0000000000..38fceb78fa --- /dev/null +++ b/tests/ui/arch/atomics/atomic_s_min.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [i32]) { + let old = unsafe { + spirv_std::arch::atomic_s_min::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_store.rs b/tests/ui/arch/atomics/atomic_store.rs new file mode 100644 index 0000000000..8e44bf17f1 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_store.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_store::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_u_max.rs b/tests/ui/arch/atomics/atomic_u_max.rs new file mode 100644 index 0000000000..62cbd8c0f8 --- /dev/null +++ b/tests/ui/arch/atomics/atomic_u_max.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_u_max::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_u_min.rs b/tests/ui/arch/atomics/atomic_u_min.rs new file mode 100644 index 0000000000..0297fa3a9d --- /dev/null +++ b/tests/ui/arch/atomics/atomic_u_min.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_u_min::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +} diff --git a/tests/ui/arch/atomics/atomic_xor.rs b/tests/ui/arch/atomics/atomic_xor.rs new file mode 100644 index 0000000000..526fa068ed --- /dev/null +++ b/tests/ui/arch/atomics/atomic_xor.rs @@ -0,0 +1,19 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] +use spirv_std::{ + arch::IndexUnchecked, + memory::{Scope, Semantics}, +}; + +#[spirv(compute(threads(64)))] +pub fn main(#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &mut [u32]) { + let old = unsafe { + spirv_std::arch::atomic_xor::< + _, + { Scope::Workgroup as u32 }, + { Semantics::UNIFORM_MEMORY.bits() as u32 }, + >(&mut *buffer.index_unchecked_mut(0), 5) + }; +}