include!("macros.rs");
use core::{arch::asm, sync::atomic::Ordering};
use crate::utils::{Pair, U128};
#[cfg(any(
target_feature = "fast-serialization",
portable_atomic_target_feature = "fast-serialization",
))]
macro_rules! serialization {
() => {
"bcr 14, 0"
};
}
#[cfg(not(any(
target_feature = "fast-serialization",
portable_atomic_target_feature = "fast-serialization",
)))]
macro_rules! serialization {
() => {
"bcr 15, 0"
};
}
#[cfg(any(target_feature = "distinct-ops", portable_atomic_target_feature = "distinct-ops"))]
macro_rules! distinct_op {
($op:tt, $a0:tt, $a1:tt, $a2:tt) => {
concat!($op, "k ", $a0, ", ", $a1, ", ", $a2)
};
}
#[cfg(not(any(target_feature = "distinct-ops", portable_atomic_target_feature = "distinct-ops")))]
macro_rules! distinct_op {
($op:tt, $a0:tt, $a1:tt, $a2:tt) => {
concat!("lgr ", $a0, ", ", $a1, "\n", $op, " ", $a0, ", ", $a2)
};
}
#[cfg(any(
target_feature = "miscellaneous-extensions-3",
portable_atomic_target_feature = "miscellaneous-extensions-3",
))]
#[cfg(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
))]
macro_rules! select_op {
($cond:tt, $a0:tt, $a1:tt, $a2:tt) => {
concat!("selgr", $cond, " ", $a0, ", ", $a1, ", ", $a2)
};
}
#[cfg(not(any(
target_feature = "miscellaneous-extensions-3",
portable_atomic_target_feature = "miscellaneous-extensions-3",
)))]
#[cfg(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
))]
macro_rules! select_op {
($cond:tt, $a0:tt, $a1:tt, $a2:tt) => {
concat!("lgr ", $a0, ", ", $a2, "\n", "locgr", $cond, " ", $a0, ", ", $a1)
};
}
#[inline]
fn extract_cc(r: i64) -> bool {
r.wrapping_add(-268435456) & (1 << 31) != 0
}
#[inline]
unsafe fn atomic_load(src: *mut u128, _order: Ordering) -> u128 {
debug_assert!(src as usize % 16 == 0);
let (out_hi, out_lo);
unsafe {
asm!(
"lpq %r0, 0({src})", src = in(reg) ptr_reg!(src),
out("r0") out_hi,
out("r1") out_lo,
options(nostack, preserves_flags),
);
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);
let val = U128 { whole: val };
unsafe {
macro_rules! atomic_store {
($acquire:expr) => {
asm!(
"stpq %r0, 0({dst})", $acquire, dst = in(reg) ptr_reg!(dst),
in("r0") val.pair.hi,
in("r1") val.pair.lo,
options(nostack, preserves_flags),
)
};
}
match order {
Ordering::Relaxed | Ordering::Release => atomic_store!(""),
Ordering::SeqCst => atomic_store!(serialization!()),
_ => unreachable!(),
}
}
}
#[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 old = U128 { whole: old };
let new = U128 { whole: new };
let (prev_hi, prev_lo);
let r;
let prev = unsafe {
asm!(
"cdsg %r0, %r12, 0({dst})", "ipm {r}", dst = in(reg) ptr_reg!(dst),
r = lateout(reg) r,
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 extract_cc(r) { Ok(prev) } else { Err(prev) }
}
use self::atomic_compare_exchange as atomic_compare_exchange_weak;
#[cfg(not(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
)))]
#[inline]
unsafe fn byte_wise_atomic_load(src: *const u128) -> u128 {
unsafe {
let (out_hi, out_lo);
asm!(
"lg {out_hi}, 8({src})", "lg {out_lo}, 0({src})", src = in(reg) src,
out_hi = out(reg) out_hi,
out_lo = out(reg) out_lo,
options(pure, nostack, preserves_flags, readonly),
);
U128 { pair: Pair { hi: out_hi, lo: out_lo } }.whole
}
}
#[cfg(not(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
)))]
#[inline(always)]
unsafe fn atomic_update<F>(dst: *mut u128, order: Ordering, mut f: F) -> u128
where
F: FnMut(u128) -> u128,
{
unsafe {
let mut prev = byte_wise_atomic_load(dst);
loop {
let next = f(prev);
match atomic_compare_exchange_weak(dst, prev, next, order, Ordering::Relaxed) {
Ok(x) => return x,
Err(x) => prev = x,
}
}
}
}
#[inline]
unsafe fn atomic_swap(dst: *mut u128, val: u128, _order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
let val = U128 { whole: val };
let (mut prev_hi, mut prev_lo);
unsafe {
asm!(
"lg %r0, 8({dst})", "lg %r1, 0({dst})", "2:", "cdsg %r0, %r12, 0({dst})", "jl 2b", dst = in(reg) ptr_reg!(dst),
out("r0") prev_hi,
out("r1") prev_lo,
in("r12") val.pair.hi,
in("r13") val.pair.lo,
options(nostack),
);
U128 { pair: Pair { hi: prev_hi, lo: prev_lo } }.whole
}
}
macro_rules! atomic_rmw_cas_3 {
($name:ident, [$($reg:tt)*], $($op:tt)*) => {
#[inline]
unsafe fn $name(dst: *mut u128, val: u128, _order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
let val = U128 { whole: val };
let (mut prev_hi, mut prev_lo);
unsafe {
asm!(
"lg %r0, 8({dst})", "lg %r1, 0({dst})", "2:", $($op)*
"cdsg %r0, %r12, 0({dst})", "jl 2b", dst = in(reg) ptr_reg!(dst),
val_hi = in(reg) val.pair.hi,
val_lo = in(reg) val.pair.lo,
$($reg)*
out("r0") prev_hi,
out("r1") prev_lo,
out("r12") _,
out("r13") _,
options(nostack),
);
U128 { pair: Pair { hi: prev_hi, lo: prev_lo } }.whole
}
}
};
}
macro_rules! atomic_rmw_cas_2 {
($name:ident, [$($reg:tt)*], $($op:tt)*) => {
#[inline]
unsafe fn $name(dst: *mut u128, _order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
let (mut prev_hi, mut prev_lo);
unsafe {
asm!(
"lg %r0, 8({dst})", "lg %r1, 0({dst})", "2:", $($op)*
"cdsg %r0, %r12, 0({dst})", "jl 2b", dst = in(reg) ptr_reg!(dst),
$($reg)*
out("r0") prev_hi,
out("r1") prev_lo,
out("r12") _,
out("r13") _,
options(nostack),
);
U128 { pair: Pair { hi: prev_hi, lo: prev_lo } }.whole
}
}
};
}
atomic_rmw_cas_3! {
atomic_add, [],
distinct_op!("algr", "%r13", "%r1", "{val_lo}"), "lgr %r12, %r0", "alcgr %r12, {val_hi}", }
atomic_rmw_cas_3! {
atomic_sub, [],
distinct_op!("slgr", "%r13", "%r1", "{val_lo}"), "lgr %r12, %r0", "slbgr %r12, {val_hi}", }
atomic_rmw_cas_3! {
atomic_and, [],
distinct_op!("ngr", "%r13", "%r1", "{val_lo}"), distinct_op!("ngr", "%r12", "%r0", "{val_hi}"), }
#[cfg(any(
target_feature = "miscellaneous-extensions-3",
portable_atomic_target_feature = "miscellaneous-extensions-3",
))]
atomic_rmw_cas_3! {
atomic_nand, [],
"nngrk %r13, %r1, {val_lo}", "nngrk %r12, %r0, {val_hi}", }
#[cfg(not(any(
target_feature = "miscellaneous-extensions-3",
portable_atomic_target_feature = "miscellaneous-extensions-3",
)))]
atomic_rmw_cas_3! {
atomic_nand, [],
distinct_op!("ngr", "%r13", "%r1", "{val_lo}"), distinct_op!("ngr", "%r12", "%r0", "{val_hi}"), "lcgr %r13, %r13", "aghi %r13, -1", "lcgr %r12, %r12", "aghi %r12, -1", }
atomic_rmw_cas_3! {
atomic_or, [],
distinct_op!("ogr", "%r13", "%r1", "{val_lo}"), distinct_op!("ogr", "%r12", "%r0", "{val_hi}"), }
atomic_rmw_cas_3! {
atomic_xor, [],
distinct_op!("xgr", "%r13", "%r1", "{val_lo}"), distinct_op!("xgr", "%r12", "%r0", "{val_hi}"), }
#[cfg(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
))]
atomic_rmw_cas_3! {
atomic_max, [],
"clgr %r1, {val_lo}", select_op!("h", "%r12", "%r1", "{val_lo}"), "cgr %r0, {val_hi}", select_op!("h", "%r13", "%r1", "{val_lo}"), "locgre %r13, %r12", select_op!("h", "%r12", "%r0", "{val_hi}"), }
#[cfg(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
))]
atomic_rmw_cas_3! {
atomic_umax, [tmp = out(reg) _,],
"clgr %r1, {val_lo}", select_op!("h", "{tmp}", "%r1", "{val_lo}"), "clgr %r0, {val_hi}", select_op!("h", "%r12", "%r0", "{val_hi}"), select_op!("h", "%r13", "%r1", "{val_lo}"), "cgr %r0, {val_hi}", "locgre %r13, {tmp}", }
#[cfg(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
))]
atomic_rmw_cas_3! {
atomic_min, [],
"clgr %r1, {val_lo}", select_op!("l", "%r12", "%r1", "{val_lo}"), "cgr %r0, {val_hi}", select_op!("l", "%r13", "%r1", "{val_lo}"), "locgre %r13, %r12", select_op!("l", "%r12", "%r0", "{val_hi}"), }
#[cfg(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
))]
atomic_rmw_cas_3! {
atomic_umin, [tmp = out(reg) _,],
"clgr %r1, {val_lo}", select_op!("l", "{tmp}", "%r1", "{val_lo}"), "clgr %r0, {val_hi}", select_op!("l", "%r12", "%r0", "{val_hi}"), select_op!("l", "%r13", "%r1", "{val_lo}"), "cgr %r0, {val_hi}", "locgre %r13, {tmp}", }
#[cfg(not(any(
target_feature = "load-store-on-cond",
portable_atomic_target_feature = "load-store-on-cond",
)))]
atomic_rmw_by_atomic_update!(cmp);
atomic_rmw_cas_2! {
atomic_not, [],
"lcgr %r13, %r1", "aghi %r13, -1", "lcgr %r12, %r0", "aghi %r12, -1", }
#[cfg(any(target_feature = "distinct-ops", portable_atomic_target_feature = "distinct-ops"))]
atomic_rmw_cas_2! {
atomic_neg, [zero = in(reg) 0_u64,],
"slgrk %r13, {zero}, %r1", "lghi %r12, 0", "slbgr %r12, %r0", }
#[cfg(not(any(target_feature = "distinct-ops", portable_atomic_target_feature = "distinct-ops")))]
atomic_rmw_cas_2! {
atomic_neg, [],
"lghi %r13, 0", "slgr %r13, %r1", "lghi %r12, 0", "slbgr %r12, %r0", }
#[inline]
const fn is_lock_free() -> bool {
IS_ALWAYS_LOCK_FREE
}
const IS_ALWAYS_LOCK_FREE: bool = true;
atomic128!(AtomicI128, i128, atomic_max, atomic_min);
atomic128!(AtomicU128, u128, atomic_umax, atomic_umin);
#[cfg(test)]
mod tests {
use super::*;
test_atomic_int!(i128);
test_atomic_int!(u128);
stress_test!(u128);
}