1313// be possible to omit the dynamic kernel version check if the std feature is enabled on Rust 1.64+.
1414// https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html
1515
16+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
1617#[ path = "fallback/outline_atomics.rs" ]
1718mod fallback;
1819
1920#[ cfg( not( portable_atomic_no_asm) ) ]
2021use core:: arch:: asm;
21- use core:: { cell:: UnsafeCell , mem, sync:: atomic:: Ordering } ;
22+ use core:: sync:: atomic:: Ordering ;
23+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
24+ use core:: { cell:: UnsafeCell , mem} ;
2225
23- use crate :: utils:: { Pair , U64 } ;
26+ use super :: core_atomic:: {
27+ AtomicI16 , AtomicI32 , AtomicI8 , AtomicIsize , AtomicPtr , AtomicU16 , AtomicU32 , AtomicU8 ,
28+ AtomicUsize ,
29+ } ;
2430
2531// https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt
2632const KUSER_HELPER_VERSION : usize = 0xFFFF0FFC ;
33+ // __kuser_helper_version >= 3 (kernel version 2.6.15+)
34+ const KUSER_MEMORY_BARRIER : usize = 0xFFFF0FA0 ;
2735// __kuser_helper_version >= 5 (kernel version 3.1+)
36+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
2837const KUSER_CMPXCHG64 : usize = 0xFFFF0F60 ;
38+
2939#[ inline]
3040fn __kuser_helper_version ( ) -> i32 {
3141 use core:: sync:: atomic:: AtomicI32 ;
@@ -41,6 +51,7 @@ fn __kuser_helper_version() -> i32 {
4151 CACHE . store ( v, Ordering :: Relaxed ) ;
4252 v
4353}
54+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
4455#[ inline]
4556fn has_kuser_cmpxchg64 ( ) -> bool {
4657 // Note: detect_false cfg is intended to make it easy for portable-atomic developers to
@@ -51,6 +62,7 @@ fn has_kuser_cmpxchg64() -> bool {
5162 }
5263 __kuser_helper_version ( ) >= 5
5364}
65+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
5466#[ inline]
5567unsafe fn __kuser_cmpxchg64 ( old_val : * const u64 , new_val : * const u64 , ptr : * mut u64 ) -> bool {
5668 // SAFETY: the caller must uphold the safety contract.
@@ -61,7 +73,107 @@ unsafe fn __kuser_cmpxchg64(old_val: *const u64, new_val: *const u64, ptr: *mut
6173 }
6274}
6375
76+ #[ cfg( any( target_feature = "v5te" , portable_atomic_target_feature = "v5te" ) ) ]
77+ macro_rules! blx {
78+ ( $addr: tt) => {
79+ concat!( "blx " , $addr)
80+ } ;
81+ }
82+ #[ cfg( not( any( target_feature = "v5te" , portable_atomic_target_feature = "v5te" ) ) ) ]
83+ macro_rules! blx {
84+ ( $addr: tt) => {
85+ concat!( "mov lr, pc" , "\n " , "bx " , $addr)
86+ } ;
87+ }
88+
89+ macro_rules! atomic_load_store {
90+ ( $( [ $( $generics: tt) * ] ) ? $atomic_type: ident, $value_type: ty, $asm_suffix: tt) => {
91+ impl $( <$( $generics) * >) ? $atomic_type $( <$( $generics) * >) ? {
92+ #[ cfg_attr(
93+ any( all( debug_assertions, not( portable_atomic_no_track_caller) ) , miri) ,
94+ track_caller
95+ ) ]
96+ #[ inline]
97+ pub ( crate ) fn load( & self , order: Ordering ) -> $value_type {
98+ crate :: utils:: assert_load_ordering( order) ;
99+ // SAFETY: any data races are prevented by atomic intrinsics and the raw
100+ // pointer passed in is valid because we got it from a reference.
101+ unsafe {
102+ match order {
103+ Ordering :: Relaxed => self . inner. load( Ordering :: Relaxed ) ,
104+ // Acquire and SeqCst loads are equivalent.
105+ Ordering :: Acquire | Ordering :: SeqCst => {
106+ debug_assert!( __kuser_helper_version( ) >= 3 ) ;
107+ let src = self . as_ptr( ) ;
108+ let out;
109+ asm!(
110+ concat!( "ldr" , $asm_suffix, " {out}, [{src}]" ) ,
111+ blx!( "{kuser_memory_barrier}" ) ,
112+ src = in( reg) src,
113+ out = lateout( reg) out,
114+ kuser_memory_barrier = inout( reg) KUSER_MEMORY_BARRIER => _,
115+ out( "lr" ) _,
116+ options( nostack, preserves_flags) ,
117+ ) ;
118+ out
119+ }
120+ _ => unreachable!( "{:?}" , order) ,
121+ }
122+ }
123+ }
124+ #[ inline]
125+ #[ cfg_attr(
126+ any( all( debug_assertions, not( portable_atomic_no_track_caller) ) , miri) ,
127+ track_caller
128+ ) ]
129+ pub ( crate ) fn store( & self , val: $value_type, order: Ordering ) {
130+ crate :: utils:: assert_store_ordering( order) ;
131+ let dst = self . as_ptr( ) ;
132+ // SAFETY: any data races are prevented by atomic intrinsics and the raw
133+ // pointer passed in is valid because we got it from a reference.
134+ unsafe {
135+ macro_rules! atomic_store_release {
136+ ( $acquire: expr) => { {
137+ debug_assert!( __kuser_helper_version( ) >= 3 ) ;
138+ asm!(
139+ blx!( "{kuser_memory_barrier}" ) ,
140+ concat!( "str" , $asm_suffix, " {val}, [{dst}]" ) ,
141+ $acquire,
142+ dst = in( reg) dst,
143+ val = in( reg) val,
144+ kuser_memory_barrier = inout( reg) KUSER_MEMORY_BARRIER => _,
145+ out( "lr" ) _,
146+ options( nostack, preserves_flags) ,
147+ )
148+ } } ;
149+ }
150+ match order {
151+ Ordering :: Relaxed => self . inner. store( val, Ordering :: Relaxed ) ,
152+ Ordering :: Release => atomic_store_release!( "" ) ,
153+ Ordering :: SeqCst => atomic_store_release!( blx!( "{kuser_memory_barrier}" ) ) ,
154+ _ => unreachable!( "{:?}" , order) ,
155+ }
156+ }
157+ }
158+ }
159+ } ;
160+ }
161+
162+ atomic_load_store ! ( AtomicI8 , i8 , "b" ) ;
163+ atomic_load_store ! ( AtomicU8 , u8 , "b" ) ;
164+ atomic_load_store ! ( AtomicI16 , i16 , "h" ) ;
165+ atomic_load_store ! ( AtomicU16 , u16 , "h" ) ;
166+ atomic_load_store ! ( AtomicI32 , i32 , "" ) ;
167+ atomic_load_store ! ( AtomicU32 , u32 , "" ) ;
168+ atomic_load_store ! ( AtomicIsize , isize , "" ) ;
169+ atomic_load_store ! ( AtomicUsize , usize , "" ) ;
170+ atomic_load_store ! ( [ T ] AtomicPtr , * mut T , "" ) ;
171+
172+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
173+ use crate :: utils:: { Pair , U64 } ;
174+
64175// 64-bit atomic load by two 32-bit atomic loads.
176+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
65177#[ inline]
66178unsafe fn byte_wise_atomic_load ( src : * const u64 ) -> u64 {
67179 // SAFETY: the caller must uphold the safety contract.
@@ -79,6 +191,7 @@ unsafe fn byte_wise_atomic_load(src: *const u64) -> u64 {
79191 }
80192}
81193
194+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
82195#[ inline( always) ]
83196unsafe fn atomic_update_kuser_cmpxchg64 < F > ( dst : * mut u64 , mut f : F ) -> u64
84197where
@@ -110,6 +223,7 @@ macro_rules! atomic_with_ifunc {
110223 unsafe fn $name: ident( $( $arg: tt) * ) $( -> $ret_ty: ty) ? { $( $kuser_cmpxchg64_fn_body: tt) * }
111224 fallback = $seqcst_fallback_fn: ident
112225 ) => {
226+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
113227 #[ inline]
114228 unsafe fn $name( $( $arg) * ) $( -> $ret_ty) ? {
115229 unsafe fn kuser_cmpxchg64_fn( $( $arg) * ) $( -> $ret_ty) ? {
@@ -254,6 +368,7 @@ atomic_with_ifunc! {
254368 fallback = atomic_neg_seqcst
255369}
256370
371+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
257372macro_rules! atomic64 {
258373 ( $atomic_type: ident, $int_type: ident, $atomic_max: ident, $atomic_min: ident) => {
259374 #[ repr( C , align( 8 ) ) ]
@@ -443,7 +558,9 @@ macro_rules! atomic64 {
443558 } ;
444559}
445560
561+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
446562atomic64 ! ( AtomicI64 , i64 , atomic_max, atomic_min) ;
563+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
447564atomic64 ! ( AtomicU64 , u64 , atomic_umax, atomic_umin) ;
448565
449566#[ allow(
@@ -464,10 +581,13 @@ mod tests {
464581 assert_eq ! ( version, unsafe { ( KUSER_HELPER_VERSION as * const i32 ) . read( ) } ) ;
465582 }
466583
584+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
467585 test_atomic_int ! ( i64 ) ;
586+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
468587 test_atomic_int ! ( u64 ) ;
469588
470589 // load/store/swap implementation is not affected by signedness, so it is
471590 // enough to test only unsigned types.
591+ #[ cfg( all( feature = "fallback" , not( portable_atomic_no_outline_atomics) ) ) ]
472592 stress_test ! ( u64 ) ;
473593}
0 commit comments