Skip to content

Atomic unit tests #892

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

Closed
58 changes: 58 additions & 0 deletions crates/spirv-std/src/arch/atomics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,64 @@ pub unsafe fn atomic_xor<I: Integer, const SCOPE: u32, const SEMANTICS: u32>(
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<I: Integer, const SCOPE: u32, const SEMANTICS: u32>(
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<I: Integer, const SCOPE: u32, const SEMANTICS: u32>(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:
///
Expand Down
6 changes: 4 additions & 2 deletions crates/spirv-std/src/number.rs
Original file line number Diff line number Diff line change
@@ -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 {}
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_and.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
20 changes: 20 additions & 0 deletions tests/ui/arch/atomics/atomic_compare_exchange.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_exchange.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
25 changes: 25 additions & 0 deletions tests/ui/arch/atomics/atomic_flag_clear.rs
Original file line number Diff line number Diff line change
@@ -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))
};
}
25 changes: 25 additions & 0 deletions tests/ui/arch/atomics/atomic_flag_test_and_set.rs
Original file line number Diff line number Diff line change
@@ -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))
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_i_add.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
21 changes: 21 additions & 0 deletions tests/ui/arch/atomics/atomic_i_decrement.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
Original file line number Diff line number Diff line change
@@ -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]) {
Expand All @@ -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);
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_i_sub.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_load.rs
Original file line number Diff line number Diff line change
@@ -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));
}
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_or.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_s_max.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_s_min.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_store.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_u_max.rs
Original file line number Diff line number Diff line change
@@ -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)
};
}
Loading