Skip to content

Commit 8069abb

Browse files
committed
Move read_volatile to volatile module and implement thumbv6 in asm.
The goal is to ensure LLVM doesn't do anything silly (:3) with the loads.
1 parent ae9173d commit 8069abb

File tree

2 files changed

+130
-80
lines changed

2 files changed

+130
-80
lines changed

library/core/src/ptr/mod.rs

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,12 @@ pub use non_null::NonNull;
425425
mod unique;
426426
#[unstable(feature = "ptr_internals", issue = "none")]
427427
pub use unique::Unique;
428+
#[stable(feature = "volatile", since = "1.9.0")]
429+
pub use volatile::read_volatile;
428430

429431
mod const_ptr;
430432
mod mut_ptr;
433+
mod volatile;
431434

432435
/// Executes the destructor (if any) of the pointed-to value.
433436
///
@@ -1669,86 +1672,6 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
16691672
}
16701673
}
16711674

1672-
/// Performs a volatile read of the value from `src` without moving it. This
1673-
/// leaves the memory in `src` unchanged.
1674-
///
1675-
/// Volatile operations are intended to act on I/O memory, and are guaranteed
1676-
/// to not be elided or reordered by the compiler across other volatile
1677-
/// operations.
1678-
///
1679-
/// # Notes
1680-
///
1681-
/// Rust does not currently have a rigorously and formally defined memory model,
1682-
/// so the precise semantics of what "volatile" means here is subject to change
1683-
/// over time. That being said, the semantics will almost always end up pretty
1684-
/// similar to [C11's definition of volatile][c11].
1685-
///
1686-
/// The compiler shouldn't change the relative order or number of volatile
1687-
/// memory operations. However, volatile memory operations on zero-sized types
1688-
/// (e.g., if a zero-sized type is passed to `read_volatile`) are noops
1689-
/// and may be ignored.
1690-
///
1691-
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
1692-
///
1693-
/// # Safety
1694-
///
1695-
/// Behavior is undefined if any of the following conditions are violated:
1696-
///
1697-
/// * `src` must be [valid] for reads.
1698-
///
1699-
/// * `src` must be properly aligned.
1700-
///
1701-
/// * `src` must point to a properly initialized value of type `T`.
1702-
///
1703-
/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
1704-
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
1705-
/// value and the value at `*src` can [violate memory safety][read-ownership].
1706-
/// However, storing non-[`Copy`] types in volatile memory is almost certainly
1707-
/// incorrect.
1708-
///
1709-
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
1710-
///
1711-
/// [valid]: self#safety
1712-
/// [read-ownership]: read#ownership-of-the-returned-value
1713-
///
1714-
/// Just like in C, whether an operation is volatile has no bearing whatsoever
1715-
/// on questions involving concurrent access from multiple threads. Volatile
1716-
/// accesses behave exactly like non-atomic accesses in that regard. In particular,
1717-
/// a race between a `read_volatile` and any write operation to the same location
1718-
/// is undefined behavior.
1719-
///
1720-
/// # Examples
1721-
///
1722-
/// Basic usage:
1723-
///
1724-
/// ```
1725-
/// let x = 12;
1726-
/// let y = &x as *const i32;
1727-
///
1728-
/// unsafe {
1729-
/// assert_eq!(std::ptr::read_volatile(y), 12);
1730-
/// }
1731-
/// ```
1732-
#[inline]
1733-
#[stable(feature = "volatile", since = "1.9.0")]
1734-
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
1735-
#[rustc_diagnostic_item = "ptr_read_volatile"]
1736-
pub unsafe fn read_volatile<T>(src: *const T) -> T {
1737-
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
1738-
unsafe {
1739-
ub_checks::assert_unsafe_precondition!(
1740-
check_language_ub,
1741-
"ptr::read_volatile requires that the pointer argument is aligned and non-null",
1742-
(
1743-
addr: *const () = src as *const (),
1744-
align: usize = align_of::<T>(),
1745-
is_zst: bool = T::IS_ZST,
1746-
) => ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
1747-
);
1748-
intrinsics::volatile_load(src)
1749-
}
1750-
}
1751-
17521675
/// Performs a volatile write of a memory location with the given value without
17531676
/// reading or dropping the old value.
17541677
///

library/core/src/ptr/volatile.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use crate::{mem::SizedTypeProperties, cfg_match, intrinsics};
2+
3+
/// Performs a volatile read of the value from `src` without moving it. This
4+
/// leaves the memory in `src` unchanged.
5+
///
6+
/// Volatile operations are intended to act on I/O memory, and are guaranteed
7+
/// to not be elided or reordered by the compiler across other volatile
8+
/// operations.
9+
///
10+
/// # Notes
11+
///
12+
/// Rust does not currently have a rigorously and formally defined memory model,
13+
/// so the precise semantics of what "volatile" means here is subject to change
14+
/// over time. That being said, the semantics will almost always end up pretty
15+
/// similar to [C11's definition of volatile][c11].
16+
///
17+
/// The compiler shouldn't change the relative order or number of volatile
18+
/// memory operations. However, volatile memory operations on zero-sized types
19+
/// (e.g., if a zero-sized type is passed to `read_volatile`) are noops
20+
/// and may be ignored.
21+
///
22+
/// [c11]: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf
23+
///
24+
/// # Safety
25+
///
26+
/// Behavior is undefined if any of the following conditions are violated:
27+
///
28+
/// * `src` must be [valid] for reads.
29+
///
30+
/// * `src` must be properly aligned.
31+
///
32+
/// * `src` must point to a properly initialized value of type `T`.
33+
///
34+
/// Like [`read`], `read_volatile` creates a bitwise copy of `T`, regardless of
35+
/// whether `T` is [`Copy`]. If `T` is not [`Copy`], using both the returned
36+
/// value and the value at `*src` can [violate memory safety][read-ownership].
37+
/// However, storing non-[`Copy`] types in volatile memory is almost certainly
38+
/// incorrect.
39+
///
40+
/// Note that even if `T` has size `0`, the pointer must be properly aligned.
41+
///
42+
/// [valid]: self#safety
43+
/// [read-ownership]: read#ownership-of-the-returned-value
44+
///
45+
/// Just like in C, whether an operation is volatile has no bearing whatsoever
46+
/// on questions involving concurrent access from multiple threads. Volatile
47+
/// accesses behave exactly like non-atomic accesses in that regard. In particular,
48+
/// a race between a `read_volatile` and any write operation to the same location
49+
/// is undefined behavior.
50+
///
51+
/// # Examples
52+
///
53+
/// Basic usage:
54+
///
55+
/// ```
56+
/// let x = 12;
57+
/// let y = &x as *const i32;
58+
///
59+
/// unsafe {
60+
/// assert_eq!(std::ptr::read_volatile(y), 12);
61+
/// }
62+
/// ```
63+
#[inline]
64+
#[stable(feature = "volatile", since = "1.9.0")]
65+
#[cfg_attr(miri, track_caller)] // Even without panics, this helps for Miri backtraces
66+
#[rustc_diagnostic_item = "ptr_read_volatile"]
67+
pub unsafe fn read_volatile<T>(src: *const T) -> T {
68+
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
69+
unsafe {
70+
crate::ub_checks::assert_unsafe_precondition!(
71+
check_language_ub,
72+
"ptr::read_volatile requires that the pointer argument is aligned and non-null",
73+
(
74+
addr: *const () = src as *const (),
75+
align: usize = align_of::<T>(),
76+
is_zst: bool = T::IS_ZST,
77+
) => crate::ub_checks::maybe_is_aligned_and_not_null(addr, align, is_zst)
78+
);
79+
cfg_match! {
80+
all(target_arch = "arm", target_feature = "thumb-mode", target_pointer_width = "32") => {
81+
{
82+
use crate::arch::asm;
83+
use crate::mem::MaybeUninit;
84+
85+
match size_of::<T>() {
86+
// For the relevant sizes, ensure that just a single load is emitted
87+
// for the read with nothing merged or split.
88+
1 => {
89+
let byte: MaybeUninit::<u8>;
90+
asm!(
91+
"ldrb {out}, [{in}]",
92+
in = in(reg) src,
93+
out = out(reg) byte
94+
);
95+
96+
intrinsics::transmute_unchecked(byte)
97+
}
98+
2 => {
99+
let halfword: MaybeUninit::<u16>;
100+
asm!(
101+
"ldrh {out}, [{in}]",
102+
in = in(reg) src,
103+
out = out(reg) halfword
104+
);
105+
106+
intrinsics::transmute_unchecked(halfword)
107+
},
108+
4 => {
109+
let word: MaybeUninit::<u32>;
110+
asm!(
111+
"ldr {out}, [{in}]",
112+
in = in(reg) src,
113+
out = out(reg) word
114+
);
115+
116+
intrinsics::transmute_unchecked(word)
117+
},
118+
// Anything else is mostly meaningless.
119+
_ => intrinsics::volatile_load(src),
120+
}
121+
}}
122+
_ => {
123+
intrinsics::volatile_load(src)
124+
}
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)