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        AtomicOp::LruUpdate => Ok(atomic_lru_update(old, value)),
33        _ => Err(Error::interp(format!(
34            "unsupported atomic op `{op:?}` reached the reference interpreter. Fix: define sequential semantics before constructing this AtomicOp."
35        ))),
36    }
37}
38
39/// Return the old value and the value after atomic add.
40pub fn atomic_add(old: u32, value: u32) -> (u32, u32) {
41    (old, old.wrapping_add(value))
42}
43
44/// Return the old value and the value after atomic bitwise OR.
45pub fn atomic_or(old: u32, value: u32) -> (u32, u32) {
46    (old, old | value)
47}
48
49/// Return the old value and the value after atomic bitwise AND.
50pub fn atomic_and(old: u32, value: u32) -> (u32, u32) {
51    (old, old & value)
52}
53
54/// Return the old value and the value after atomic bitwise XOR.
55pub fn atomic_xor(old: u32, value: u32) -> (u32, u32) {
56    (old, old ^ value)
57}
58
59/// Return the old value and the value after atomic unsigned minimum.
60pub fn atomic_min(old: u32, value: u32) -> (u32, u32) {
61    (old, old.min(value))
62}
63
64/// Return the old value and the value after atomic unsigned maximum.
65pub fn atomic_max(old: u32, value: u32) -> (u32, u32) {
66    (old, old.max(value))
67}
68
69/// Return the old value and the replacement value for atomic exchange.
70pub fn atomic_exchange(old: u32, value: u32) -> (u32, u32) {
71    (old, value)
72}
73
74/// LRU-update semantics: replace the slot with `value` only when
75/// `value` is strictly greater than `old` (the "more recent
76/// timestamp" wins). Identical tie-breaker behavior to `atomic_max`,
77/// kept as a distinct op so backends can lower it to a dedicated
78/// LRU-tracking instruction on hardware that has one. CPU reference is the
79/// correctness oracle.
80pub fn atomic_lru_update(old: u32, value: u32) -> (u32, u32) {
81    (old, old.max(value))
82}
83
84/// Return the old value and the value after atomic compare-exchange.
85///
86/// # Errors
87///
88/// Returns [`Error::Interp`] if `expected` is `None`.
89pub fn atomic_compare_exchange(
90    old: u32,
91    expected: Option<u32>,
92    value: u32,
93) -> Result<(u32, u32), vyre::Error> {
94    let Some(expected) = expected else {
95        return Err(Error::interp(
96            "compare-exchange atomic is missing expected value. Fix: set Expr::Atomic.expected.",
97        ));
98    };
99    let new = if old == expected { value } else { old };
100    Ok((old, new))
101}