include!("macros.rs");
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(
test,
not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
any(target_feature = "lse2", portable_atomic_target_feature = "lse2"),
)),
))]
#[cfg(any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(target_env = "musl", any(not(target_feature = "crt-static"), feature = "std")),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
))]
#[path = "../detect/auxv.rs"]
mod detect;
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(
test,
not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
any(target_feature = "lse2", portable_atomic_target_feature = "lse2"),
)),
))]
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
#[path = "../detect/aarch64_aa64reg.rs"]
mod detect;
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(test, portable_atomic_outline_atomics))] #[cfg(any(
test,
not(all(
any(target_feature = "lse2", portable_atomic_target_feature = "lse2"),
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
)),
))]
#[cfg(target_os = "illumos")]
#[path = "../detect/aarch64_illumos.rs"]
mod detect;
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))]
#[cfg(target_os = "fuchsia")]
#[path = "../detect/aarch64_fuchsia.rs"]
mod detect;
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))]
#[cfg(windows)]
#[path = "../detect/aarch64_windows.rs"]
mod detect;
#[cfg(test)] #[cfg(not(valgrind))]
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
#[path = "../detect/aarch64_aa64reg.rs"]
mod test_detect_aa64reg;
#[cfg(test)] #[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(target_vendor = "apple")]
#[path = "../detect/aarch64_apple.rs"]
mod test_detect_apple;
#[cfg(test)] #[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(target_os = "openbsd")]
#[path = "../detect/auxv.rs"]
mod test_detect_auxv;
#[cfg(not(portable_atomic_no_asm))]
use core::arch::asm;
use core::sync::atomic::Ordering;
use crate::utils::{Pair, U128};
#[cfg(any(
target_feature = "lse",
portable_atomic_target_feature = "lse",
not(portable_atomic_no_outline_atomics),
))]
#[rustfmt::skip]
macro_rules! debug_assert_lse {
() => {
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
target_os = "fuchsia",
windows,
),
))]
#[cfg(not(any(target_feature = "lse", portable_atomic_target_feature = "lse")))]
{
debug_assert!(detect::detect().lse());
}
};
}
#[cfg(any(
target_feature = "lse2",
portable_atomic_target_feature = "lse2",
not(portable_atomic_no_outline_atomics),
))]
#[rustfmt::skip]
macro_rules! debug_assert_lse2 {
() => {
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
// These don't support detection of FEAT_LSE2.
// target_os = "fuchsia",
// windows,
),
))]
#[cfg(not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")))]
{
debug_assert!(detect::detect().lse2());
}
};
}
#[cfg(any(
target_feature = "lse128",
portable_atomic_target_feature = "lse128",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
#[rustfmt::skip]
macro_rules! debug_assert_lse128 {
() => {
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
// These don't support detection of FEAT_LSE128.
// target_os = "fuchsia",
// windows,
),
))]
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
{
debug_assert!(detect::detect().lse128());
}
};
}
#[cfg(any(
target_feature = "rcpc3",
portable_atomic_target_feature = "rcpc3",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
#[rustfmt::skip]
macro_rules! debug_assert_rcpc3 {
() => {
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
// These don't support detection of FEAT_LRCPC3.
// target_os = "fuchsia",
// windows,
),
))]
#[cfg(not(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3")))]
{
debug_assert!(detect::detect().rcpc3());
}
};
}
#[cfg(any(
target_feature = "lse",
portable_atomic_target_feature = "lse",
not(portable_atomic_no_outline_atomics),
))]
macro_rules! start_lse {
() => {
".arch_extension lse"
};
}
#[cfg(not(portable_atomic_pre_llvm_16))]
#[cfg(any(
target_feature = "lse128",
portable_atomic_target_feature = "lse128",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
macro_rules! start_lse128 {
() => {
".arch_extension lse128"
};
}
#[cfg(not(portable_atomic_pre_llvm_16))]
#[cfg(any(
target_feature = "rcpc3",
portable_atomic_target_feature = "rcpc3",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
macro_rules! start_rcpc3 {
() => {
".arch_extension rcpc3"
};
}
#[cfg(target_endian = "little")]
macro_rules! select_le_or_be {
($le:expr, $be:expr) => {
$le
};
}
#[cfg(target_endian = "big")]
macro_rules! select_le_or_be {
($le:expr, $be:expr) => {
$be
};
}
macro_rules! atomic_rmw {
($op:ident, $order:ident) => {
atomic_rmw!($op, $order, write = $order)
};
($op:ident, $order:ident, write = $write:ident) => {
match $order {
Ordering::Relaxed => $op!("", "", ""),
Ordering::Acquire => $op!("a", "", ""),
Ordering::Release => $op!("", "l", ""),
Ordering::AcqRel => $op!("a", "l", ""),
#[cfg(target_env = "msvc")]
Ordering::SeqCst if $write == Ordering::SeqCst => $op!("a", "l", "dmb ish"),
Ordering::SeqCst => $op!("a", "l", ""),
_ => unreachable!(),
}
};
}
#[cfg(portable_atomic_pre_llvm_16)]
#[cfg(any(
target_feature = "lse128",
portable_atomic_target_feature = "lse128",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
macro_rules! atomic_rmw_inst {
($op:ident, $order:ident) => {
atomic_rmw_inst!($op, $order, write = $order)
};
($op:ident, $order:ident, write = $write:ident) => {
match $order {
Ordering::Relaxed => $op!("2", ""), Ordering::Acquire => $op!("a", ""), Ordering::Release => $op!("6", ""), Ordering::AcqRel => $op!("e", ""), #[cfg(target_env = "msvc")]
Ordering::SeqCst if $write == Ordering::SeqCst => $op!("e", "dmb ish"),
Ordering::SeqCst => $op!("e", ""),
_ => unreachable!(),
}
};
}
cfg_sel!({
#[cfg(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))]
{
use self::_atomic_load_ldp as atomic_load;
}
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
// These don't support detection of FEAT_LSE2.
// target_os = "fuchsia",
// windows,
),
))]
{
#[inline]
unsafe fn atomic_load(src: *mut u128, order: Ordering) -> u128 {
fn_alias! {
#[inline(never)]
unsafe fn(src: *mut u128) -> u128;
atomic_load_lse2_relaxed = _atomic_load_ldp(Ordering::Relaxed);
atomic_load_lse2_acquire = _atomic_load_ldp(Ordering::Acquire);
atomic_load_lse2_seqcst = _atomic_load_ldp(Ordering::SeqCst);
atomic_load_lse2_rcpc3_acquire = _atomic_load_ldiapp(Ordering::Acquire);
atomic_load_lse2_rcpc3_seqcst = _atomic_load_ldiapp(Ordering::SeqCst);
}
fn_alias! {
unsafe fn(src: *mut u128) -> u128;
atomic_load_no_lse2_relaxed = atomic_load_no_lse2(Ordering::Relaxed);
atomic_load_no_lse2_acquire = atomic_load_no_lse2(Ordering::Acquire);
atomic_load_no_lse2_seqcst = atomic_load_no_lse2(Ordering::SeqCst);
}
unsafe {
match order {
Ordering::Relaxed => {
ifunc!(unsafe fn(src: *mut u128) -> u128 {
let cpuinfo = detect::detect();
if cpuinfo.lse2() {
atomic_load_lse2_relaxed
} else {
atomic_load_no_lse2_relaxed
}
})
}
Ordering::Acquire => {
ifunc!(unsafe fn(src: *mut u128) -> u128 {
let cpuinfo = detect::detect();
if cpuinfo.lse2() {
if cpuinfo.rcpc3() {
atomic_load_lse2_rcpc3_acquire
} else {
atomic_load_lse2_acquire
}
} else {
atomic_load_no_lse2_acquire
}
})
}
Ordering::SeqCst => {
ifunc!(unsafe fn(src: *mut u128) -> u128 {
let cpuinfo = detect::detect();
if cpuinfo.lse2() {
if cpuinfo.rcpc3() {
atomic_load_lse2_rcpc3_seqcst
} else {
atomic_load_lse2_seqcst
}
} else {
atomic_load_no_lse2_seqcst
}
})
}
_ => unreachable!(),
}
}
}
}
#[cfg(else)]
{
use self::atomic_load_no_lse2 as atomic_load;
}
});
#[cfg(not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")))]
cfg_sel!({
#[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))]
{
use self::_atomic_load_casp as atomic_load_no_lse2;
}
#[cfg(else)]
{
use self::_atomic_load_ldxp_stxp as atomic_load_no_lse2;
}
});
#[cfg(any(
target_feature = "lse2",
portable_atomic_target_feature = "lse2",
not(portable_atomic_no_outline_atomics),
))]
#[inline]
unsafe fn _atomic_load_ldp(src: *mut u128, order: Ordering) -> u128 {
debug_assert!(src as usize % 16 == 0);
debug_assert_lse2!();
unsafe {
let (out_lo, out_hi);
macro_rules! atomic_load_relaxed {
($acquire:tt) => {{
asm!(
"ldp {out_lo}, {out_hi}, [{src}]",
$acquire,
src = in(reg) ptr_reg!(src),
out_hi = lateout(reg) out_hi,
out_lo = lateout(reg) out_lo,
options(nostack, preserves_flags),
);
U128 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
}};
}
match order {
#[cfg(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3"))]
Ordering::Acquire | Ordering::SeqCst => _atomic_load_ldiapp(src, order),
Ordering::Relaxed => atomic_load_relaxed!(""),
#[cfg(not(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3")))]
Ordering::Acquire => atomic_load_relaxed!("dmb ishld"),
#[cfg(not(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3")))]
Ordering::SeqCst => {
asm!(
"ldar {tmp}, [{src}]",
"ldp {out_lo}, {out_hi}, [{src}]",
"dmb ishld",
src = in(reg) ptr_reg!(src),
out_hi = lateout(reg) out_hi,
out_lo = lateout(reg) out_lo,
tmp = out(reg) _,
options(nostack, preserves_flags),
);
U128 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
}
_ => unreachable!(),
}
}
}
#[cfg(any(
target_feature = "lse2",
portable_atomic_target_feature = "lse2",
not(portable_atomic_no_outline_atomics),
))]
#[cfg(any(
target_feature = "rcpc3",
portable_atomic_target_feature = "rcpc3",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
#[inline]
unsafe fn _atomic_load_ldiapp(src: *mut u128, order: Ordering) -> u128 {
debug_assert!(src as usize % 16 == 0);
debug_assert_lse2!();
debug_assert_rcpc3!();
unsafe {
let (out_lo, out_hi);
match order {
Ordering::Acquire => {
#[cfg(not(portable_atomic_pre_llvm_16))]
asm!(
start_rcpc3!(),
"ldiapp {out_lo}, {out_hi}, [{src}]",
src = in(reg) ptr_reg!(src),
out_hi = lateout(reg) out_hi,
out_lo = lateout(reg) out_lo,
options(nostack, preserves_flags),
);
#[cfg(portable_atomic_pre_llvm_16)]
asm!(
".inst 0xd9411800",
in("x0") ptr_reg!(src),
lateout("x1") out_hi,
lateout("x0") out_lo,
options(nostack, preserves_flags),
);
}
Ordering::SeqCst => {
#[cfg(not(portable_atomic_pre_llvm_16))]
asm!(
start_rcpc3!(),
"ldar {tmp}, [{src}]",
"ldiapp {out_lo}, {out_hi}, [{src}]",
src = in(reg) ptr_reg!(src),
out_hi = lateout(reg) out_hi,
out_lo = lateout(reg) out_lo,
tmp = out(reg) _,
options(nostack, preserves_flags),
);
#[cfg(portable_atomic_pre_llvm_16)]
asm!(
"ldar {tmp}, [x0]",
".inst 0xd9411800",
tmp = out(reg) _,
in("x0") ptr_reg!(src),
lateout("x1") out_hi,
lateout("x0") out_lo,
options(nostack, preserves_flags),
);
}
_ => unreachable!(),
}
U128 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
}
}
#[cfg(any(test, not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))))]
#[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))]
#[inline]
unsafe fn _atomic_load_casp(src: *mut u128, order: Ordering) -> u128 {
debug_assert!(src as usize % 16 == 0);
debug_assert_lse!();
unsafe {
let (out_lo, out_hi);
macro_rules! atomic_load {
($acquire:tt, $release:tt) => {
asm!(
start_lse!(),
concat!("casp", $acquire, $release, " x2, x3, x2, x3, [{src}]"),
src = in(reg) ptr_reg!(src),
inout("x2") 0_u64 => out_lo,
inout("x3") 0_u64 => out_hi,
options(nostack, preserves_flags),
)
};
}
match order {
Ordering::Relaxed => atomic_load!("", ""),
Ordering::Acquire => atomic_load!("a", ""),
Ordering::SeqCst => atomic_load!("a", "l"),
_ => unreachable!(),
}
U128 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
}
}
#[cfg(any(
test,
all(
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
not(any(target_feature = "lse", portable_atomic_target_feature = "lse")),
),
))]
#[inline]
unsafe fn _atomic_load_ldxp_stxp(src: *mut u128, order: Ordering) -> u128 {
debug_assert!(src as usize % 16 == 0);
unsafe {
let (mut out_lo, mut out_hi);
macro_rules! atomic_load {
($acquire:tt, $release:tt) => {
asm!(
"2:",
concat!("ld", $acquire, "xp {out_lo}, {out_hi}, [{src}]"),
concat!("st", $release, "xp {r:w}, {out_lo}, {out_hi}, [{src}]"),
"cbnz {r:w}, 2b",
src = in(reg) ptr_reg!(src),
out_lo = out(reg) out_lo,
out_hi = out(reg) out_hi,
r = out(reg) _,
options(nostack, preserves_flags),
)
};
}
match order {
Ordering::Relaxed => atomic_load!("", ""),
Ordering::Acquire => atomic_load!("a", ""),
Ordering::SeqCst => atomic_load!("a", "l"),
_ => unreachable!(),
}
U128 { pair: Pair { lo: out_lo, hi: out_hi } }.whole
}
}
cfg_sel!({
#[cfg(any(target_feature = "lse2", portable_atomic_target_feature = "lse2"))]
{
use self::_atomic_store_stp as atomic_store;
}
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
// These don't support detection of FEAT_LSE2.
// target_os = "fuchsia",
// windows,
),
))]
{
#[inline]
unsafe fn atomic_store(dst: *mut u128, val: u128, order: Ordering) {
#[cfg(any(
target_feature = "lse128",
portable_atomic_target_feature = "lse128",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
#[inline]
unsafe fn _atomic_store_swpp(dst: *mut u128, val: u128, order: Ordering) {
unsafe {
_atomic_swap_swpp(dst, val, order);
}
}
fn_alias! {
#[inline(never)]
unsafe fn(dst: *mut u128, val: u128);
atomic_store_lse2_relaxed = _atomic_store_stp(Ordering::Relaxed);
atomic_store_lse2_release = _atomic_store_stp(Ordering::Release);
atomic_store_lse2_seqcst = _atomic_store_stp(Ordering::SeqCst);
atomic_store_lse2_rcpc3_release = _atomic_store_stilp(Ordering::Release);
atomic_store_lse2_rcpc3_seqcst = _atomic_store_stilp(Ordering::SeqCst);
atomic_store_lse128_release = _atomic_store_swpp(Ordering::Release);
atomic_store_lse128_seqcst = _atomic_store_swpp(Ordering::SeqCst);
}
fn_alias! {
unsafe fn(dst: *mut u128, val: u128);
atomic_store_no_lse2_relaxed = atomic_store_no_lse2(Ordering::Relaxed);
atomic_store_no_lse2_release = atomic_store_no_lse2(Ordering::Release);
atomic_store_no_lse2_seqcst = atomic_store_no_lse2(Ordering::SeqCst);
}
unsafe {
match order {
Ordering::Relaxed => {
ifunc!(unsafe fn(dst: *mut u128, val: u128) {
let cpuinfo = detect::detect();
if cpuinfo.lse2() {
atomic_store_lse2_relaxed
} else {
atomic_store_no_lse2_relaxed
}
});
}
Ordering::Release => {
ifunc!(unsafe fn(dst: *mut u128, val: u128) {
let cpuinfo = detect::detect();
if cpuinfo.lse2() {
if cpuinfo.rcpc3() {
atomic_store_lse2_rcpc3_release
} else if cpuinfo.lse128() {
atomic_store_lse128_release
} else {
atomic_store_lse2_release
}
} else {
atomic_store_no_lse2_release
}
});
}
Ordering::SeqCst => {
ifunc!(unsafe fn(dst: *mut u128, val: u128) {
let cpuinfo = detect::detect();
if cpuinfo.lse2() {
if cpuinfo.lse128() {
atomic_store_lse128_seqcst
} else if cpuinfo.rcpc3() {
atomic_store_lse2_rcpc3_seqcst
} else {
atomic_store_lse2_seqcst
}
} else {
atomic_store_no_lse2_seqcst
}
});
}
_ => unreachable!(),
}
}
}
}
#[cfg(else)]
{
use self::atomic_store_no_lse2 as atomic_store;
}
});
#[cfg(not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")))]
cfg_sel!({
#[cfg(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))]
{
#[inline]
unsafe fn atomic_store_no_lse2(dst: *mut u128, val: u128, order: Ordering) {
unsafe {
_atomic_swap_casp(dst, val, order);
}
}
}
#[cfg(else)]
{
use self::_atomic_store_ldxp_stxp as atomic_store_no_lse2;
}
});
#[cfg(any(
target_feature = "lse2",
portable_atomic_target_feature = "lse2",
not(portable_atomic_no_outline_atomics),
))]
#[inline]
unsafe fn _atomic_store_stp(dst: *mut u128, val: u128, order: Ordering) {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse2!();
unsafe {
macro_rules! atomic_store {
($acquire:tt, $release:tt) => {{
let val = U128 { whole: val };
asm!(
$release,
"stp {val_lo}, {val_hi}, [{dst}]",
$acquire,
dst = in(reg) ptr_reg!(dst),
val_lo = in(reg) val.pair.lo,
val_hi = in(reg) val.pair.hi,
options(nostack, preserves_flags),
);
}};
}
match order {
#[cfg(any(target_feature = "lse128", portable_atomic_target_feature = "lse128"))]
Ordering::SeqCst => {
_atomic_swap_swpp(dst, val, order);
}
#[cfg(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3"))]
Ordering::Release => _atomic_store_stilp(dst, val, order),
#[cfg(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3"))]
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
Ordering::SeqCst => _atomic_store_stilp(dst, val, order),
#[cfg(not(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3")))]
#[cfg(any(target_feature = "lse128", portable_atomic_target_feature = "lse128"))]
Ordering::Release => {
_atomic_swap_swpp(dst, val, order);
}
Ordering::Relaxed => atomic_store!("", ""),
#[cfg(not(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3")))]
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
Ordering::Release => atomic_store!("", "dmb ish"),
#[cfg(not(any(target_feature = "rcpc3", portable_atomic_target_feature = "rcpc3")))]
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
Ordering::SeqCst => atomic_store!("dmb ish", "dmb ish"),
_ => unreachable!(),
}
}
}
#[cfg(any(
target_feature = "lse2",
portable_atomic_target_feature = "lse2",
not(portable_atomic_no_outline_atomics),
))]
#[cfg(any(
target_feature = "rcpc3",
portable_atomic_target_feature = "rcpc3",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
#[inline]
unsafe fn _atomic_store_stilp(dst: *mut u128, val: u128, order: Ordering) {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse2!();
debug_assert_rcpc3!();
unsafe {
macro_rules! atomic_store {
($acquire:tt) => {{
let val = U128 { whole: val };
#[cfg(not(portable_atomic_pre_llvm_16))]
asm!(
start_rcpc3!(),
"stilp {val_lo}, {val_hi}, [{dst}]",
$acquire,
dst = in(reg) ptr_reg!(dst),
val_lo = in(reg) val.pair.lo,
val_hi = in(reg) val.pair.hi,
options(nostack, preserves_flags),
);
#[cfg(portable_atomic_pre_llvm_16)]
asm!(
".inst 0xd9031802",
$acquire,
in("x0") ptr_reg!(dst),
in("x2") val.pair.lo,
in("x3") val.pair.hi,
options(nostack, preserves_flags),
);
}};
}
match order {
Ordering::Release => atomic_store!(""),
Ordering::SeqCst => atomic_store!("dmb ish"),
_ => unreachable!(),
}
}
}
#[cfg(any(
test,
not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))
))]
#[inline]
unsafe fn _atomic_store_ldxp_stxp(dst: *mut u128, val: u128, order: Ordering) {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let val = U128 { whole: val };
macro_rules! store {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
"2:",
concat!("ld", $acquire, "xp xzr, {tmp}, [{dst}]"),
concat!("st", $release, "xp {tmp:w}, {val_lo}, {val_hi}, [{dst}]"),
"cbnz {tmp:w}, 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = in(reg) val.pair.lo,
val_hi = in(reg) val.pair.hi,
tmp = out(reg) _,
options(nostack, preserves_flags),
)
};
}
atomic_rmw!(store, order);
}
}
#[inline]
unsafe fn atomic_compare_exchange(
dst: *mut u128,
old: u128,
new: u128,
success: Ordering,
failure: Ordering,
) -> Result<u128, u128> {
#[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))]
let prev = unsafe { _atomic_compare_exchange_casp(dst, old, new, success, failure) };
#[cfg(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
target_os = "fuchsia",
windows,
),
))]
#[cfg(not(any(target_feature = "lse", portable_atomic_target_feature = "lse")))]
let prev = {
fn_alias! {
#[inline(never)]
unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128;
atomic_compare_exchange_casp_relaxed
= _atomic_compare_exchange_casp(Ordering::Relaxed, Ordering::Relaxed);
atomic_compare_exchange_casp_acquire
= _atomic_compare_exchange_casp(Ordering::Acquire, Ordering::Acquire);
atomic_compare_exchange_casp_release
= _atomic_compare_exchange_casp(Ordering::Release, Ordering::Relaxed);
atomic_compare_exchange_casp_acqrel
= _atomic_compare_exchange_casp(Ordering::AcqRel, Ordering::Acquire);
#[cfg(target_env = "msvc")]
atomic_compare_exchange_casp_seqcst
= _atomic_compare_exchange_casp(Ordering::SeqCst, Ordering::SeqCst);
}
fn_alias! {
unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128;
atomic_compare_exchange_ldxp_stxp_relaxed
= _atomic_compare_exchange_ldxp_stxp(Ordering::Relaxed, Ordering::Relaxed);
atomic_compare_exchange_ldxp_stxp_acquire
= _atomic_compare_exchange_ldxp_stxp(Ordering::Acquire, Ordering::Acquire);
atomic_compare_exchange_ldxp_stxp_release
= _atomic_compare_exchange_ldxp_stxp(Ordering::Release, Ordering::Relaxed);
atomic_compare_exchange_ldxp_stxp_acqrel
= _atomic_compare_exchange_ldxp_stxp(Ordering::AcqRel, Ordering::Acquire);
#[cfg(target_env = "msvc")]
atomic_compare_exchange_ldxp_stxp_seqcst
= _atomic_compare_exchange_ldxp_stxp(Ordering::SeqCst, Ordering::SeqCst);
}
unsafe {
let success = crate::utils::upgrade_success_ordering(success, failure);
match success {
Ordering::Relaxed => {
ifunc!(unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128 {
if detect::detect().lse() {
atomic_compare_exchange_casp_relaxed
} else {
atomic_compare_exchange_ldxp_stxp_relaxed
}
})
}
Ordering::Acquire => {
ifunc!(unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128 {
if detect::detect().lse() {
atomic_compare_exchange_casp_acquire
} else {
atomic_compare_exchange_ldxp_stxp_acquire
}
})
}
Ordering::Release => {
ifunc!(unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128 {
if detect::detect().lse() {
atomic_compare_exchange_casp_release
} else {
atomic_compare_exchange_ldxp_stxp_release
}
})
}
#[cfg(not(target_env = "msvc"))]
Ordering::AcqRel | Ordering::SeqCst => {
ifunc!(unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128 {
if detect::detect().lse() {
atomic_compare_exchange_casp_acqrel
} else {
atomic_compare_exchange_ldxp_stxp_acqrel
}
})
}
#[cfg(target_env = "msvc")]
Ordering::AcqRel => {
ifunc!(unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128 {
if detect::detect().lse() {
atomic_compare_exchange_casp_acqrel
} else {
atomic_compare_exchange_ldxp_stxp_acqrel
}
})
}
#[cfg(target_env = "msvc")]
Ordering::SeqCst => {
ifunc!(unsafe fn(dst: *mut u128, old: u128, new: u128) -> u128 {
if detect::detect().lse() {
atomic_compare_exchange_casp_seqcst
} else {
atomic_compare_exchange_ldxp_stxp_seqcst
}
})
}
_ => unreachable!(),
}
}
};
#[cfg(not(all(
not(portable_atomic_no_outline_atomics),
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
target_env = "musl",
any(not(target_feature = "crt-static"), feature = "std"),
),
target_env = "ohos",
all(target_env = "uclibc", not(target_feature = "crt-static")),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
all(target_os = "illumos", portable_atomic_outline_atomics),
target_os = "fuchsia",
windows,
),
)))]
#[cfg(not(any(target_feature = "lse", portable_atomic_target_feature = "lse")))]
let prev = unsafe { _atomic_compare_exchange_ldxp_stxp(dst, old, new, success, failure) };
if prev == old { Ok(prev) } else { Err(prev) }
}
#[cfg(any(
target_feature = "lse",
portable_atomic_target_feature = "lse",
not(portable_atomic_no_outline_atomics),
))]
#[inline]
unsafe fn _atomic_compare_exchange_casp(
dst: *mut u128,
old: u128,
new: u128,
success: Ordering,
failure: Ordering,
) -> u128 {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse!();
let order = crate::utils::upgrade_success_ordering(success, failure);
unsafe {
let old = U128 { whole: old };
let new = U128 { whole: new };
let (prev_lo, prev_hi);
macro_rules! cmpxchg {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse!(),
concat!("casp", $acquire, $release, " x6, x7, x4, x5, [{dst}]"),
$fence,
dst = in(reg) ptr_reg!(dst),
inout("x6") old.pair.lo => prev_lo,
inout("x7") old.pair.hi => prev_hi,
in("x4") new.pair.lo,
in("x5") new.pair.hi,
options(nostack, preserves_flags),
)
};
}
atomic_rmw!(cmpxchg, order, write = success);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))]
#[inline]
unsafe fn _atomic_compare_exchange_ldxp_stxp(
dst: *mut u128,
old: u128,
new: u128,
success: Ordering,
failure: Ordering,
) -> u128 {
debug_assert!(dst as usize % 16 == 0);
let order = crate::utils::upgrade_success_ordering(success, failure);
unsafe {
let old = U128 { whole: old };
let new = U128 { whole: new };
let (mut prev_lo, mut prev_hi);
macro_rules! cmpxchg {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
"2:",
concat!("ld", $acquire, "xp {prev_lo}, {prev_hi}, [{dst}]"),
"cmp {prev_lo}, {old_lo}",
"cset {r:w}, ne",
"cmp {prev_hi}, {old_hi}",
"cinc {r:w}, {r:w}, ne",
"cbz {r:w}, 3f",
concat!("st", $release, "xp {r:w}, {prev_lo}, {prev_hi}, [{dst}]"),
"cbnz {r:w}, 2b",
"b 4f",
"3:",
concat!("st", $release, "xp {r:w}, {new_lo}, {new_hi}, [{dst}]"),
"cbnz {r:w}, 2b",
"4:",
$fence,
dst = in(reg) ptr_reg!(dst),
old_lo = in(reg) old.pair.lo,
old_hi = in(reg) old.pair.hi,
new_lo = in(reg) new.pair.lo,
new_hi = in(reg) new.pair.hi,
prev_lo = out(reg) prev_lo,
prev_hi = out(reg) prev_hi,
r = out(reg) _,
options(nostack),
)
};
}
atomic_rmw!(cmpxchg, order, write = success);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
use self::atomic_compare_exchange as atomic_compare_exchange_weak;
cfg_sel!({
#[cfg(any(target_feature = "lse128", portable_atomic_target_feature = "lse128"))]
{
use self::_atomic_swap_swpp as atomic_swap;
}
#[cfg(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))]
{
use self::_atomic_swap_casp as atomic_swap;
}
#[cfg(else)]
{
use self::_atomic_swap_ldxp_stxp as atomic_swap;
}
});
#[cfg(any(
target_feature = "lse128",
portable_atomic_target_feature = "lse128",
all(
not(portable_atomic_no_outline_atomics),
not(any(target_feature = "lse2", portable_atomic_target_feature = "lse2")),
),
))]
#[inline]
unsafe fn _atomic_swap_swpp(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse128!();
unsafe {
let val = U128 { whole: val };
let (prev_lo, prev_hi);
#[cfg(not(portable_atomic_pre_llvm_16))]
macro_rules! swap {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse128!(),
concat!("swpp", $acquire, $release, " {val_lo}, {val_hi}, [{dst}]"),
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = inout(reg) val.pair.lo => prev_lo,
val_hi = inout(reg) val.pair.hi => prev_hi,
options(nostack, preserves_flags),
)
};
}
#[cfg(not(portable_atomic_pre_llvm_16))]
atomic_rmw!(swap, order);
#[cfg(portable_atomic_pre_llvm_16)]
macro_rules! swap {
($order:tt, $fence:tt) => {
asm!(
concat!(".inst 0x19", $order, "18002"),
$fence,
in("x0") ptr_reg!(dst),
inout("x2") val.pair.lo => prev_lo,
inout("x1") val.pair.hi => prev_hi,
options(nostack, preserves_flags),
)
};
}
#[cfg(portable_atomic_pre_llvm_16)]
atomic_rmw_inst!(swap, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
#[cfg(any(test, not(portable_atomic_ll_sc_rmw)))]
#[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))]
#[inline]
unsafe fn _atomic_swap_casp(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse!();
unsafe {
let val = U128 { whole: val };
let (mut prev_lo, mut prev_hi);
macro_rules! swap {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse!(),
"ldp x4, x5, [{dst}]",
"2:",
"mov {tmp_lo}, x4",
"mov {tmp_hi}, x5",
concat!("casp", $acquire, $release, " x4, x5, x2, x3, [{dst}]"),
"cmp {tmp_hi}, x5",
"ccmp {tmp_lo}, x4, #0, eq",
"b.ne 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
tmp_lo = out(reg) _,
tmp_hi = out(reg) _,
out("x4") prev_lo,
out("x5") prev_hi,
in("x2") val.pair.lo,
in("x3") val.pair.hi,
options(nostack),
)
};
}
atomic_rmw!(swap, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
#[cfg(any(
test,
not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))
))]
#[inline]
unsafe fn _atomic_swap_ldxp_stxp(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let val = U128 { whole: val };
let (mut prev_lo, mut prev_hi);
macro_rules! swap {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
"2:",
concat!("ld", $acquire, "xp {prev_lo}, {prev_hi}, [{dst}]"),
concat!("st", $release, "xp {r:w}, {val_lo}, {val_hi}, [{dst}]"),
"cbnz {r:w}, 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = in(reg) val.pair.lo,
val_hi = in(reg) val.pair.hi,
prev_lo = out(reg) prev_lo,
prev_hi = out(reg) prev_hi,
r = out(reg) _,
options(nostack, preserves_flags),
)
};
}
atomic_rmw!(swap, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
macro_rules! atomic_rmw_ll_sc_3 {
($name:ident as $reexport_name:ident $(($preserves_flags:tt))?, $($op:tt)*) => {
#[cfg(not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
)))]
use self::$name as $reexport_name;
#[cfg(any(
test,
not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))
))]
#[inline]
unsafe fn $name(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let val = U128 { whole: val };
let (mut prev_lo, mut prev_hi);
macro_rules! op {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
"2:",
concat!("ld", $acquire, "xp {prev_lo}, {prev_hi}, [{dst}]"),
$($op)*
concat!("st", $release, "xp {r:w}, {new_lo}, {new_hi}, [{dst}]"),
"cbnz {r:w}, 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = in(reg) val.pair.lo,
val_hi = in(reg) val.pair.hi,
prev_lo = out(reg) prev_lo,
prev_hi = out(reg) prev_hi,
new_lo = out(reg) _,
new_hi = out(reg) _,
r = out(reg) _,
options(nostack $(, $preserves_flags)?),
)
};
}
atomic_rmw!(op, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
};
}
macro_rules! atomic_rmw_cas_3 {
($name:ident as $reexport_name:ident, $($op:tt)*) => {
#[cfg(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))]
use self::$name as $reexport_name;
#[cfg(any(test, not(portable_atomic_ll_sc_rmw)))]
#[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))]
#[inline]
unsafe fn $name(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse!();
unsafe {
let val = U128 { whole: val };
let (mut prev_lo, mut prev_hi);
macro_rules! op {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse!(),
"ldp x6, x7, [{dst}]",
"2:",
"mov {tmp_lo}, x6",
"mov {tmp_hi}, x7",
$($op)*
concat!("casp", $acquire, $release, " x6, x7, x4, x5, [{dst}]"),
"cmp {tmp_hi}, x7",
"ccmp {tmp_lo}, x6, #0, eq",
"b.ne 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = in(reg) val.pair.lo,
val_hi = in(reg) val.pair.hi,
tmp_lo = out(reg) _,
tmp_hi = out(reg) _,
out("x6") prev_lo,
out("x7") prev_hi,
out("x4") _,
out("x5") _,
options(nostack),
)
};
}
atomic_rmw!(op, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
};
}
macro_rules! atomic_rmw_ll_sc_2 {
($name:ident as $reexport_name:ident $(($preserves_flags:tt))?, $($op:tt)*) => {
#[cfg(not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
)))]
use self::$name as $reexport_name;
#[cfg(any(
test,
not(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))
))]
#[inline]
unsafe fn $name(dst: *mut u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let (mut prev_lo, mut prev_hi);
macro_rules! op {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
"2:",
concat!("ld", $acquire, "xp {prev_lo}, {prev_hi}, [{dst}]"),
$($op)*
concat!("st", $release, "xp {r:w}, {new_lo}, {new_hi}, [{dst}]"),
"cbnz {r:w}, 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
prev_lo = out(reg) prev_lo,
prev_hi = out(reg) prev_hi,
new_lo = out(reg) _,
new_hi = out(reg) _,
r = out(reg) _,
options(nostack $(, $preserves_flags)?),
)
};
}
atomic_rmw!(op, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
};
}
macro_rules! atomic_rmw_cas_2 {
($name:ident as $reexport_name:ident, $($op:tt)*) => {
#[cfg(all(
any(target_feature = "lse", portable_atomic_target_feature = "lse"),
not(portable_atomic_ll_sc_rmw),
))]
use self::$name as $reexport_name;
#[cfg(any(test, not(portable_atomic_ll_sc_rmw)))]
#[cfg(any(target_feature = "lse", portable_atomic_target_feature = "lse"))]
#[inline]
unsafe fn $name(dst: *mut u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
debug_assert_lse!();
unsafe {
let (mut prev_lo, mut prev_hi);
macro_rules! op {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse!(),
"ldp x6, x7, [{dst}]",
"2:",
"mov {tmp_lo}, x6",
"mov {tmp_hi}, x7",
$($op)*
concat!("casp", $acquire, $release, " x6, x7, x4, x5, [{dst}]"),
"cmp {tmp_hi}, x7",
"ccmp {tmp_lo}, x6, #0, eq",
"b.ne 2b",
$fence,
dst = in(reg) ptr_reg!(dst),
tmp_lo = out(reg) _,
tmp_hi = out(reg) _,
out("x6") prev_lo,
out("x7") prev_hi,
out("x4") _,
out("x5") _,
options(nostack),
)
};
}
atomic_rmw!(op, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
};
}
atomic_rmw_ll_sc_3! {
_atomic_add_ldxp_stxp as atomic_add,
select_le_or_be!("adds {new_lo}, {prev_lo}, {val_lo}", "adds {new_hi}, {prev_hi}, {val_hi}"),
select_le_or_be!("adc {new_hi}, {prev_hi}, {val_hi}", "adc {new_lo}, {prev_lo}, {val_lo}"),
}
atomic_rmw_cas_3! {
_atomic_add_casp as atomic_add,
select_le_or_be!("adds x4, x6, {val_lo}", "adds x5, x7, {val_hi}"),
select_le_or_be!("adc x5, x7, {val_hi}", "adc x4, x6, {val_lo}"),
}
atomic_rmw_ll_sc_3! {
_atomic_sub_ldxp_stxp as atomic_sub,
select_le_or_be!("subs {new_lo}, {prev_lo}, {val_lo}", "subs {new_hi}, {prev_hi}, {val_hi}"),
select_le_or_be!("sbc {new_hi}, {prev_hi}, {val_hi}", "sbc {new_lo}, {prev_lo}, {val_lo}"),
}
atomic_rmw_cas_3! {
_atomic_sub_casp as atomic_sub,
select_le_or_be!("subs x4, x6, {val_lo}", "subs x5, x7, {val_hi}"),
select_le_or_be!("sbc x5, x7, {val_hi}", "sbc x4, x6, {val_lo}"),
}
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
atomic_rmw_ll_sc_3! {
_atomic_and_ldxp_stxp as atomic_and (preserves_flags),
"and {new_lo}, {prev_lo}, {val_lo}",
"and {new_hi}, {prev_hi}, {val_hi}",
}
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
atomic_rmw_cas_3! {
_atomic_and_casp as atomic_and,
"and x4, x6, {val_lo}",
"and x5, x7, {val_hi}",
}
#[cfg(any(target_feature = "lse128", portable_atomic_target_feature = "lse128"))]
#[inline]
unsafe fn atomic_and(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let val = U128 { whole: !val };
let (prev_lo, prev_hi);
#[cfg(not(portable_atomic_pre_llvm_16))]
macro_rules! clear {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse128!(),
concat!("ldclrp", $acquire, $release, " {val_lo}, {val_hi}, [{dst}]"),
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = inout(reg) val.pair.lo => prev_lo,
val_hi = inout(reg) val.pair.hi => prev_hi,
options(nostack, preserves_flags),
)
};
}
#[cfg(not(portable_atomic_pre_llvm_16))]
atomic_rmw!(clear, order);
#[cfg(portable_atomic_pre_llvm_16)]
macro_rules! clear {
($order:tt, $fence:tt) => {
asm!(
concat!(".inst 0x19", $order, "11008"),
$fence,
in("x0") ptr_reg!(dst),
inout("x8") val.pair.lo => prev_lo,
inout("x1") val.pair.hi => prev_hi,
options(nostack, preserves_flags),
)
};
}
#[cfg(portable_atomic_pre_llvm_16)]
atomic_rmw_inst!(clear, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
atomic_rmw_ll_sc_3! {
_atomic_nand_ldxp_stxp as atomic_nand (preserves_flags),
"and {new_lo}, {prev_lo}, {val_lo}",
"and {new_hi}, {prev_hi}, {val_hi}",
"mvn {new_lo}, {new_lo}",
"mvn {new_hi}, {new_hi}",
}
atomic_rmw_cas_3! {
_atomic_nand_casp as atomic_nand,
"and x4, x6, {val_lo}",
"and x5, x7, {val_hi}",
"mvn x4, x4",
"mvn x5, x5",
}
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
atomic_rmw_ll_sc_3! {
_atomic_or_ldxp_stxp as atomic_or (preserves_flags),
"orr {new_lo}, {prev_lo}, {val_lo}",
"orr {new_hi}, {prev_hi}, {val_hi}",
}
#[cfg(not(any(target_feature = "lse128", portable_atomic_target_feature = "lse128")))]
atomic_rmw_cas_3! {
_atomic_or_casp as atomic_or,
"orr x4, x6, {val_lo}",
"orr x5, x7, {val_hi}",
}
#[cfg(any(target_feature = "lse128", portable_atomic_target_feature = "lse128"))]
#[inline]
unsafe fn atomic_or(dst: *mut u128, val: u128, order: Ordering) -> u128 {
debug_assert!(dst as usize % 16 == 0);
unsafe {
let val = U128 { whole: val };
let (prev_lo, prev_hi);
#[cfg(not(portable_atomic_pre_llvm_16))]
macro_rules! or {
($acquire:tt, $release:tt, $fence:tt) => {
asm!(
start_lse128!(),
concat!("ldsetp", $acquire, $release, " {val_lo}, {val_hi}, [{dst}]"),
$fence,
dst = in(reg) ptr_reg!(dst),
val_lo = inout(reg) val.pair.lo => prev_lo,
val_hi = inout(reg) val.pair.hi => prev_hi,
options(nostack, preserves_flags),
)
};
}
#[cfg(not(portable_atomic_pre_llvm_16))]
atomic_rmw!(or, order);
#[cfg(portable_atomic_pre_llvm_16)]
macro_rules! or {
($order:tt, $fence:tt) => {
asm!(
concat!(".inst 0x19", $order, "13002"),
$fence,
in("x0") ptr_reg!(dst),
inout("x2") val.pair.lo => prev_lo,
inout("x1") val.pair.hi => prev_hi,
options(nostack, preserves_flags),
)
};
}
#[cfg(portable_atomic_pre_llvm_16)]
atomic_rmw_inst!(or, order);
U128 { pair: Pair { lo: prev_lo, hi: prev_hi } }.whole
}
}
atomic_rmw_ll_sc_3! {
_atomic_xor_ldxp_stxp as atomic_xor (preserves_flags),
"eor {new_lo}, {prev_lo}, {val_lo}",
"eor {new_hi}, {prev_hi}, {val_hi}",
}
atomic_rmw_cas_3! {
_atomic_xor_casp as atomic_xor,
"eor x4, x6, {val_lo}",
"eor x5, x7, {val_hi}",
}
atomic_rmw_ll_sc_2! {
_atomic_not_ldxp_stxp as atomic_not (preserves_flags),
"mvn {new_lo}, {prev_lo}",
"mvn {new_hi}, {prev_hi}",
}
atomic_rmw_cas_2! {
_atomic_not_casp as atomic_not,
"mvn x4, x6",
"mvn x5, x7",
}
atomic_rmw_ll_sc_2! {
_atomic_neg_ldxp_stxp as atomic_neg,
select_le_or_be!("negs {new_lo}, {prev_lo}", "negs {new_hi}, {prev_hi}"),
select_le_or_be!("ngc {new_hi}, {prev_hi}", "ngc {new_lo}, {prev_lo}"),
}
atomic_rmw_cas_2! {
_atomic_neg_casp as atomic_neg,
select_le_or_be!("negs x4, x6", "negs x5, x7"),
select_le_or_be!("ngc x5, x7", "ngc x4, x6"),
}
atomic_rmw_ll_sc_3! {
_atomic_max_ldxp_stxp as atomic_max,
select_le_or_be!("cmp {val_lo}, {prev_lo}", "cmp {val_hi}, {prev_hi}"),
select_le_or_be!("sbcs xzr, {val_hi}, {prev_hi}", "sbcs xzr, {val_lo}, {prev_lo}"),
"csel {new_hi}, {prev_hi}, {val_hi}, lt", "csel {new_lo}, {prev_lo}, {val_lo}, lt", }
atomic_rmw_cas_3! {
_atomic_max_casp as atomic_max,
select_le_or_be!("cmp {val_lo}, x6", "cmp {val_hi}, x7"),
select_le_or_be!("sbcs xzr, {val_hi}, x7", "sbcs xzr, {val_lo}, x6"),
"csel x5, x7, {val_hi}, lt", "csel x4, x6, {val_lo}, lt", }
atomic_rmw_ll_sc_3! {
_atomic_umax_ldxp_stxp as atomic_umax,
select_le_or_be!("cmp {val_lo}, {prev_lo}", "cmp {val_hi}, {prev_hi}"),
select_le_or_be!("sbcs xzr, {val_hi}, {prev_hi}", "sbcs xzr, {val_lo}, {prev_lo}"),
"csel {new_hi}, {prev_hi}, {val_hi}, lo", "csel {new_lo}, {prev_lo}, {val_lo}, lo", }
atomic_rmw_cas_3! {
_atomic_umax_casp as atomic_umax,
select_le_or_be!("cmp {val_lo}, x6", "cmp {val_hi}, x7"),
select_le_or_be!("sbcs xzr, {val_hi}, x7", "sbcs xzr, {val_lo}, x6"),
"csel x5, x7, {val_hi}, lo", "csel x4, x6, {val_lo}, lo", }
atomic_rmw_ll_sc_3! {
_atomic_min_ldxp_stxp as atomic_min,
select_le_or_be!("cmp {val_lo}, {prev_lo}", "cmp {val_hi}, {prev_hi}"),
select_le_or_be!("sbcs xzr, {val_hi}, {prev_hi}", "sbcs xzr, {val_lo}, {prev_lo}"),
"csel {new_hi}, {prev_hi}, {val_hi}, ge", "csel {new_lo}, {prev_lo}, {val_lo}, ge", }
atomic_rmw_cas_3! {
_atomic_min_casp as atomic_min,
select_le_or_be!("cmp {val_lo}, x6", "cmp {val_hi}, x7"),
select_le_or_be!("sbcs xzr, {val_hi}, x7", "sbcs xzr, {val_lo}, x6"),
"csel x5, x7, {val_hi}, ge", "csel x4, x6, {val_lo}, ge", }
atomic_rmw_ll_sc_3! {
_atomic_umin_ldxp_stxp as atomic_umin,
select_le_or_be!("cmp {val_lo}, {prev_lo}", "cmp {val_hi}, {prev_hi}"),
select_le_or_be!("sbcs xzr, {val_hi}, {prev_hi}", "sbcs xzr, {val_lo}, {prev_lo}"),
"csel {new_hi}, {prev_hi}, {val_hi}, hs", "csel {new_lo}, {prev_lo}, {val_lo}, hs", }
atomic_rmw_cas_3! {
_atomic_umin_casp as atomic_umin,
select_le_or_be!("cmp {val_lo}, x6", "cmp {val_hi}, x7"),
select_le_or_be!("sbcs xzr, {val_hi}, x7", "sbcs xzr, {val_lo}, x6"),
"csel x5, x7, {val_hi}, hs", "csel x4, x6, {val_lo}, hs", }
#[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);
}