include!("macros.rs");
use core::{arch::asm, sync::atomic::Ordering};
use crate::utils::strongest_failure_ordering;
#[derive(Clone, Copy)]
#[repr(C)]
union U128 {
whole: u128,
pair: Pair,
}
#[derive(Clone, Copy)]
#[repr(C, align(16))]
struct Pair {
hi: u64,
lo: u64,
}
#[inline]
unsafe fn atomic_load(src: *mut u128, _order: Ordering) -> u128 {
debug_assert!(src as usize % 16 == 0);
unsafe {
let (out_hi, out_lo);
asm!(
"lpq %r0, 0({src})",
src = in(reg) src,
out("r0") out_hi,
out("r1") out_lo,
options(nostack),
);
U128 { pair: Pair { hi: out_hi, lo: out_lo } }.whole
}
}
#[inline]
unsafe fn atomic_store(dst: *mut u128, val: u128, order: Ordering) {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let val = U128 { whole: val };
match order {
Ordering::Relaxed | Ordering::Release => {
asm!(
"stpq %r0, 0({dst})",
dst = in(reg) dst,
in("r0") val.pair.hi,
in("r1") val.pair.lo,
options(nostack),
);
}
Ordering::SeqCst => {
asm!(
"stpq %r0, 0({dst})",
"bcr 15, %r0",
dst = in(reg) dst,
in("r0") val.pair.hi,
in("r1") val.pair.lo,
options(nostack),
);
}
_ => unreachable!("{:?}", order),
}
}
}
#[inline]
unsafe fn atomic_compare_exchange(
dst: *mut u128,
old: u128,
new: u128,
_success: Ordering,
_failure: Ordering,
) -> Result<u128, u128> {
debug_assert!(dst as usize % 16 == 0);
let res = unsafe {
let old = U128 { whole: old };
let new = U128 { whole: new };
let (prev_hi, prev_lo);
asm!(
"cdsg %r0, %r12, 0({dst})",
dst = in(reg) dst,
inout("r0") old.pair.hi => prev_hi,
inout("r1") old.pair.lo => prev_lo,
in("r12") new.pair.hi,
in("r13") new.pair.lo,
options(nostack),
);
U128 { pair: Pair { hi: prev_hi, lo: prev_lo } }.whole
};
if res == old {
Ok(res)
} else {
Err(res)
}
}
use atomic_compare_exchange as atomic_compare_exchange_weak;
#[inline]
unsafe fn atomic_update<F>(dst: *mut u128, order: Ordering, mut f: F) -> u128
where
F: FnMut(u128) -> u128,
{
let failure = strongest_failure_ordering(order);
unsafe {
let mut old = atomic_load(dst, failure);
loop {
let next = f(old);
match atomic_compare_exchange_weak(dst, old, next, order, failure) {
Ok(x) => return x,
Err(x) => old = x,
}
}
}
}
#[inline]
unsafe fn atomic_swap(dst: *mut u128, val: u128, order: Ordering) -> u128 {
unsafe { atomic_update(dst, order, |_| val) }
}
atomic128!(AtomicI128, i128);
atomic128!(AtomicU128, u128);
#[cfg(test)]
mod tests {
use super::*;
test_atomic_int!(i128);
test_atomic_int!(u128);
}