From cbc20274592ac4850af36b5ec5a943f9391af9a9 Mon Sep 17 00:00:00 2001 From: Erin Power Date: Mon, 22 Mar 2021 08:51:58 +0100 Subject: [PATCH] Add memory_barrier and control_barrier --- crates/spirv-std/src/arch.rs | 4 ++ crates/spirv-std/src/arch/barrier.rs | 77 +++++++++++++++++++++++ crates/spirv-std/src/lib.rs | 6 ++ crates/spirv-std/src/memory.rs | 92 ++++++++++++++++++++++++++++ tests/ui/arch/control_barrier.rs | 17 +++++ tests/ui/arch/memory_barrier.rs | 16 +++++ 6 files changed, 212 insertions(+) create mode 100644 crates/spirv-std/src/arch/barrier.rs create mode 100644 crates/spirv-std/src/memory.rs create mode 100644 tests/ui/arch/control_barrier.rs create mode 100644 tests/ui/arch/memory_barrier.rs diff --git a/crates/spirv-std/src/arch.rs b/crates/spirv-std/src/arch.rs index 1de744fcd2..b55a175976 100644 --- a/crates/spirv-std/src/arch.rs +++ b/crates/spirv-std/src/arch.rs @@ -8,10 +8,14 @@ use crate::{scalar::Scalar, vector::Vector}; #[cfg(feature = "const-generics")] mod arithmetic; +#[cfg(feature = "const-generics")] +mod barrier; mod derivative; #[cfg(feature = "const-generics")] pub use arithmetic::*; +#[cfg(feature = "const-generics")] +pub use barrier::*; pub use derivative::*; /// Result is true if any component of `vector` is true, otherwise result is diff --git a/crates/spirv-std/src/arch/barrier.rs b/crates/spirv-std/src/arch/barrier.rs new file mode 100644 index 0000000000..a5af4d31af --- /dev/null +++ b/crates/spirv-std/src/arch/barrier.rs @@ -0,0 +1,77 @@ +use crate::memory::{Scope, Semantics}; + +/// Wait for other invocations of this module to reach the current point +/// of execution. +/// +/// All invocations of this module within Execution scope reach this point of +/// execution before any invocation proceeds beyond it. +/// +/// When Execution is [`Scope::Workgroup`] or larger, behavior is undefined +/// unless all invocations within Execution execute the same dynamic instance of +/// this instruction. When Execution is Subgroup or Invocation, the behavior of +/// this instruction in non-uniform control flow is defined by the client API. +/// +/// If [`Semantics`] is not [`Semantics::None`], this instruction also serves as +/// an [`memory_barrier`] function call, and also performs and adheres to the +/// description and semantics of an [`memory_barrier`] function with the same +/// `MEMORY` and `SEMANTICS` operands. This allows atomically specifying both a +/// control barrier and a memory barrier (that is, without needing two +/// instructions). If [`Semantics`] is [`Semantics::None`], `MEMORY` is ignored. +/// +/// Before SPIRV-V version 1.3, it is only valid to use this instruction with +/// `TessellationControl`, `GLCompute`, or `Kernel` execution models. There is +/// no such restriction starting with version 1.3. +/// +/// If used with the `TessellationControl` execution model, it also implicitly +/// synchronizes the [`crate::storage_class::Output`] Storage Class: Writes to +/// `Output` variables performed by any invocation executed prior to a +/// [`control_barrier`] are visible to any other invocation proceeding beyond +/// that [`control_barrier`]. +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpControlBarrier")] +#[inline] +pub unsafe fn control_barrier< + const EXECUTION: Scope, + const MEMORY: Scope, + const SEMANTICS: Semantics, +>() { + 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 u8, + memory = const MEMORY as u8, + semantics = const SEMANTICS as u8, + } +} + +/// Control the order that memory accesses are observed. +/// +/// Ensures that memory accesses issued before this instruction are observed +/// before memory accesses issued after this instruction. This control is +/// ensured only for memory accesses issued by this invocation and observed by +/// another invocation executing within `MEMORY` scope. If the `vulkan` memory +/// model is declared, this ordering only applies to memory accesses that +/// use the `NonPrivatePointer` memory operand or `NonPrivateTexel` +/// image operand. +/// +/// `SEMANTICS` declares what kind of memory is being controlled and what kind +/// of control to apply. +/// +/// To execute both a memory barrier and a control barrier, +/// see [`control_barrier`]. +#[spirv_std_macros::gpu_only] +#[doc(alias = "OpMemoryBarrier")] +#[inline] +pub unsafe fn memory_barrier() { + asm! { + "%u32 = OpTypeInt 32 0", + "%memory = OpConstant %u32 {memory}", + "%semantics = OpConstant %u32 {semantics}", + "OpMemoryBarrier %memory %semantics", + memory = const MEMORY as u8, + semantics = const SEMANTICS as u8, + } +} diff --git a/crates/spirv-std/src/lib.rs b/crates/spirv-std/src/lib.rs index 7ee0191e63..6e444ebb0c 100644 --- a/crates/spirv-std/src/lib.rs +++ b/crates/spirv-std/src/lib.rs @@ -4,6 +4,11 @@ feature(asm, register_attr, repr_simd, core_intrinsics, lang_items), register_attr(spirv) )] +#![cfg_attr( + feature = "const-generics", + feature(const_generics), + allow(incomplete_features) +)] // BEGIN - Embark standard lints v0.3 // do not change or add/remove here, but one can add exceptions after this section // for more info see: @@ -72,6 +77,7 @@ pub extern crate spirv_std_macros as macros; pub mod arch; pub mod float; pub mod integer; +pub mod memory; pub mod scalar; pub(crate) mod sealed; pub mod storage_class; diff --git a/crates/spirv-std/src/memory.rs b/crates/spirv-std/src/memory.rs new file mode 100644 index 0000000000..3628a99010 --- /dev/null +++ b/crates/spirv-std/src/memory.rs @@ -0,0 +1,92 @@ +//! Types for handling memory ordering constraints for concurrent memory access. + +#[derive(Debug, PartialEq, Eq)] +pub enum Scope { + /// Crosses multiple devices. + CrossDevice = 0, + + /// The current device. + Device = 1, + + /// The current workgroup. + Workgroup = 2, + + /// The current subgroup. + Subgroup = 3, + + /// The current invocation. + Invocation = 4, + + /// The current queue family. + QueueFamily = 5, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum Semantics { + /// No memory semantics. + None = 0, + + /// On an atomic instruction, orders memory operations provided in program + /// order after this atomic instruction against this atomic instruction. On + /// a barrier, orders memory operations provided in program order after this + /// barrier against atomic instructions before this barrier. + Acquire = 0x2, + + /// On an atomic instruction, orders memory operations provided in program + /// order before this atomic instruction against this atomic instruction. On + /// a barrier, orders memory operations provided in program order before + /// this barrier against atomic instructions after this barrier. + Release = 0x4, + + /// Has the properties of both [`Self::Acquire`] and [`Self::Release`] semantics. It + /// is used for read-modify-write operations. + AcquireRelease = 0x8, + + /// All observers see this memory access in the same order with respect to + /// other sequentially-consistent memory accesses from this invocation. + /// If the declared memory model is `vulkan`, `SequentiallyConsistent` must + /// not be used. + SequentiallyConsistent = 0x10, + + /// Apply the memory-ordering constraints to + /// [`crate::storage_class::StorageBuffer`], + /// [`crate::storage_class::PhysicalStorageBuffer`], or + /// [`crate::storage_class::Uniform`] Storage Class memory. + UniformMemory = 0x40, + + /// Apply the memory-ordering constraints to subgroup memory. + SubgroupMemory = 0x80, + + /// Apply the memory-ordering constraints to + /// [`crate::storage_class::Workgroup`] Storage Class memory. + WorkgroupMemory = 0x100, + + /// Apply the memory-ordering constraints to + /// [`crate::storage_class::CrossWorkgroup`] Storage Class memory. + CrossWorkgroupMemory = 0x200, + + /// Apply the memory-ordering constraints to + /// [`crate::storage_class::AtomicCounter`] Storage Class memory. + AtomicCounterMemory = 0x400, + + /// Apply the memory-ordering constraints to image contents (types declared + /// by `OpTypeImage`), or to accesses done through pointers to the + /// [`crate::storage_class::Image`] Storage Class. + ImageMemory = 0x800, + + /// Apply the memory-ordering constraints to the + /// [`crate::storage_class::Output`] Storage Class memory. + OutputMemory = 0x1000, + + /// Perform an availability operation on all references in the selected + /// storage classes. + MakeAvailable = 0x2000, + + /// Perform a visibility operation on all references in the selected + /// storage classes. + MakeVisible = 0x4000, + + /// This access cannot be eliminated, duplicated, or combined with + /// other accesses. + Volatile = 0x8000, +} diff --git a/tests/ui/arch/control_barrier.rs b/tests/ui/arch/control_barrier.rs new file mode 100644 index 0000000000..c143af7dd0 --- /dev/null +++ b/tests/ui/arch/control_barrier.rs @@ -0,0 +1,17 @@ +// build-pass + +#![feature(const_generics)] +#![allow(incomplete_features)] + +use spirv_std::memory::{Scope, Semantics}; + +#[spirv(fragment)] +pub fn main() { + unsafe { + spirv_std::arch::control_barrier::< + { Scope::Workgroup }, + { Scope::Workgroup }, + { Semantics::None }, + >(); + } +} diff --git a/tests/ui/arch/memory_barrier.rs b/tests/ui/arch/memory_barrier.rs new file mode 100644 index 0000000000..2e2508f38c --- /dev/null +++ b/tests/ui/arch/memory_barrier.rs @@ -0,0 +1,16 @@ +// build-pass + +#![feature(const_generics)] +#![allow(incomplete_features)] + +use spirv_std::memory::{Scope, Semantics}; + +#[spirv(fragment)] +pub fn main() { + unsafe { + spirv_std::arch::memory_barrier::< + { Scope::Workgroup }, + { Semantics::None }, + >(); + } +}