1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Substrate-neutral memory model contracts.
/// Memory ordering attached to atomic and barrier operations.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize)]
pub enum MemoryOrdering {
/// No synchronization beyond atomicity of the operation.
Relaxed,
/// Subsequent reads observe writes released by another participant.
Acquire,
/// Prior writes become visible to acquiring participants.
Release,
/// Acquire and release semantics in one operation.
AcqRel,
/// Single total order across sequentially consistent operations
/// within the issuing thread's workgroup.
SeqCst,
/// Cross-grid synchronization. Every thread in the dispatch waits
/// here, and every prior write is globally visible after the
/// barrier returns. This is strictly stronger than `SeqCst`, which
/// only synchronizes within a workgroup. `GridSync` is required
/// when a fused kernel has an arm with divergent stores
/// (e.g. `if invocation_id == K { store ... }`) followed by an arm
/// that reads what was stored — without grid-level sync, threads
/// in non-K blocks observe stale state. Backends that lack a
/// native grid barrier (workgroup-only fences, no cooperative
/// launch) must lower this to a kernel-split: emit two separate
/// dispatches that share the underlying buffers.
GridSync,
}
impl MemoryOrdering {
/// Stable wire tag for this ordering.
#[must_use]
#[inline]
pub const fn wire_tag(self) -> u8 {
match self {
Self::Relaxed => 0,
Self::Acquire => 1,
Self::Release => 2,
Self::AcqRel => 3,
Self::SeqCst => 4,
Self::GridSync => 5,
}
}
/// Decode a stable wire tag.
///
/// # Errors
///
/// Returns an actionable error when `tag` is not assigned to a memory
/// ordering in this schema.
#[inline]
pub fn from_wire_tag(tag: u8) -> Result<Self, String> {
match tag {
0 => Ok(Self::Relaxed),
1 => Ok(Self::Acquire),
2 => Ok(Self::Release),
3 => Ok(Self::AcqRel),
4 => Ok(Self::SeqCst),
5 => Ok(Self::GridSync),
other => Err(format!(
"InvalidDiscriminant: memory ordering tag {other} is unknown. Fix: reserialize with a compatible VYRE wire schema."
)),
}
}
/// Whether this ordering is valid for an atomic RMW operation.
/// `GridSync` is barrier-only and not a valid atomic ordering.
#[must_use]
#[inline]
pub const fn is_valid_for_atomic_rmw(self) -> bool {
matches!(
self,
Self::Relaxed | Self::Acquire | Self::Release | Self::AcqRel | Self::SeqCst
)
}
/// Whether this ordering is valid for a barrier.
#[must_use]
#[inline]
pub const fn is_valid_for_barrier(self) -> bool {
matches!(
self,
Self::Acquire | Self::Release | Self::AcqRel | Self::SeqCst | Self::GridSync
)
}
/// Whether this ordering requires cross-grid synchronization.
/// Backends with a native grid barrier emit one instruction; backends
/// without must split the kernel.
#[must_use]
#[inline]
pub const fn requires_grid_sync(self) -> bool {
matches!(self, Self::GridSync)
}
}
impl Default for MemoryOrdering {
#[inline]
fn default() -> Self {
Self::SeqCst
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_seq_cst() {
assert_eq!(MemoryOrdering::default(), MemoryOrdering::SeqCst);
}
#[test]
fn all_variants_are_distinct() {
let variants = [
MemoryOrdering::Relaxed,
MemoryOrdering::Acquire,
MemoryOrdering::Release,
MemoryOrdering::AcqRel,
MemoryOrdering::SeqCst,
MemoryOrdering::GridSync,
];
for i in 0..variants.len() {
for j in (i + 1)..variants.len() {
assert_ne!(variants[i], variants[j]);
}
}
}
#[test]
fn grid_sync_round_trips() {
let tag = MemoryOrdering::GridSync.wire_tag();
assert_eq!(tag, 5);
assert_eq!(
MemoryOrdering::from_wire_tag(tag).unwrap(),
MemoryOrdering::GridSync
);
assert!(MemoryOrdering::GridSync.is_valid_for_barrier());
assert!(!MemoryOrdering::GridSync.is_valid_for_atomic_rmw());
assert!(MemoryOrdering::GridSync.requires_grid_sync());
assert!(!MemoryOrdering::SeqCst.requires_grid_sync());
}
#[test]
fn clone_eq() {
let a = MemoryOrdering::AcqRel;
let b = a;
assert_eq!(a, b);
}
}