diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index 97c3547e..748b3efc 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -1,3 +1,4 @@ +abiv amocas amoswap amswap @@ -18,10 +19,12 @@ ccmp cdsg CDSY clrex +cmpne cmpw cmpxchg csel cset +cskyv dbar distro DWCAS @@ -44,6 +47,7 @@ ldar ldarx ldaxp ldclrp +ldex ldiapp ldrex ldrexd @@ -83,6 +87,7 @@ opensbi orrs partword pstq +pthread qbsp quadword rcpc @@ -106,6 +111,7 @@ sreg srlv stbar stdcx +stex stilp stlxp stpq diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 05221d91..d3f135aa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -290,6 +290,29 @@ jobs: target: armeb-unknown-linux-gnueabi os: ubuntu-22.04 # ------------------------------------------------------------ + # csky-unknown-linux-gnuabiv2 + - rust: nightly-2025-02-14 # Rust 1.86, LLVM 19 (oldest version we can use asm_experimental_arch on this target) + target: csky-unknown-linux-gnuabiv2 + # +crt-static: Workaround for C-SKY QEMU issue + # ldex/stex requires ck860* + flags: -C target-feature=+crt-static -C target-cpu=ck860 + - rust: nightly + target: csky-unknown-linux-gnuabiv2 + # +crt-static: Workaround for C-SKY QEMU issue + # ldex/stex requires ck860* + flags: -C target-feature=+crt-static -C target-cpu=ck860 + # ------------------------------------------------------------ + # csky-unknown-linux-gnuabiv2hf + # TODO: relocations in generic ELF (EM: 252) + # - rust: nightly-2025-02-14 # Rust 1.86, LLVM 19 (oldest version we can use asm_experimental_arch on this target) + # target: csky-unknown-linux-gnuabiv2hf + # # +crt-static: Workaround for C-SKY QEMU issue + # flags: -C target-feature=+crt-static + # - rust: nightly + # target: csky-unknown-linux-gnuabiv2hf + # # +crt-static: Workaround for C-SKY QEMU issue + # flags: -C target-feature=+crt-static + # ------------------------------------------------------------ # hexagon-unknown-linux-musl - rust: nightly-2024-11-29 # Rust 1.85, LLVM 19 (oldest version we can use asm_experimental_arch on this target) target: hexagon-unknown-linux-musl @@ -510,7 +533,34 @@ jobs: - uses: taiki-e/setup-cross-toolchain-action@v1 with: target: ${{ matrix.target }} - if: steps.prepare.outputs.target-not-host == 'true' + if: steps.prepare.outputs.target-not-host == 'true' && !startsWith(matrix.target, 'csky-') + # TODO: not yet supported in setup-cross-toolchain-action + - run: | + retry() { + for i in {1..10}; do + if "$@"; then + return 0 + else + sleep "${i}" + fi + done + "$@" + } + target="${{ matrix.target }}" + # https://github.com/taiki-e/rust-cross-toolchain/pkgs/container/rust-cross-toolchain + retry docker create --name gcc-csky-linux-gnuabiv2 "ghcr.io/taiki-e/rust-cross-toolchain:${target}-dev-amd64" + docker cp -- "gcc-csky-linux-gnuabiv2:/${target}" "${HOME}"/gcc-csky-linux-gnuabiv2 + docker rm -f -- gcc-csky-linux-gnuabiv2 >/dev/null + printf '%s\n' "${HOME}"/gcc-csky-linux-gnuabiv2/bin >>"${GITHUB_PATH}" + printf '%s\n' "CARGO_TARGET_CSKY_UNKNOWN_LINUX_GNUABIV2_LINKER=csky-abiv2-linux-gcc" >>"${GITHUB_ENV}" + case "${target}" in + *hf) qemu_cpu=ck860fv ;; + # https://github.com/taiki-e/atomic-maybe-uninit/pull/32#issuecomment-3341287749 + *) qemu_cpu=ck860 ;; + esac + printf '%s\n' "CARGO_TARGET_CSKY_UNKNOWN_LINUX_GNUABIV2_RUNNER=qemu-cskyv2 -cpu ${qemu_cpu}" >>"${GITHUB_ENV}" + printf 'BUILD_STD=-Z''build-std\n' >>"${GITHUB_ENV}" + if: startsWith(matrix.target, 'csky-') - run: | target="${{ matrix.target }}" target_lower="${target//-/_}" @@ -525,7 +575,8 @@ jobs: if: matrix.flags != '' - run: printf '%s\n' "RUSTFLAGS=${RUSTFLAGS} -D linker_messages" >>"${GITHUB_ENV}" # TODO(macos): error: linker stderr: ld: ignoring duplicate libraries: '-lc', '-lm' - if: matrix.rust == 'nightly' && !contains(matrix.target, '-darwin') + # TODO(csky): error: linker stderr: /home/runner/gcc-csky-linux-gnuabiv2/bin/../lib/gcc/csky-linux-gnuabiv2/6.3.0/../../../../csky-linux-gnuabiv2/bin/ld: file ....o's arch flag ck860 conflict with target ck810,set target arch flag to ck860 + if: matrix.rust == 'nightly' && !(contains(matrix.target, '-darwin') || matrix.target == 'csky-unknown-linux-gnuabiv2') # Old LLVM bug: Undefined temporary symbol error when building std. - name: Workaround for old LLVM bug about MIPS32 run: printf 'RELEASE=--release\n' >>"${GITHUB_ENV}" diff --git a/Cargo.toml b/Cargo.toml index f8f81247..b745719c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ doc-scrape-examples = false [dev-dependencies] fastrand = "2" paste = "1" -quickcheck = { version = "1", default-features = false, git = "https://github.com/taiki-e/quickcheck.git", rev = "83b1d59" } # https://github.com/BurntSushi/quickcheck/pull/304 + https://github.com/BurntSushi/quickcheck/pull/282 + https://github.com/BurntSushi/quickcheck/pull/296 + f16/f128 support + lower MSRV +quickcheck = { version = "1", default-features = false, git = "https://github.com/taiki-e/quickcheck.git", rev = "f0c7237" } # https://github.com/BurntSushi/quickcheck/pull/304 + https://github.com/BurntSushi/quickcheck/pull/282 + https://github.com/BurntSushi/quickcheck/pull/296 + rand -> fastrand + lower MSRV [target.'cfg(valgrind)'.dev-dependencies] crabgrind = "0.1" diff --git a/README.md b/README.md index b15fe869..8c0c9c4e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This crate provides a way to soundly perform such operations. ## Platform Support -Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, PowerPC, MSP430, AVR, SPARC, Hexagon, M68k, and Xtensa are supported. +Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, PowerPC, MSP430, AVR, SPARC, Hexagon, M68k, C-SKY, and Xtensa are supported. (You can use `cfg_{has,no}_*` macros to write code based on whether or not which size of primitives is available.) | target_arch | primitives | load/store | swap/CAS | @@ -50,9 +50,10 @@ Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, P | hexagon \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | | m68k \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | | m68k (+isa-68020) \[9] \[10] (experimental) | i64,u64 | ✓ | ✓ | +| csky \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | | xtensa \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | -\[1] Arm's atomic RMW operations are not available on Armv6-M (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). Xtensa's atomic RMW operations are not available on esp32s2.
+\[1] Arm's atomic RMW operations are not available on Armv6-M (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). C-SKY's atomic RMW operations requires target-cpu ck860\* (enabled by default on the hard-float target). Xtensa's atomic RMW operations are not available on esp32s2.
\[2] Requires `cmpxchg16b` target feature (enabled by default on Apple, Windows (except Windows 7), and Fuchsia targets).
\[3] Armv6+ or Linux/Android, except for M-profile architecture such as thumbv6m, thumbv7m, etc.
\[4] Requires `zacas` target feature.
diff --git a/build.rs b/build.rs index 36827b1a..821e4315 100644 --- a/build.rs +++ b/build.rs @@ -39,7 +39,7 @@ fn main() { // Custom cfgs set by build script. Not public API. // grep -F 'cargo:rustc-cfg=' build.rs | grep -Ev '^ *//' | sed -E 's/^.*cargo:rustc-cfg=//; s/(=\\)?".*$//' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' println!( - "cargo:rustc-check-cfg=cfg(atomic_maybe_uninit_no_asm,atomic_maybe_uninit_no_cmpxchg,atomic_maybe_uninit_no_cmpxchg8b,atomic_maybe_uninit_no_const_mut_refs,atomic_maybe_uninit_no_diagnostic_namespace,atomic_maybe_uninit_no_strict_provenance,atomic_maybe_uninit_no_sync,atomic_maybe_uninit_pre_llvm_20,atomic_maybe_uninit_target_feature,atomic_maybe_uninit_unstable_asm_experimental_arch)" + "cargo:rustc-check-cfg=cfg(atomic_maybe_uninit_no_asm,atomic_maybe_uninit_no_cmpxchg,atomic_maybe_uninit_no_cmpxchg8b,atomic_maybe_uninit_no_const_mut_refs,atomic_maybe_uninit_no_diagnostic_namespace,atomic_maybe_uninit_no_ldex_stex,atomic_maybe_uninit_no_strict_provenance,atomic_maybe_uninit_no_sync,atomic_maybe_uninit_pre_llvm_20,atomic_maybe_uninit_target_feature,atomic_maybe_uninit_unstable_asm_experimental_arch)" ); // TODO: handle multi-line target_feature_fallback // grep -F 'target_feature_fallback("' build.rs | grep -Ev '^ *//' | sed -E 's/^.*target_feature_fallback\(//; s/",.*$/"/' | LC_ALL=C sort -u | tr '\n' ',' | sed -E 's/,$/\n/' @@ -118,6 +118,15 @@ fn main() { println!("cargo:rustc-cfg=atomic_maybe_uninit_unstable_asm_experimental_arch"); } } + "csky" => { + // https://github.com/rust-lang/rust/pull/136217 merged in Rust 1.86 (nightly-2025-02-14). + if version.nightly + && version.probe(86, 2025, 2, 13) + && is_allowed_feature("asm_experimental_arch") + { + println!("cargo:rustc-cfg=atomic_maybe_uninit_unstable_asm_experimental_arch"); + } + } "hexagon" => { // https://github.com/rust-lang/rust/pull/133452 merged in Rust 1.85 (nightly-2024-11-29). if version.nightly @@ -501,6 +510,23 @@ fn main() { } target_feature_fallback("rmw", xmegau); } + "csky" => { + let mut no_ldex_stex = true; + if let Some(cpu) = target_cpu() { + // https://github.com/llvm/llvm-project/blob/llvmorg-21.1.0/llvm/lib/Target/CSKY/CSKY.td#L373 + if cpu.starts_with("ck860") { + no_ldex_stex = false; + } + } else { + // https://github.com/rust-lang/rust/blob/1.90.0/compiler/rustc_target/src/spec/targets/csky_unknown_linux_gnuabiv2hf.rs#L20 + if target == "csky-unknown-linux-gnuabiv2hf" { + no_ldex_stex = false; + } + } + if no_ldex_stex { + println!("cargo:rustc-cfg=atomic_maybe_uninit_no_ldex_stex"); + } + } _ => {} } } diff --git a/src/arch/README.md b/src/arch/README.md index 5e258b65..595d5b64 100644 --- a/src/arch/README.md +++ b/src/arch/README.md @@ -8,6 +8,7 @@ This document describes the operations that are considered atomic by architectur - [AArch64](#aarch64) - [Arm](#arm) - [AVR](#avr) +- [C-SKY](#c-sky) - [Hexagon](#hexagon) - [LoongArch](#loongarch) - [M68k](#m68k) @@ -61,6 +62,13 @@ This architecture is always single-core and the following operations are atomic: disabling and restoring implementation must imply compiler fences, e.g., asm without nomem/readonly) may be moved out of the critical section by compiler optimizations. +## C-SKY + +target_arch: csky
+Implementation: [csky.rs](csky.rs)
+ +TODO: reference and overview + ## Hexagon target_arch: hexagon
diff --git a/src/arch/csky.rs b/src/arch/csky.rs new file mode 100644 index 00000000..0cd56968 --- /dev/null +++ b/src/arch/csky.rs @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT + +/* +C-SKY + +Refs: +- CSKY Architecture user_guide + https://github.com/c-sky/csky-doc/blob/9f7121f7d40970ba5cc0f15716da033db2bb9d07/CSKY%20Architecture%20user_guide.pdf +- Linux kernel's C-SKY atomic implementation + https://github.com/torvalds/linux/blob/v6.16/arch/csky/include/asm/atomic.h + https://github.com/torvalds/linux/blob/v6.16/arch/csky/include/asm/barrier.h + https://github.com/torvalds/linux/blob/v6.16/arch/csky/include/asm/cmpxchg.h + +Generated asm: +- csky https://godbolt.org/z/jK4c68WeG +*/ + +#[cfg(atomic_maybe_uninit_no_ldex_stex)] +delegate_size!(delegate_load_store); +#[cfg(not(atomic_maybe_uninit_no_ldex_stex))] +delegate_size!(delegate_all); + +use core::{ + arch::asm, + mem::{self, MaybeUninit}, + sync::atomic::Ordering, +}; + +#[cfg(not(atomic_maybe_uninit_no_ldex_stex))] +use crate::raw::{AtomicCompareExchange, AtomicSwap}; +use crate::raw::{AtomicLoad, AtomicStore}; + +macro_rules! atomic_rmw { + ($op:ident, $order:ident) => { + match $order { + Ordering::Relaxed => $op!("", ""), + Ordering::Acquire => $op!("sync32", ""), + Ordering::Release => $op!("", "sync32"), + Ordering::AcqRel | Ordering::SeqCst => $op!("sync32", "sync32"), + _ => unreachable!(), + } + }; +} + +#[rustfmt::skip] +macro_rules! atomic_load_store { + ($ty:ident, $suffix:tt) => { + #[cfg(atomic_maybe_uninit_no_ldex_stex)] + delegate_signed!(delegate_load_store, $ty); + #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] + delegate_signed!(delegate_all, $ty); + impl AtomicLoad for $ty { + #[inline] + unsafe fn atomic_load( + src: *const MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + macro_rules! atomic_load { + ($acquire:tt, $release:tt) => { + asm!( + $release, // fence + concat!("ld32.", $suffix, " {out}, ({src}, 0x0)"), // atomic { out = *src } + $acquire, // fence + src = in(reg) ptr_reg!(src), + out = lateout(reg) out, + options(nostack, preserves_flags), + ) + }; + } + match order { + Ordering::Relaxed => atomic_load!("", ""), + Ordering::Acquire => atomic_load!("sync32", ""), + Ordering::SeqCst => atomic_load!("sync32", "sync32"), + _ => unreachable!(), + } + } + out + } + } + impl AtomicStore for $ty { + #[inline] + unsafe fn atomic_store( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) { + // SAFETY: the caller must uphold the safety contract. + unsafe { + macro_rules! store { + ($acquire:tt, $release:tt) => { + asm!( + $release, // fence + concat!("st32.", $suffix, " {val}, ({dst}, 0x0)"), // atomic { *dst = val } + $acquire, // fence + dst = in(reg) ptr_reg!(dst), + val = in(reg) val, + options(nostack, preserves_flags), + ) + }; + } + atomic_rmw!(store, order); + } + } + } + }; +} + +#[rustfmt::skip] +macro_rules! atomic { + ($ty:ident) => { + atomic_load_store!($ty, "w"); + #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] + impl AtomicSwap for $ty { + #[inline] + unsafe fn atomic_swap( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + macro_rules! swap { + ($acquire:tt, $release:tt) => { + asm!( + $release, // fence + "2:", // 'retry: + "ldex32.w {out}, ({dst}, 0)", // atomic { out = *dst; EXCLUSIVE = dst } + "or32 {tmp}, {val}, {val}", // tmp = val + "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } + "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } + $acquire, // fence + dst = in(reg) ptr_reg!(dst), + val = in(reg) val, + out = out(reg) out, + tmp = out(reg) _, + options(nostack, preserves_flags), + ) + }; + } + atomic_rmw!(swap, order); + } + out + } + } + #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] + impl AtomicCompareExchange for $ty { + #[inline] + unsafe fn atomic_compare_exchange( + dst: *mut MaybeUninit, + old: MaybeUninit, + new: MaybeUninit, + success: Ordering, + failure: Ordering, + ) -> (MaybeUninit, bool) { + let order = crate::utils::upgrade_success_ordering(success, failure); + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + let mut r: u32 = 0; + macro_rules! cmpxchg { + ($acquire:tt, $release:tt) => { + asm!( + $release, // fence + "2:", // 'retry: + "ldex32.w {out}, ({dst}, 0)", // atomic { out = *dst; EXCLUSIVE = dst } + "cmpne32 {out}, {old}", // if out != old { C = 1 } else { C = 0 } + "bt32 3f", // if C == 1 { jump 'cmp-fail } + "or32 {tmp}, {new}, {new}", // tmp = new + "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } + "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } + "3:", // 'cmp-fail: + $acquire, // fence + dst = in(reg) ptr_reg!(dst), + old = in(reg) old, + new = in(reg) new, + out = out(reg) out, + tmp = inout(reg) r, + // Do not use `preserves_flags` because CMPNE modifies condition bit C. + options(nostack), + ) + }; + } + atomic_rmw!(cmpxchg, order); + crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test + (out, r != 0) + } + } + } + }; +} + +#[rustfmt::skip] +macro_rules! atomic_sub_word { + ($ty:ident, $suffix:tt) => { + atomic_load_store!($ty, $suffix); + #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] + impl AtomicSwap for $ty { + #[inline] + unsafe fn atomic_swap( + dst: *mut MaybeUninit, + val: MaybeUninit, + order: Ordering, + ) -> MaybeUninit { + let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + macro_rules! swap { + ($acquire:tt, $release:tt) => { + // Implement sub-word atomic operations using word-sized LL/SC loop. + // See also create_sub_word_mask_values. + asm!( + "lsl32 {val}, {val}, {shift}", // val <<= shift + $release, // fence + "2:", // 'retry: + "ldex32.w {out}, ({dst}, 0)", // atomic { out = *dst; EXCLUSIVE = dst } + "andn32 {tmp}, {out}, {mask}", // tmp = out & !mask + "or32 {tmp}, {tmp}, {val}", // tmp |= val + "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } + "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } + $acquire, // fence + "lsr32 {out}, {out}, {shift}", // out >>= shift + dst = in(reg) ptr_reg!(dst), + val = inout(reg) crate::utils::zero_extend32::$ty(val) => _, + out = out(reg) out, + shift = in(reg) shift, + mask = in(reg) mask, + tmp = out(reg) _, + options(nostack, preserves_flags), + ) + }; + } + atomic_rmw!(swap, order); + } + out + } + } + #[cfg(not(atomic_maybe_uninit_no_ldex_stex))] + impl AtomicCompareExchange for $ty { + #[inline] + unsafe fn atomic_compare_exchange( + dst: *mut MaybeUninit, + old: MaybeUninit, + new: MaybeUninit, + success: Ordering, + failure: Ordering, + ) -> (MaybeUninit, bool) { + let order = crate::utils::upgrade_success_ordering(success, failure); + let (dst, shift, mask) = crate::utils::create_sub_word_mask_values(dst); + let mut out: MaybeUninit; + + // SAFETY: the caller must uphold the safety contract. + unsafe { + let mut r: u32; + macro_rules! cmpxchg { + ($acquire:tt, $release:tt) => { + // Implement sub-word atomic operations using word-sized LL/SC loop. + // See also create_sub_word_mask_values. + asm!( + "lsl32 {old}, {old}, {shift}", // old <<= shift + "lsl32 {new}, {new}, {shift}", // new <<= shift + $release, // fence + "2:", // 'retry: + "ldex32.w {tmp}, ({dst}, 0)", // atomic { tmp = *dst; EXCLUSIVE = dst } + "and32 {out}, {tmp}, {mask}", // out = tmp & mask + "cmpne32 {out}, {old}", // if out != old { C = 1 } else { C = 0 } + "bt32 3f", // if C == 1 { jump 'cmp-fail } + "andn32 {tmp}, {tmp}, {mask}", // tmp &= !mask + "or32 {tmp}, {tmp}, {new}", // tmp |= new + "stex32.w {tmp}, ({dst}, 0)", // atomic { if EXCLUSIVE == dst { *dst = tmp; tmp = 1 } else { tmp = 0 }; EXCLUSIVE = None } + "bez32 {tmp}, 2b", // if tmp == 0 { jump 'retry } + "br32 4f", // jump 'success + "3:", // 'cmp-fail: + "movi32 {tmp}, 0", // tmp = 0 + "4:", // 'success: + $acquire, // fence + "lsr32 {out}, {out}, {shift}", // out >>= shift + dst = in(reg) ptr_reg!(dst), + old = inout(reg) crate::utils::zero_extend32::$ty(old) => _, + new = inout(reg) crate::utils::zero_extend32::$ty(new) => _, + out = out(reg) out, + shift = in(reg) shift, + mask = in(reg) mask, + tmp = out(reg) r, + // Do not use `preserves_flags` because CMPNE modifies condition bit C. + options(nostack), + ) + }; + } + atomic_rmw!(cmpxchg, order); + crate::utils::assert_unchecked(r == 0 || r == 1); // may help remove extra test + (out, r != 0) + } + } + } + }; +} + +atomic_sub_word!(u8, "b"); +atomic_sub_word!(u16, "h"); +atomic!(u32); + +// ----------------------------------------------------------------------------- +// cfg macros + +#[macro_export] +macro_rules! cfg_has_atomic_8 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_8 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_16 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_16 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_32 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_no_atomic_32 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_has_atomic_64 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_64 { + ($($tt:tt)*) => { $($tt)* }; +} +#[macro_export] +macro_rules! cfg_has_atomic_128 { + ($($tt:tt)*) => {}; +} +#[macro_export] +macro_rules! cfg_no_atomic_128 { + ($($tt:tt)*) => { $($tt)* }; +} +#[cfg(not(atomic_maybe_uninit_no_ldex_stex))] +#[macro_export] +macro_rules! cfg_has_atomic_cas { + ($($tt:tt)*) => { $($tt)* }; +} +#[cfg(not(atomic_maybe_uninit_no_ldex_stex))] +#[macro_export] +macro_rules! cfg_no_atomic_cas { + ($($tt:tt)*) => {}; +} +#[cfg(atomic_maybe_uninit_no_ldex_stex)] +#[macro_export] +macro_rules! cfg_has_atomic_cas { + ($($tt:tt)*) => {}; +} +#[cfg(atomic_maybe_uninit_no_ldex_stex)] +#[macro_export] +macro_rules! cfg_no_atomic_cas { + ($($tt:tt)*) => { $($tt)* }; +} diff --git a/src/lib.rs b/src/lib.rs index 4a382ec6..2a11d244 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ This crate provides a way to soundly perform such operations. ## Platform Support -Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, PowerPC, MSP430, AVR, SPARC, Hexagon, M68k, and Xtensa are supported. +Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, PowerPC, MSP430, AVR, SPARC, Hexagon, M68k, C-SKY, and Xtensa are supported. (You can use `cfg_{has,no}_*` macros to write code based on whether or not which size of primitives is available.) | target_arch | primitives | load/store | swap/CAS | @@ -47,9 +47,10 @@ Currently, x86, x86_64, Arm, AArch64, RISC-V, LoongArch, Arm64EC, s390x, MIPS, P | hexagon \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32,i64,u64 | ✓ | ✓ | | m68k \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | | m68k (+isa-68020) \[9] \[10] (experimental) | i64,u64 | ✓ | ✓ | +| csky \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | | xtensa \[9] (experimental) | isize,usize,i8,u8,i16,u16,i32,u32 | ✓ | ✓\[1] | -\[1] Arm's atomic RMW operations are not available on Armv6-M (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). Xtensa's atomic RMW operations are not available on esp32s2.
+\[1] Arm's atomic RMW operations are not available on Armv6-M (thumbv6m). RISC-V's atomic RMW operations are not available on targets without the A (or G which means IMAFD) or Zalrsc or Zacas extension, such as riscv32i, riscv32imc, etc. M68k's atomic RMW operations requires target-cpu M68020+ (enabled by default on Linux). C-SKY's atomic RMW operations requires target-cpu ck860\* (enabled by default on the hard-float target). Xtensa's atomic RMW operations are not available on esp32s2.
\[2] Requires `cmpxchg16b` target feature (enabled by default on Apple, Windows (except Windows 7), and Fuchsia targets).
\[3] Armv6+ or Linux/Android, except for M-profile architecture such as thumbv6m, thumbv7m, etc.
\[4] Requires `zacas` target feature.
@@ -858,6 +859,10 @@ pub use {cfg_has_atomic_128 as cfg_has_atomic_ptr, cfg_no_atomic_128 as cfg_no_a all(target_arch = "avr", atomic_maybe_uninit_unstable_asm_experimental_arch), path = "arch/avr.rs" )] +#[cfg_attr( + all(target_arch = "csky", atomic_maybe_uninit_unstable_asm_experimental_arch), + path = "arch/csky.rs" +)] #[cfg_attr( all(target_arch = "hexagon", atomic_maybe_uninit_unstable_asm_experimental_arch), path = "arch/hexagon.rs" diff --git a/src/tests/helper.rs b/src/tests/helper.rs index 6ef308cb..e49479d5 100644 --- a/src/tests/helper.rs +++ b/src/tests/helper.rs @@ -110,23 +110,6 @@ macro_rules! test_atomic_load_store { } }; } -macro_rules! test_atomic_load_store_swap { - ($ty:ident) => { - paste::paste! { - #[allow( - clippy::alloc_instead_of_core, - clippy::arithmetic_side_effects, - clippy::std_instead_of_alloc, - clippy::std_instead_of_core, - clippy::undocumented_unsafe_blocks, - )] - mod [] { - __test_atomic!(load_store, $ty); - __test_atomic!(swap, $ty); - } - } - }; -} macro_rules! test_atomic { ($ty:ident) => { paste::paste! { @@ -139,7 +122,9 @@ macro_rules! test_atomic { )] mod [] { __test_atomic!(load_store, $ty); + #[cfg(not(all(target_arch = "csky", atomic_maybe_uninit_no_ldex_stex)))] __test_atomic!(swap, $ty); + #[cfg(not(all(target_arch = "csky", atomic_maybe_uninit_no_ldex_stex)))] #[cfg(not(all(target_arch = "x86", atomic_maybe_uninit_no_cmpxchg)))] __test_atomic!(cas, $ty); } @@ -274,6 +259,8 @@ macro_rules! __test_atomic { } } #[test] + // TODO(csky): hang or glibc/pthread assertion fail. likely due to broken libatomic: https://github.com/rust-lang/rust/issues/117306 + #[cfg_attr(target_arch = "csky", ignore = "upstream bug")] fn stress_load_store() { let mut rng = fastrand::Rng::new(); let (iterations, threads) = stress_test_config(&mut rng); @@ -412,6 +399,8 @@ macro_rules! __test_atomic { } } #[test] + // TODO(csky): hang or glibc/pthread assertion fail. likely due to broken libatomic: https://github.com/rust-lang/rust/issues/117306 + #[cfg_attr(target_arch = "csky", ignore = "upstream bug")] fn stress_swap() { // TODO(riscv): wrong result (as of Valgrind 3.25) #[cfg(valgrind)] @@ -788,6 +777,8 @@ macro_rules! __test_atomic { } } #[test] + // TODO(csky): hang or glibc/pthread assertion fail. likely due to broken libatomic: https://github.com/rust-lang/rust/issues/117306 + #[cfg_attr(target_arch = "csky", ignore = "upstream bug")] fn stress_compare_exchange() { // TODO(riscv): wrong result (as of Valgrind 3.25) #[cfg(valgrind)] @@ -871,6 +862,8 @@ macro_rules! __test_atomic { }); } #[test] + // TODO(csky): hang or glibc/pthread assertion fail. likely due to broken libatomic: https://github.com/rust-lang/rust/issues/117306 + #[cfg_attr(target_arch = "csky", ignore = "upstream bug")] fn stress_fetch_update() { // TODO(riscv): wrong result (as of Valgrind 3.25) #[cfg(valgrind)] @@ -1393,7 +1386,6 @@ macro_rules! __stress_test_seqcst { }}; } // Catches unwinding panic on architectures with weak memory models. -#[allow(dead_code)] pub(crate) fn catch_unwind_on_weak_memory_arch(pat: &str, f: impl Fn()) { // With x86 TSO, RISC-V TSO (optional, not default), SPARC TSO (optional, default), // and IBM-370 memory models should never be a panic here. @@ -1424,7 +1416,6 @@ pub(crate) fn catch_unwind_on_weak_memory_arch(pat: &str, f: impl Fn()) { } } // Catches unwinding panic on architectures with non-sequentially consistent memory models. -#[allow(dead_code)] pub(crate) fn catch_unwind_on_non_seqcst_arch(pat: &str, f: impl Fn()) { if !is_panic_abort() { // This could be Err on architectures with non-sequentially consistent memory models. @@ -1446,6 +1437,7 @@ pub(crate) fn catch_unwind_on_non_seqcst_arch(pat: &str, f: impl Fn()) { } macro_rules! stress_test_load_store { ($ty:ident) => { + #[cfg(not(target_arch = "csky"))] // TODO(csky): hang or glibc/pthread assertion fail. likely due to broken libatomic: https://github.com/rust-lang/rust/issues/117306 paste::paste! { #[allow( clippy::alloc_instead_of_core, @@ -1491,6 +1483,7 @@ macro_rules! stress_test_load_store { macro_rules! stress_test { ($ty:ident) => { stress_test_load_store!($ty); + #[cfg(not(target_arch = "csky"))] // TODO(csky): hang or glibc/pthread assertion fail. likely due to broken libatomic: https://github.com/rust-lang/rust/issues/117306 paste::paste! { #[allow( clippy::alloc_instead_of_core, diff --git a/tools/build.sh b/tools/build.sh index 202c1fd7..0711d704 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -69,6 +69,11 @@ default_targets=( # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.arch == "avr" then .key else empty end' avr-none + # csky + # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.arch == "csky" then .key else empty end' + csky-unknown-linux-gnuabiv2 + csky-unknown-linux-gnuabiv2hf + # hexagon # rustc -Z unstable-options --print all-target-specs-json | jq -r '. | to_entries[] | if .value.arch == "hexagon" then .key else empty end' # TODO(hexagon): error: symbol 'fma' is already defined @@ -467,6 +472,11 @@ build() { RUSTFLAGS="${target_rustflags} -C target-feature=+rmw" \ x_cargo "${args[@]}" "$@" ;; + csky-unknown-linux-gnuabiv2) + CARGO_TARGET_DIR="${target_dir}/ck860" \ + RUSTFLAGS="${target_rustflags} -C target-cpu=ck860" \ + x_cargo "${args[@]}" "$@" + ;; esac }