1- #![ allow( unused_doc_comments, missing_docs) ]
2- use crate :: { mem:: SizedTypeProperties , cfg_match, intrinsics, ub_checks} ;
1+ use crate :: { mem:: SizedTypeProperties , cfg_match, intrinsics} ;
32
4- /// Performs a volatile read of the value from `src` without moving it. This
5- /// leaves the memory in `src` unchanged.
6- ///
7- /// Volatile operations are intended to act on I/O memory, and are guaranteed
8- /// to not be elided or reordered by the compiler across other volatile
9- /// operations.
10- ///
11- /// # Notes
12- ///
13- /// Rust does not currently have a rigorously and formally defined memory model,
14- /// so the precise semantics of what "volatile" means here is subject to change
15- /// over time. That being said, the semantics will almost always end up pretty
16- /// similar to [C11's definition of volatile][c11].
17- ///
18- /// The compiler shouldn't change the relative order or number of volatile
19- /// memory operations. However, volatile memory operations on zero-sized types
20- /// (e.g., if a zero-sized type is passed to `read_volatile`) are noops
21- /// and may be ignored.
22- ///
23- /// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
24- ///
25- /// # Safety
26- ///
27- /// Behavior is undefined if any of the following conditions are violated:
28- ///
29- /// * `src` must be [valid] for reads.
30- ///
31- /// * `src` must be properly aligned.
32- ///
33- /// * `src` must point to a properly initialized value of type `T`.
34- ///
35- /// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
36- /// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
37- /// value and the value at `*src` can [violate memory safety][read-ownership].
38- /// However, storing non-[`Copy`] types in volatile memory is almost certainly
39- /// incorrect.
40- ///
41- /// Note that even if `T` has size `0`, the pointer must be properly aligned.
42- ///
43- /// [valid]: self#safety
44- /// [read-ownership]: read#ownership-of-the-returned-value
45- ///
46- /// Just like in C, whether an operation is volatile has no bearing whatsoever
47- /// on questions involving concurrent access from multiple threads. Volatile
48- /// accesses behave exactly like non-atomic accesses in that regard. In particular,
49- /// a race between a `read_volatile` and any write operation to the same location
50- /// is undefined behavior.
51- ///
52- /// # Examples
53- ///
54- /// Basic usage:
55- ///
56- /// ```
57- /// let x = 12;
58- /// let y = &x as *const i32;
59- ///
60- /// unsafe {
61- /// assert_eq!(std::ptr::read_volatile(y), 12);
62- /// }
63- /// ```
64- cfg_match ! {
65- all( target_arch = "arm" , target_feature = "thumb-mode" , target_pointer_width = "32" ) => {
3+ macro_rules! read_volatile {
4+ ( $name: ident, $express: expr) => {
665 #[ inline]
676 #[ stable( feature = "volatile" , since = "1.9.0" ) ]
687 #[ cfg_attr( miri, track_caller) ] // Even without panics, this helps for Miri backtraces
698 #[ rustc_diagnostic_item = "ptr_read_volatile" ]
70- pub unsafe fn read_volatile<T >( src: * const T ) -> T {
71- use crate :: mem:: { size_of, MaybeUninit } ;
72- use crate :: arch:: asm;
73- use crate :: intrinsics:: transmute_unchecked;
74-
9+ #[ doc = "read_volatile.md" ]
10+ pub unsafe fn read_volatile<T >( $name: * const T ) -> T {
7511 // SAFETY: the caller must uphold the safety contract for `volatile_load`.
7612 unsafe {
77- ub_checks:: assert_unsafe_precondition!(
13+ crate :: ub_checks:: assert_unsafe_precondition!(
7814 check_language_ub,
7915 "ptr::read_volatile requires that the pointer argument is aligned and non-null" ,
8016 (
81- addr: * const ( ) = src as * const ( ) ,
17+ addr: * const ( ) = $name as * const ( ) ,
8218 align: usize = align_of:: <T >( ) ,
8319 is_zst: bool = T :: IS_ZST ,
84- ) => ub_checks:: maybe_is_aligned_and_not_null( addr, align, is_zst)
20+ ) => crate :: ub_checks:: maybe_is_aligned_and_not_null( addr, align, is_zst)
8521 ) ;
22+ $express
23+ }
24+ }
25+ }
26+ }
8627
87- let out_val: T = match size_of:: <T >( ) {
28+ cfg_match ! {
29+ all( target_arch = "arm" , target_feature = "thumb-mode" , target_pointer_width = "32" ) => {
30+ read_volatile!( src, {
31+ use crate :: arch:: asm;
32+ use crate :: mem:: MaybeUninit ;
33+
34+ match size_of:: <T >( ) {
8835 // For the relevant sizes, ensure that just a single load is emitted
8936 // for the read with nothing merged or split.
9037 1 => {
@@ -95,7 +42,7 @@ cfg_match! {
9542 out = out( reg) byte
9643 ) ;
9744
98- transmute_unchecked( byte)
45+ intrinsics :: transmute_unchecked( byte)
9946 }
10047 2 => {
10148 let halfword: MaybeUninit :: <u16 >;
@@ -105,7 +52,7 @@ cfg_match! {
10552 out = out( reg) halfword
10653 ) ;
10754
108- transmute_unchecked( halfword)
55+ intrinsics :: transmute_unchecked( halfword)
10956 } ,
11057 4 => {
11158 let word: MaybeUninit :: <u32 >;
@@ -115,34 +62,14 @@ cfg_match! {
11562 out = out( reg) word
11663 ) ;
11764
118- transmute_unchecked( word)
65+ intrinsics :: transmute_unchecked( word)
11966 } ,
12067 // Anything else is mostly meaningless.
12168 _ => intrinsics:: volatile_load( src) ,
122- } ;
123- out_val
124- }
125- } }
126- // Fallback
69+ } }
70+ ) ;
71+ }
12772 _ => {
128- #[ inline]
129- #[ stable( feature = "volatile" , since = "1.9.0" ) ]
130- #[ cfg_attr( miri, track_caller) ] // Even without panics, this helps for Miri backtraces
131- #[ rustc_diagnostic_item = "ptr_read_volatile" ]
132- pub unsafe fn read_volatile<T >( src: * const T ) -> T {
133- // SAFETY: the caller must uphold the safety contract for `volatile_load`.
134- unsafe {
135- ub_checks:: assert_unsafe_precondition!(
136- check_language_ub,
137- "ptr::read_volatile requires that the pointer argument is aligned and non-null" ,
138- (
139- addr: * const ( ) = src as * const ( ) ,
140- align: usize = align_of:: <T >( ) ,
141- is_zst: bool = T :: IS_ZST ,
142- ) => ub_checks:: maybe_is_aligned_and_not_null( addr, align, is_zst)
143- ) ;
144- intrinsics:: volatile_load( src)
145- }
146- }
73+ read_volatile!( src, intrinsics:: volatile_load( src) ) ;
14774 }
14875}
0 commit comments