Skip to content
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

Atomic unit tests #898

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -576,6 +576,64 @@ pub unsafe fn atomic_f_max<F: Float, 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
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_and.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
18 changes: 18 additions & 0 deletions tests/ui/arch/atomics/atomic_compare_exchange.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// build-pass

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::Device as u32 },
{ Semantics::UNIFORM_MEMORY.bits() },
{ Semantics::UNIFORM_MEMORY.bits() },
>(&mut *buffer.index_unchecked_mut(0), 5, 10);
}
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_exchange.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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::Device as u32 },
{ Semantics::UNIFORM_MEMORY.bits() },
>(&mut *buffer.index_unchecked_mut(0), 5);
}
}
23 changes: 23 additions & 0 deletions tests/ui/arch/atomics/atomic_flag_clear.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// build-pass

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") };
Comment on lines +13 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have other tests that do this? My understanding was that we've given up on Kernel support (could be wrong though).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kernel capability is required for the atomic flag operations. Other test that require specific capabilities are the atomic floats, which I've omitted for the reason that the capability is vendor specific (Nvidia only if I'm correct). The same can be done for the atomic flags tests if required


let old = unsafe {
spirv_std::arch::atomic_flag_clear::<
_,
{ Scope::Workgroup as u32 },
{ Semantics::UNIFORM_MEMORY.bits() },
>(&mut *buffer.index_unchecked_mut(0))
};
}
23 changes: 23 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,23 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0))
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_i_add.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_i_decrement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// build-pass

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() },
>(reference)
};
}
19 changes: 19 additions & 0 deletions tests/ui/arch/atomics/atomic_i_increment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// build-pass

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_increment::<
_,
{ Scope::Workgroup as u32 },
{ Semantics::NONE.bits() },
>(reference)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_i_sub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_load.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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::Device as u32 },
{ Semantics::UNIFORM_MEMORY.bits() },
>(&mut *buffer.index_unchecked_mut(0));
}
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_or.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_s_max.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_s_min.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_store.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_u_max.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_u_min.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}
17 changes: 17 additions & 0 deletions tests/ui/arch/atomics/atomic_xor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

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() },
>(&mut *buffer.index_unchecked_mut(0), 5)
};
}