Skip to main content

vyre_reference/
atomics.rs

1//! Atomic operation semantics enforced by the parity engine.
2//!
3//! GPU atomic instructions vary in memory ordering and return-value behavior
4//! across backends. This module exists to define one sequentially consistent,
5//! return-old-value semantics that every backend must match byte-for-byte.
6
7use vyre::ir::AtomicOp;
8
9use vyre::Error;
10
11/// Apply one sequentially consistent atomic operation.
12///
13/// # Errors
14///
15/// Returns [`Error::Interp`] if `AtomicOp::CompareExchange` is
16/// invoked without an `expected` value.
17pub fn apply(
18    op: AtomicOp,
19    old: u32,
20    expected: Option<u32>,
21    value: u32,
22) -> Result<(u32, u32), vyre::Error> {
23    match op {
24        AtomicOp::Add => Ok(atomic_add(old, value)),
25        AtomicOp::Or => Ok(atomic_or(old, value)),
26        AtomicOp::And => Ok(atomic_and(old, value)),
27        AtomicOp::Xor => Ok(atomic_xor(old, value)),
28        AtomicOp::Min => Ok(atomic_min(old, value)),
29        AtomicOp::Max => Ok(atomic_max(old, value)),
30        AtomicOp::Exchange => Ok(atomic_exchange(old, value)),
31        AtomicOp::CompareExchange => atomic_compare_exchange(old, expected, value),
32        _ => Err(Error::interp(format!(
33            "unsupported atomic op `{op:?}` reached the reference interpreter. Fix: define sequential semantics before constructing this AtomicOp."
34        ))),
35    }
36}
37
38/// Return the old value and the value after atomic add.
39pub fn atomic_add(old: u32, value: u32) -> (u32, u32) {
40    (old, old.wrapping_add(value))
41}
42
43/// Return the old value and the value after atomic bitwise OR.
44pub fn atomic_or(old: u32, value: u32) -> (u32, u32) {
45    (old, old | value)
46}
47
48/// Return the old value and the value after atomic bitwise AND.
49pub fn atomic_and(old: u32, value: u32) -> (u32, u32) {
50    (old, old & value)
51}
52
53/// Return the old value and the value after atomic bitwise XOR.
54pub fn atomic_xor(old: u32, value: u32) -> (u32, u32) {
55    (old, old ^ value)
56}
57
58/// Return the old value and the value after atomic unsigned minimum.
59pub fn atomic_min(old: u32, value: u32) -> (u32, u32) {
60    (old, old.min(value))
61}
62
63/// Return the old value and the value after atomic unsigned maximum.
64pub fn atomic_max(old: u32, value: u32) -> (u32, u32) {
65    (old, old.max(value))
66}
67
68/// Return the old value and the replacement value for atomic exchange.
69pub fn atomic_exchange(old: u32, value: u32) -> (u32, u32) {
70    (old, value)
71}
72
73/// Return the old value and the value after atomic compare-exchange.
74///
75/// # Errors
76///
77/// Returns [`Error::Interp`] if `expected` is `None`.
78pub fn atomic_compare_exchange(
79    old: u32,
80    expected: Option<u32>,
81    value: u32,
82) -> Result<(u32, u32), vyre::Error> {
83    let Some(expected) = expected else {
84        return Err(Error::interp(
85            "compare-exchange atomic is missing expected value. Fix: set Expr::Atomic.expected.",
86        ));
87    };
88    let new = if old == expected { value } else { old };
89    Ok((old, new))
90}