From b94414c69a06fc114892f138fd59a5f4f9ace91c Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Fri, 9 Aug 2024 14:23:49 -0700 Subject: [PATCH] Detect atomic support using target_has_atomic This is adapted from @josephlr's similar implementation in #1091. Fixes #1086 Co-authored-by: Joe Richey --- .github/workflows/ci.yml | 17 ++++++- Cargo.toml | 4 ++ src/impls.rs | 103 ++++++++++++++++++++++++--------------- src/lib.rs | 9 ++-- 4 files changed, 88 insertions(+), 45 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8009b80db4..f37cddfafc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,19 @@ jobs: matrix: # See `INTERNAL.md` for an explanation of these pinned toolchain # versions. - toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ] + toolchain: [ + "msrv", + "stable", + "nightly", + + # These are the names of specific Rust versions detected in + # `build.rs`. Each of these represents the minimum Rust version for + # which a particular feature is supported. + "zerocopy-generic-bounds-in-const-fn", + "zerocopy-target-has-atomics", + "zerocopy-aarch64-simd", + "zerocopy-panic-in-const" + ] target: [ "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", @@ -57,6 +69,7 @@ jobs: "riscv64gc-unknown-linux-gnu", "s390x-unknown-linux-gnu", "x86_64-pc-windows-msvc", + "thumbv6m-none-eabi", "wasm32-wasi" ] features: [ "--no-default-features", "", "--features __internal_use_only_features_that_work_on_stable", "--all-features" ] @@ -109,6 +122,8 @@ jobs: event_name: "pull_request" - target: "s390x-unknown-linux-gnu" event_name: "pull_request" + - target: "thumbv6m-none-eabi" + event_name: "pull_request" - target: "wasm32-wasi" event_name: "pull_request" diff --git a/Cargo.toml b/Cargo.toml index 0f6c354c4e..0d1dd6770f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,10 @@ exclude = [".*"] # From 1.61.0, Rust supports generic types with trait bounds in `const fn`. zerocopy-generic-bounds-in-const-fn = "1.61.0" +# From 1.60.0, Rust supports `cfg(target_has_atomics)`, which allows us to +# detect whether a target supports particular sets of atomics. +zerocopy-target-has-atomics = "1.60.0" + # When the "simd" feature is enabled, include SIMD types from the # `core::arch::aarch64` module, which was stabilized in 1.59.0. On earlier Rust # versions, these types require the "simd-nightly" feature. diff --git a/src/impls.rs b/src/impls.rs index 1933710723..23576d26b1 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -440,49 +440,72 @@ safety_comment! { unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => Immutable for opt_extern_c_fn!(...)); } -macro_rules! impl_traits_for_atomics { - ($($atomics:ident),* $(,)?) => { - $( - impl_for_transparent_wrapper!(=> TryFromBytes for $atomics); - impl_for_transparent_wrapper!(=> FromZeros for $atomics); - impl_for_transparent_wrapper!(=> FromBytes for $atomics); - impl_for_transparent_wrapper!(=> IntoBytes for $atomics); - )* - }; -} +#[cfg(zerocopy_target_has_atomics)] +mod atomics { + use super::*; -#[rustfmt::skip] -impl_traits_for_atomics!( - AtomicI16, AtomicI32, AtomicI8, AtomicIsize, - AtomicU16, AtomicU32, AtomicU8, AtomicUsize, -); + macro_rules! impl_traits_for_atomics { + ($($atomics:ident),* $(,)?) => { + $( + impl_for_transparent_wrapper!(=> TryFromBytes for $atomics); + impl_for_transparent_wrapper!(=> FromZeros for $atomics); + impl_for_transparent_wrapper!(=> FromBytes for $atomics); + impl_for_transparent_wrapper!(=> IntoBytes for $atomics); + )* + }; + } -impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool); -impl_for_transparent_wrapper!(=> FromZeros for AtomicBool); -impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool); + #[cfg(target_has_atomic = "8")] + #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "8")))] + mod atomic_8 { + use super::*; + + impl_traits_for_atomics!(AtomicU8, AtomicI8); + + impl_for_transparent_wrapper!(=> TryFromBytes for AtomicBool); + impl_for_transparent_wrapper!(=> FromZeros for AtomicBool); + impl_for_transparent_wrapper!(=> IntoBytes for AtomicBool); + + safety_comment! { + /// SAFETY: + /// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same + /// size as `bool`, `u8`, and `i8` respectively. Since a type's + /// alignment cannot be smaller than 1 [2], and since its alignment + /// cannot be greater than its size [3], the only possible value for + /// the alignment is 1. Thus, it is sound to implement `Unaligned`. + /// + /// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): + /// Cite docs once they've landed. + /// + /// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: + /// + /// Alignment is measured in bytes, and must be at least 1. + /// + /// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: + /// + /// The size of a value is always a multiple of its alignment. + unsafe_impl!(AtomicBool: Unaligned); + unsafe_impl!(AtomicU8: Unaligned); + unsafe_impl!(AtomicI8: Unaligned); + assert_unaligned!(AtomicBool, AtomicU8, AtomicI8); + } + } -safety_comment! { - /// SAFETY: - /// Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the same size as - /// `bool`, `u8`, and `i8` respectively. Since a type's alignment cannot be - /// smaller than 1 [2], and since its alignment cannot be greater than its - /// size [3], the only possible value for the alignment is 1. Thus, it is - /// sound to implement `Unaligned`. - /// - /// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943): - /// Cite docs once they've landed. - /// - /// [2] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: - /// - /// Alignment is measured in bytes, and must be at least 1. - /// - /// [3] Per https://doc.rust-lang.org/reference/type-layout.html#size-and-alignment: - /// - /// The size of a value is always a multiple of its alignment. - unsafe_impl!(AtomicBool: Unaligned); - unsafe_impl!(AtomicU8: Unaligned); - unsafe_impl!(AtomicI8: Unaligned); - assert_unaligned!(AtomicBool, AtomicU8, AtomicI8); + #[cfg(target_has_atomic = "16")] + #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "16")))] + impl_traits_for_atomics!(AtomicU16, AtomicI16); + + #[cfg(target_has_atomic = "32")] + #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "32")))] + impl_traits_for_atomics!(AtomicU32, AtomicI32); + + #[cfg(target_has_atomic = "64")] + #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "64")))] + impl_traits_for_atomics!(AtomicU64, AtomicI64); + + #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(doc_cfg, doc(cfg(target_has_atomic = "ptr")))] + impl_traits_for_atomics!(AtomicUsize, AtomicIsize); } safety_comment! { diff --git a/src/lib.rs b/src/lib.rs index 4dfe7705ee..a5fad3ecdc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -338,8 +338,8 @@ use core::{ ptr::{self, NonNull}, slice, sync::atomic::{ - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, AtomicU32, - AtomicU8, AtomicUsize, + AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16, + AtomicU32, AtomicU64, AtomicU8, AtomicUsize, }, }; @@ -820,8 +820,9 @@ impl_known_layout!( bool, char, NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize, - AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicIsize, AtomicU16, AtomicU32, - AtomicU8, AtomicUsize + AtomicBool, + AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, + AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize ); #[rustfmt::skip] impl_known_layout!(