//! `__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. //! //! #![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::()`. unsafe fn rmw(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::()`. unsafe fn cas(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);