|
| 1 | +#![allow(unused_doc_comments, missing_docs)] |
| 2 | +use crate::{mem::SizedTypeProperties, cfg_match, intrinsics, ub_checks}; |
| 3 | + |
| 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") => { |
| 66 | + #[inline] |
| 67 | + #[stable(feature = "volatile", since = "1.9.0")] |
| 68 | + #[cfg_attr(miri, track_caller)] // Even without panics, this helps for Miri backtraces |
| 69 | + #[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 | + |
| 75 | + // SAFETY: the caller must uphold the safety contract for `volatile_load`. |
| 76 | + unsafe { |
| 77 | + ub_checks::assert_unsafe_precondition!( |
| 78 | + check_language_ub, |
| 79 | + "ptr::read_volatile requires that the pointer argument is aligned and non-null", |
| 80 | + ( |
| 81 | + addr: *const () = src as *const (), |
| 82 | + align: usize = align_of::<T>(), |
| 83 | + is_zst: bool = T::IS_ZST, |
| 84 | + ) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst) |
| 85 | + ); |
| 86 | + |
| 87 | + let out_val: T = match size_of::<T>() { |
| 88 | + // For the relevant sizes, ensure that just a single load is emitted |
| 89 | + // for the read with nothing merged or split. |
| 90 | + 1 => { |
| 91 | + let byte: MaybeUninit::<u8>; |
| 92 | + asm!( |
| 93 | + "LDRB {out}, [{in}]", |
| 94 | + in = in(reg) src, |
| 95 | + out = out(reg) byte |
| 96 | + ); |
| 97 | + |
| 98 | + transmute_unchecked(byte) |
| 99 | + } |
| 100 | + 2 => { |
| 101 | + let halfword: MaybeUninit::<u16>; |
| 102 | + asm!( |
| 103 | + "LDRH {out}, [{in}]", |
| 104 | + in = in(reg) src, |
| 105 | + out = out(reg) halfword |
| 106 | + ); |
| 107 | + |
| 108 | + transmute_unchecked(halfword) |
| 109 | + }, |
| 110 | + 4 => { |
| 111 | + let word: MaybeUninit::<u32>; |
| 112 | + asm!( |
| 113 | + "LDR {out}, [{in}]", |
| 114 | + in = in(reg) src, |
| 115 | + out = out(reg) word |
| 116 | + ); |
| 117 | + |
| 118 | + transmute_unchecked(word) |
| 119 | + }, |
| 120 | + // Anything else is mostly meaningless. |
| 121 | + _ => intrinsics::volatile_load(src), |
| 122 | + }; |
| 123 | + out_val |
| 124 | + } |
| 125 | + }} |
| 126 | + // Fallback |
| 127 | + _ => { |
| 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 | + } |
| 147 | + } |
| 148 | +} |
0 commit comments