blob: c70533cc05c93a5fc0295f1a1642466fd1b0ca12 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR MIT
#![cfg_attr(not(all(test, feature = "float")), allow(dead_code, unused_macros))]
#[macro_use]
#[path = "gen/utils.rs"]
mod gen;
use core::sync::atomic::Ordering;
macro_rules! static_assert {
($cond:expr $(,)?) => {{
let [] = [(); true as usize - $crate::utils::_assert_is_bool($cond) as usize];
}};
}
pub(crate) const fn _assert_is_bool(v: bool) -> bool {
v
}
macro_rules! static_assert_layout {
($atomic_type:ty, $value_type:ty) => {
static_assert!(
core::mem::align_of::<$atomic_type>() == core::mem::size_of::<$atomic_type>()
);
static_assert!(core::mem::size_of::<$atomic_type>() == core::mem::size_of::<$value_type>());
};
}
// #[doc = concat!(...)] requires Rust 1.54
macro_rules! doc_comment {
($doc:expr, $($tt:tt)*) => {
#[doc = $doc]
$($tt)*
};
}
// Adapted from https://github.com/BurntSushi/memchr/blob/2.4.1/src/memchr/x86/mod.rs#L9-L71.
/// # Safety
///
/// - the caller must uphold the safety contract for the function returned by $detect_body.
/// - the memory pointed by the function pointer returned by $detect_body must be visible from any threads.
///
/// The second requirement is always met if the function pointer is to the function definition.
/// (Currently, all uses of this macro in our code are in this case.)
#[allow(unused_macros)]
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
all(target_arch = "x86_64", not(any(target_env = "sgx", miri))),
))]
macro_rules! ifunc {
(unsafe fn($($arg_pat:ident: $arg_ty:ty),*) $(-> $ret_ty:ty)? { $($detect_body:tt)* }) => {{
type FnTy = unsafe fn($($arg_ty),*) $(-> $ret_ty)?;
static FUNC: core::sync::atomic::AtomicPtr<()>
= core::sync::atomic::AtomicPtr::new(detect as *mut ());
#[cold]
unsafe fn detect($($arg_pat: $arg_ty),*) $(-> $ret_ty)? {
let func: FnTy = { $($detect_body)* };
FUNC.store(func as *mut (), core::sync::atomic::Ordering::Relaxed);
// SAFETY: the caller must uphold the safety contract for the function returned by $detect_body.
unsafe { func($($arg_pat),*) }
}
// SAFETY: `FnTy` is a function pointer, which is always safe to transmute with a `*mut ()`.
// (To force the caller to use unsafe block for this macro, do not use
// unsafe block here.)
let func = {
core::mem::transmute::<*mut (), FnTy>(FUNC.load(core::sync::atomic::Ordering::Relaxed))
};
// SAFETY: the caller must uphold the safety contract for the function returned by $detect_body.
// (To force the caller to use unsafe block for this macro, do not use
// unsafe block here.)
func($($arg_pat),*)
}};
}
#[allow(unused_macros)]
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc64",
all(target_arch = "x86_64", not(any(target_env = "sgx", miri))),
))]
macro_rules! fn_alias {
(
$(#[$($fn_attr:tt)*])*
$vis:vis unsafe fn($($arg_pat:ident: $arg_ty:ty),*) $(-> $ret_ty:ty)?;
$(#[$($alias_attr:tt)*])*
$new:ident = $from:ident($($last_args:tt)*);
$($rest:tt)*
) => {
$(#[$($fn_attr)*])*
$(#[$($alias_attr)*])*
$vis unsafe fn $new($($arg_pat: $arg_ty),*) $(-> $ret_ty)? {
// SAFETY: the caller must uphold the safety contract.
unsafe { $from($($arg_pat,)* $($last_args)*) }
}
fn_alias! {
$(#[$($fn_attr)*])*
$vis unsafe fn($($arg_pat: $arg_ty),*) $(-> $ret_ty)?;
$($rest)*
}
};
(
$(#[$($attr:tt)*])*
$vis:vis unsafe fn($($arg_pat:ident: $arg_ty:ty),*) $(-> $ret_ty:ty)?;
) => {}
}
/// Make the given function const if the given condition is true.
macro_rules! const_fn {
(
const_if: #[cfg($($cfg:tt)+)];
$(#[$($attr:tt)*])*
$vis:vis const fn $($rest:tt)*
) => {
#[cfg($($cfg)+)]
$(#[$($attr)*])*
$vis const fn $($rest)*
#[cfg(not($($cfg)+))]
$(#[$($attr)*])*
$vis fn $($rest)*
};
}
/// Implements `core::fmt::Debug` and `serde::{Serialize, Deserialize}` (when serde
/// feature is enabled) for atomic bool, integer, or float.
macro_rules! impl_debug_and_serde {
($atomic_type:ident) => {
impl fmt::Debug for $atomic_type {
#[allow(clippy::missing_inline_in_public_items)] // fmt is not hot path
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// std atomic types use Relaxed in Debug::fmt: https://github.com/rust-lang/rust/blob/1.70.0/library/core/src/sync/atomic.rs#L2024
fmt::Debug::fmt(&self.load(Ordering::Relaxed), f)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl serde::ser::Serialize for $atomic_type {
#[allow(clippy::missing_inline_in_public_items)] // serde doesn't use inline on std atomic's Serialize/Deserialize impl
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
// https://github.com/serde-rs/serde/blob/v1.0.152/serde/src/ser/impls.rs#L958-L959
self.load(Ordering::Relaxed).serialize(serializer)
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> serde::de::Deserialize<'de> for $atomic_type {
#[allow(clippy::missing_inline_in_public_items)] // serde doesn't use inline on std atomic's Serialize/Deserialize impl
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
serde::de::Deserialize::deserialize(deserializer).map(Self::new)
}
}
};
}
// We do not provide `nand` because it cannot be optimized on neither x86 nor MSP430.
// https://godbolt.org/z/7TzjKqYvE
macro_rules! impl_default_no_fetch_ops {
($atomic_type:ident, bool) => {
impl $atomic_type {
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn and(&self, val: bool, order: Ordering) {
self.fetch_and(val, order);
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn or(&self, val: bool, order: Ordering) {
self.fetch_or(val, order);
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn xor(&self, val: bool, order: Ordering) {
self.fetch_xor(val, order);
}
}
};
($atomic_type:ident, $int_type:ident) => {
impl $atomic_type {
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn add(&self, val: $int_type, order: Ordering) {
self.fetch_add(val, order);
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn sub(&self, val: $int_type, order: Ordering) {
self.fetch_sub(val, order);
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn and(&self, val: $int_type, order: Ordering) {
self.fetch_and(val, order);
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn or(&self, val: $int_type, order: Ordering) {
self.fetch_or(val, order);
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn xor(&self, val: $int_type, order: Ordering) {
self.fetch_xor(val, order);
}
}
};
}
macro_rules! impl_default_bit_opts {
($atomic_type:ident, $int_type:ident) => {
impl $atomic_type {
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn bit_set(&self, bit: u32, order: Ordering) -> bool {
let mask = (1 as $int_type).wrapping_shl(bit);
self.fetch_or(mask, order) & mask != 0
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn bit_clear(&self, bit: u32, order: Ordering) -> bool {
let mask = (1 as $int_type).wrapping_shl(bit);
self.fetch_and(!mask, order) & mask != 0
}
#[inline]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
pub(crate) fn bit_toggle(&self, bit: u32, order: Ordering) -> bool {
let mask = (1 as $int_type).wrapping_shl(bit);
self.fetch_xor(mask, order) & mask != 0
}
}
};
}
// This just outputs the input as is, but can be used like an item-level block by using it with cfg.
macro_rules! items {
($($tt:tt)*) => {
$($tt)*
};
}
#[cfg(not(all(
portable_atomic_no_atomic_load_store,
not(any(
target_arch = "avr",
target_arch = "bpf",
target_arch = "msp430",
target_arch = "riscv32",
target_arch = "riscv64",
feature = "critical-section",
)),
)))]
#[macro_use]
mod atomic_ptr_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_ptr {
($($tt:tt)*) => {
$($tt)*
};
}
}
#[cfg(all(
portable_atomic_no_atomic_load_store,
not(any(
target_arch = "avr",
target_arch = "bpf",
target_arch = "msp430",
target_arch = "riscv32",
target_arch = "riscv64",
feature = "critical-section",
)),
))]
#[macro_use]
mod atomic_ptr_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_ptr {
($($tt:tt)*) => {};
}
}
#[cfg(not(all(
portable_atomic_no_atomic_load_store,
not(any(
target_arch = "avr",
target_arch = "msp430",
target_arch = "riscv32",
target_arch = "riscv64",
feature = "critical-section",
)),
)))]
#[macro_use]
mod atomic_8_16_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_8 {
($($tt:tt)*) => {
$($tt)*
};
}
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_16 {
($($tt:tt)*) => {
$($tt)*
};
}
}
#[cfg(all(
portable_atomic_no_atomic_load_store,
not(any(
target_arch = "avr",
target_arch = "msp430",
target_arch = "riscv32",
target_arch = "riscv64",
feature = "critical-section",
)),
))]
#[macro_use]
mod atomic_8_16_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_8 {
($($tt:tt)*) => {};
}
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_16 {
($($tt:tt)*) => {};
}
}
#[cfg(all(
any(not(target_pointer_width = "16"), feature = "fallback"),
not(all(
portable_atomic_no_atomic_load_store,
not(any(
target_arch = "avr",
target_arch = "msp430",
target_arch = "riscv32",
target_arch = "riscv64",
feature = "critical-section",
)),
)),
))]
#[macro_use]
mod atomic_32_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_32 {
($($tt:tt)*) => {
$($tt)*
};
}
}
#[cfg(not(all(
any(not(target_pointer_width = "16"), feature = "fallback"),
not(all(
portable_atomic_no_atomic_load_store,
not(any(
target_arch = "avr",
target_arch = "msp430",
target_arch = "riscv32",
target_arch = "riscv64",
feature = "critical-section",
)),
)),
)))]
#[macro_use]
mod atomic_32_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_32 {
($($tt:tt)*) => {};
}
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
all(
feature = "fallback",
any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
),
),
not(portable_atomic_no_atomic_64),
not(any(target_pointer_width = "16", target_pointer_width = "32")),
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(
all(
feature = "fallback",
any(
target_has_atomic = "ptr",
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
),
),
target_has_atomic = "64",
not(any(target_pointer_width = "16", target_pointer_width = "32")),
))
)]
#[macro_use]
mod atomic_64_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_64 {
($($tt:tt)*) => {
$($tt)*
};
}
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(not(any(
all(
feature = "fallback",
any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
),
),
not(portable_atomic_no_atomic_64),
not(any(target_pointer_width = "16", target_pointer_width = "32")),
)))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(not(any(
all(
feature = "fallback",
any(
target_has_atomic = "ptr",
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
),
),
target_has_atomic = "64",
not(any(target_pointer_width = "16", target_pointer_width = "32")),
)))
)]
#[macro_use]
mod atomic_64_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_64 {
($($tt:tt)*) => {};
}
}
#[cfg_attr(
not(feature = "fallback"),
cfg(any(
all(
target_arch = "aarch64",
any(not(portable_atomic_no_asm), portable_atomic_unstable_asm),
),
all(
target_arch = "x86_64",
any(not(portable_atomic_no_asm), portable_atomic_unstable_asm),
any(
target_feature = "cmpxchg16b",
portable_atomic_target_feature = "cmpxchg16b",
all(
feature = "fallback",
not(portable_atomic_no_cmpxchg16b_target_feature),
not(portable_atomic_no_outline_atomics),
not(any(target_env = "sgx", miri)),
),
),
),
all(
target_arch = "powerpc64",
portable_atomic_unstable_asm_experimental_arch,
any(
target_feature = "quadword-atomics",
portable_atomic_target_feature = "quadword-atomics",
all(
feature = "fallback",
not(portable_atomic_no_outline_atomics),
portable_atomic_outline_atomics, // TODO(powerpc64): currently disabled by default
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
any(target_env = "musl", target_env = "ohos"),
not(target_feature = "crt-static"),
),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
),
not(any(miri, portable_atomic_sanitize_thread)),
),
),
),
all(target_arch = "s390x", portable_atomic_unstable_asm_experimental_arch),
))
)]
#[cfg_attr(
all(feature = "fallback", portable_atomic_no_cfg_target_has_atomic),
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
))
)]
#[cfg_attr(
all(feature = "fallback", not(portable_atomic_no_cfg_target_has_atomic)),
cfg(any(
target_has_atomic = "ptr",
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
))
)]
#[macro_use]
mod atomic_128_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_128 {
($($tt:tt)*) => {
$($tt)*
};
}
}
#[cfg_attr(
not(feature = "fallback"),
cfg(not(any(
all(
target_arch = "aarch64",
any(not(portable_atomic_no_asm), portable_atomic_unstable_asm),
),
all(
target_arch = "x86_64",
any(not(portable_atomic_no_asm), portable_atomic_unstable_asm),
any(
target_feature = "cmpxchg16b",
portable_atomic_target_feature = "cmpxchg16b",
all(
feature = "fallback",
not(portable_atomic_no_cmpxchg16b_target_feature),
not(portable_atomic_no_outline_atomics),
not(any(target_env = "sgx", miri)),
),
),
),
all(
target_arch = "powerpc64",
portable_atomic_unstable_asm_experimental_arch,
any(
target_feature = "quadword-atomics",
portable_atomic_target_feature = "quadword-atomics",
all(
feature = "fallback",
not(portable_atomic_no_outline_atomics),
portable_atomic_outline_atomics, // TODO(powerpc64): currently disabled by default
any(
all(
target_os = "linux",
any(
target_env = "gnu",
all(
any(target_env = "musl", target_env = "ohos"),
not(target_feature = "crt-static"),
),
portable_atomic_outline_atomics,
),
),
target_os = "android",
target_os = "freebsd",
),
not(any(miri, portable_atomic_sanitize_thread)),
),
),
),
all(target_arch = "s390x", portable_atomic_unstable_asm_experimental_arch),
)))
)]
#[cfg_attr(
all(feature = "fallback", portable_atomic_no_cfg_target_has_atomic),
cfg(not(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
)))
)]
#[cfg_attr(
all(feature = "fallback", not(portable_atomic_no_cfg_target_has_atomic)),
cfg(not(any(
target_has_atomic = "ptr",
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
)))
)]
#[macro_use]
mod atomic_128_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_128 {
($($tt:tt)*) => {};
}
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(any(
target_has_atomic = "ptr",
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
))
)]
#[macro_use]
mod atomic_cas_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_cas {
($($tt:tt)*) => {
$($tt)*
};
}
}
#[cfg_attr(
portable_atomic_no_cfg_target_has_atomic,
cfg(not(any(
not(portable_atomic_no_atomic_cas),
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
)))
)]
#[cfg_attr(
not(portable_atomic_no_cfg_target_has_atomic),
cfg(not(any(
target_has_atomic = "ptr",
portable_atomic_unsafe_assume_single_core,
feature = "critical-section",
target_arch = "avr",
target_arch = "msp430",
)))
)]
#[macro_use]
mod atomic_cas_macros {
#[doc(hidden)] // Not public API. (please submit an issue if you want this to be public API)
#[macro_export]
macro_rules! cfg_has_atomic_cas {
($($tt:tt)*) => {};
}
}
// https://github.com/rust-lang/rust/blob/1.70.0/library/core/src/sync/atomic.rs#L3155
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn assert_load_ordering(order: Ordering) {
match order {
Ordering::Acquire | Ordering::Relaxed | Ordering::SeqCst => {}
Ordering::Release => panic!("there is no such thing as a release load"),
Ordering::AcqRel => panic!("there is no such thing as an acquire-release load"),
_ => unreachable!("{:?}", order),
}
}
// https://github.com/rust-lang/rust/blob/1.70.0/library/core/src/sync/atomic.rs#L3140
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn assert_store_ordering(order: Ordering) {
match order {
Ordering::Release | Ordering::Relaxed | Ordering::SeqCst => {}
Ordering::Acquire => panic!("there is no such thing as an acquire store"),
Ordering::AcqRel => panic!("there is no such thing as an acquire-release store"),
_ => unreachable!("{:?}", order),
}
}
// https://github.com/rust-lang/rust/blob/1.70.0/library/core/src/sync/atomic.rs#L3221
#[inline]
#[cfg_attr(all(debug_assertions, not(portable_atomic_no_track_caller)), track_caller)]
pub(crate) fn assert_compare_exchange_ordering(success: Ordering, failure: Ordering) {
match success {
Ordering::AcqRel
| Ordering::Acquire
| Ordering::Relaxed
| Ordering::Release
| Ordering::SeqCst => {}
_ => unreachable!("{:?}, {:?}", success, failure),
}
match failure {
Ordering::Acquire | Ordering::Relaxed | Ordering::SeqCst => {}
Ordering::Release => panic!("there is no such thing as a release failure ordering"),
Ordering::AcqRel => panic!("there is no such thing as an acquire-release failure ordering"),
_ => unreachable!("{:?}, {:?}", success, failure),
}
}
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0418r2.html
// https://github.com/rust-lang/rust/pull/98383
#[allow(dead_code)]
#[inline]
pub(crate) fn upgrade_success_ordering(success: Ordering, failure: Ordering) -> Ordering {
match (success, failure) {
(Ordering::Relaxed, Ordering::Acquire) => Ordering::Acquire,
(Ordering::Release, Ordering::Acquire) => Ordering::AcqRel,
(_, Ordering::SeqCst) => Ordering::SeqCst,
_ => success,
}
}
/// Zero-extends the given 32-bit pointer to `MaybeUninit<u64>`.
/// This is used for 64-bit architecture's 32-bit ABI (e.g., AArch64 ILP32 ABI).
/// See ptr_reg! macro in src/gen/utils.rs for details.
#[cfg(not(portable_atomic_no_asm_maybe_uninit))]
#[cfg(target_pointer_width = "32")]
#[allow(dead_code)]
#[inline]
pub(crate) fn zero_extend64_ptr(v: *mut ()) -> core::mem::MaybeUninit<u64> {
#[repr(C)]
struct ZeroExtended {
#[cfg(target_endian = "big")]
pad: *mut (),
v: *mut (),
#[cfg(target_endian = "little")]
pad: *mut (),
}
// SAFETY: we can safely transmute any 64-bit value to MaybeUninit<u64>.
unsafe { core::mem::transmute(ZeroExtended { v, pad: core::ptr::null_mut() }) }
}
#[allow(dead_code)]
#[cfg(any(
target_arch = "aarch64",
target_arch = "powerpc64",
target_arch = "s390x",
target_arch = "x86_64",
))]
/// A 128-bit value represented as a pair of 64-bit values.
///
/// This type is `#[repr(C)]`, both fields have the same in-memory representation
/// and are plain old data types, so access to the fields is always safe.
#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) union U128 {
pub(crate) whole: u128,
pub(crate) pair: Pair<u64>,
}
#[allow(dead_code)]
#[cfg(target_arch = "arm")]
/// A 64-bit value represented as a pair of 32-bit values.
///
/// This type is `#[repr(C)]`, both fields have the same in-memory representation
/// and are plain old data types, so access to the fields is always safe.
#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) union U64 {
pub(crate) whole: u64,
pub(crate) pair: Pair<u32>,
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) struct Pair<T: Copy> {
// little endian order
#[cfg(any(target_endian = "little", target_arch = "aarch64", target_arch = "arm"))]
pub(crate) lo: T,
pub(crate) hi: T,
// big endian order
#[cfg(not(any(target_endian = "little", target_arch = "aarch64", target_arch = "arm")))]
pub(crate) lo: T,
}
#[allow(dead_code)]
type MinWord = u32;
#[cfg(target_arch = "riscv32")]
type RegSize = u32;
#[cfg(target_arch = "riscv64")]
type RegSize = u64;
// Adapted from https://github.com/taiki-e/atomic-maybe-uninit/blob/v0.3.0/src/utils.rs#L210.
// Helper for implementing sub-word atomic operations using word-sized LL/SC loop or CAS loop.
//
// Refs: https://github.com/llvm/llvm-project/blob/llvmorg-17.0.0-rc2/llvm/lib/CodeGen/AtomicExpandPass.cpp#L699
// (aligned_ptr, shift, mask)
#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
#[allow(dead_code)]
#[inline]
pub(crate) fn create_sub_word_mask_values<T>(ptr: *mut T) -> (*mut MinWord, RegSize, RegSize) {
use core::mem;
const SHIFT_MASK: bool = !cfg!(any(
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "loongarch64",
target_arch = "s390x",
));
let ptr_mask = mem::size_of::<MinWord>() - 1;
let aligned_ptr = strict::with_addr(ptr, ptr as usize & !ptr_mask) as *mut MinWord;
let ptr_lsb = if SHIFT_MASK {
ptr as usize & ptr_mask
} else {
// We use 32-bit wrapping shift instructions in asm on these platforms.
ptr as usize
};
let shift = if cfg!(any(target_endian = "little", target_arch = "s390x")) {
ptr_lsb.wrapping_mul(8)
} else {
(ptr_lsb ^ (mem::size_of::<MinWord>() - mem::size_of::<T>())).wrapping_mul(8)
};
let mut mask: RegSize = (1 << (mem::size_of::<T>() * 8)) - 1; // !(0 as T) as RegSize
if SHIFT_MASK {
mask <<= shift;
}
(aligned_ptr, shift as RegSize, mask)
}
/// Emulate strict provenance.
///
/// Once strict_provenance is stable, migrate to the standard library's APIs.
#[cfg(any(miri, target_arch = "riscv32", target_arch = "riscv64"))]
#[allow(dead_code)]
#[allow(clippy::cast_possible_wrap)]
pub(crate) mod strict {
/// Replace the address portion of this pointer with a new address.
#[inline]
#[must_use]
pub(crate) fn with_addr<T>(ptr: *mut T, addr: usize) -> *mut T {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
//
// In the mean-time, this operation is defined to be "as if" it was
// a wrapping_offset, so we can emulate it as such. This should properly
// restore pointer provenance even under today's compiler.
let self_addr = ptr as usize as isize;
let dest_addr = addr as isize;
let offset = dest_addr.wrapping_sub(self_addr);
// This is the canonical desugaring of this operation.
(ptr as *mut u8).wrapping_offset(offset) as *mut T
}
/// Run an operation of some kind on a pointer.
#[inline]
#[must_use]
pub(crate) fn map_addr<T>(ptr: *mut T, f: impl FnOnce(usize) -> usize) -> *mut T {
with_addr(ptr, f(ptr as usize))
}
}