Skip to content

Commit cd96f3f

Browse files
committed
Use a macro for generating the read_volatile boilerplate
1 parent efa8fee commit cd96f3f

File tree

2 files changed

+69
-100
lines changed

2 files changed

+69
-100
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
Performs a volatile read of the value from `src` without moving it. This
2+
leaves the memory in `src` unchanged.
3+
Volatile operations are intended to act on I/O memory, and are guaranteed
4+
to not be elided or reordered by the compiler across other volatile
5+
operations.
6+
# Notes
7+
Rust does not currently have a rigorously and formally defined memory model,
8+
so the precise semantics of what "volatile" means here is subject to change
9+
over time. That being said, the semantics will almost always end up pretty
10+
similar to [C11's definition of volatile][c11].
11+
The compiler shouldn't change the relative order or number of volatile
12+
memory operations. However, volatile memory operations on zero-sized types
13+
(e.g., if a zero-sized type is passed to `read_volatile`) are noops
14+
and may be ignored.
15+
[c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
16+
# Safety
17+
Behavior is undefined if any of the following conditions are violated:
18+
* `src` must be [valid] for reads.
19+
* `src` must be properly aligned.
20+
* `src` must point to a properly initialized value of type `T`.
21+
Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
22+
whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
23+
value and the value at `*src` can [violate memory safety][read-ownership].
24+
However, storing non-[`Copy`] types in volatile memory is almost certainly
25+
incorrect.
26+
Note that even if `T` has size `0`, the pointer must be properly aligned.
27+
[valid]: self#safety
28+
[read-ownership]: read#ownership-of-the-returned-value
29+
Just like in C, whether an operation is volatile has no bearing whatsoever
30+
on questions involving concurrent access from multiple threads. Volatile
31+
accesses behave exactly like non-atomic accesses in that regard. In particular,
32+
a race between a `read_volatile` and any write operation to the same location
33+
is undefined behavior.
34+
# Examples
35+
Basic usage:
36+
```
37+
let x = 12;
38+
let y = &x as *const i32;
39+
unsafe {
40+
assert_eq!(std::ptr::read_volatile(y), 12);
41+
}
42+
```

library/core/src/ptr/volatile.rs

Lines changed: 27 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,37 @@
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

Comments
 (0)