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
}