aboutsummaryrefslogtreecommitdiff
path: root/crates/sync-single-core/src/lib.rs
diff options
context:
space:
mode:
authorIan McIntyre <me@mciantyre.dev>2026-08-30 17:35:22 -0400
committerIan McIntyre <me@mciantyre.dev>2026-09-05 13:37:16 -0400
commitbfa4501cd703149f4720a4ef6cdbe74ef6c3e361 (patch)
tree0bf7f586d2d363d7ae5a5599204378f8951a74e8 /crates/sync-single-core/src/lib.rs
parentc4962c8b9207e9474659526e91a1cf387d2a7b8f (diff)
Add RP2040 support
Diffstat (limited to 'crates/sync-single-core/src/lib.rs')
-rw-r--r--crates/sync-single-core/src/lib.rs178
1 files changed, 178 insertions, 0 deletions
diff --git a/crates/sync-single-core/src/lib.rs b/crates/sync-single-core/src/lib.rs
new file mode 100644
index 0000000..08f7887
--- /dev/null
+++ b/crates/sync-single-core/src/lib.rs
@@ -0,0 +1,178 @@
+//! `__sync_*` atomic builtins for Armv6-M.
+//!
+//! Some Rust ThreadX targets, like `thumbv6m-threadx-eabi`, declares `atomic_cas: true`
+//! and emulate them with OS support. Right now, we handle this by disabling interrupts.
+//! This should be OK for single core support.
+//!
+//! <https://llvm.org/docs/Atomics.html>
+
+#![no_std]
+#![feature(rustc_private)]
+extern crate fusible;
+
+use fusible::interrupt_control::with_disabled;
+
+/// Atomically apply `op` to `*ptr`, returning the previous value.
+///
+/// # Safety
+///
+/// `ptr` must be valid for reads and writes and aligned to `size_of::<T>()`.
+unsafe fn rmw<T, O>(ptr: *mut T, op: O) -> T
+where
+ T: Copy,
+ O: FnOnce(T) -> T,
+{
+ // Safety: interrupt control sufficient for single-core access. Caller
+ // (LLVM) assumed to uphold safety contract.
+ with_disabled(|| unsafe {
+ let old = ptr.read();
+ ptr.write(op(old));
+ old
+ })
+}
+
+/// Atomically store `new` into `*ptr` if `*ptr` equals `old`, returning the
+/// previous value either way.
+///
+/// # Safety
+///
+/// `ptr` must be valid for reads and writes and aligned to `size_of::<T>()`.
+unsafe fn cas<T>(ptr: *mut T, old: T, new: T) -> T
+where
+ T: Copy + PartialEq,
+{
+ // Safety: Same as rmw.
+ with_disabled(|| unsafe {
+ let current = ptr.read();
+ if current == old {
+ ptr.write(new);
+ }
+ current
+ })
+}
+
+macro_rules! fetch_and_op {
+ ($name:ident, $ty:ty, $op:expr) => {
+ #[unsafe(no_mangle)]
+ pub unsafe extern "C" fn $name(ptr: *mut $ty, val: $ty) -> $ty {
+ unsafe { rmw(ptr, |cur| $op(cur, val)) }
+ }
+ };
+}
+
+macro_rules! val_compare_and_swap {
+ ($name:ident, $ty:ty) => {
+ #[unsafe(no_mangle)]
+ pub unsafe extern "C" fn $name(ptr: *mut $ty, oldval: $ty, newval: $ty) -> $ty {
+ unsafe { cas(ptr, oldval, newval) }
+ }
+ };
+}
+
+fetch_and_op!(__sync_fetch_and_add_1, u8, |a: u8, b: u8| a.wrapping_add(b));
+fetch_and_op!(__sync_fetch_and_add_2, u16, |a: u16, b: u16| a
+ .wrapping_add(b));
+fetch_and_op!(__sync_fetch_and_add_4, u32, |a: u32, b: u32| a
+ .wrapping_add(b));
+
+fetch_and_op!(__sync_fetch_and_sub_1, u8, |a: u8, b: u8| a.wrapping_sub(b));
+fetch_and_op!(__sync_fetch_and_sub_2, u16, |a: u16, b: u16| a
+ .wrapping_sub(b));
+fetch_and_op!(__sync_fetch_and_sub_4, u32, |a: u32, b: u32| a
+ .wrapping_sub(b));
+
+fetch_and_op!(__sync_fetch_and_and_1, u8, |a: u8, b: u8| a & b);
+fetch_and_op!(__sync_fetch_and_and_2, u16, |a: u16, b: u16| a & b);
+fetch_and_op!(__sync_fetch_and_and_4, u32, |a: u32, b: u32| a & b);
+
+fetch_and_op!(__sync_fetch_and_or_1, u8, |a: u8, b: u8| a | b);
+fetch_and_op!(__sync_fetch_and_or_2, u16, |a: u16, b: u16| a | b);
+fetch_and_op!(__sync_fetch_and_or_4, u32, |a: u32, b: u32| a | b);
+
+fetch_and_op!(__sync_fetch_and_xor_1, u8, |a: u8, b: u8| a ^ b);
+fetch_and_op!(__sync_fetch_and_xor_2, u16, |a: u16, b: u16| a ^ b);
+fetch_and_op!(__sync_fetch_and_xor_4, u32, |a: u32, b: u32| a ^ b);
+
+fetch_and_op!(__sync_fetch_and_nand_1, u8, |a: u8, b: u8| !(a & b));
+fetch_and_op!(__sync_fetch_and_nand_2, u16, |a: u16, b: u16| !(a & b));
+fetch_and_op!(__sync_fetch_and_nand_4, u32, |a: u32, b: u32| !(a & b));
+
+fetch_and_op!(__sync_fetch_and_max_1, i8, |a: i8, b: i8| if a > b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_max_2, i16, |a: i16, b: i16| if a > b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_max_4, i32, |a: i32, b: i32| if a > b {
+ a
+} else {
+ b
+});
+
+fetch_and_op!(__sync_fetch_and_umax_1, u8, |a: u8, b: u8| if a > b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_umax_2, u16, |a: u16, b: u16| if a > b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_umax_4, u32, |a: u32, b: u32| if a > b {
+ a
+} else {
+ b
+});
+
+fetch_and_op!(__sync_fetch_and_min_1, i8, |a: i8, b: i8| if a < b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_min_2, i16, |a: i16, b: i16| if a < b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_min_4, i32, |a: i32, b: i32| if a < b {
+ a
+} else {
+ b
+});
+
+fetch_and_op!(__sync_fetch_and_umin_1, u8, |a: u8, b: u8| if a < b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_umin_2, u16, |a: u16, b: u16| if a < b {
+ a
+} else {
+ b
+});
+fetch_and_op!(__sync_fetch_and_umin_4, u32, |a: u32, b: u32| if a < b {
+ a
+} else {
+ b
+});
+
+//
+// From the GCC docs, linked from the LLVM docs:
+//
+// > This built-in function, as described by Intel, is not a traditional
+// > test-and-set operation, but rather an atomic exchange operation. It
+// > writes value into *ptr, and returns the previous contents of *ptr.
+//
+
+fetch_and_op!(__sync_lock_test_and_set_1, u8, |_: u8, new: u8| new);
+fetch_and_op!(__sync_lock_test_and_set_2, u16, |_: u16, new: u16| new);
+fetch_and_op!(__sync_lock_test_and_set_4, u32, |_: u32, new: u32| new);
+
+val_compare_and_swap!(__sync_val_compare_and_swap_1, u8);
+val_compare_and_swap!(__sync_val_compare_and_swap_2, u16);
+val_compare_and_swap!(__sync_val_compare_and_swap_4, u32);