| Volatile operations are intended to act on I/O memory. As such, they are considered externally |
| observable events (just like syscalls, but less opaque), and are guaranteed to not be elided or |
| reordered by the compiler across other externally observable events. With this in mind, there |
| are two cases of usage that need to be distinguished: |
| |
| - When a volatile operation is used for memory inside an [allocation], it behaves exactly |
| like [`load`][Self::load], except for the additional guarantee that it won't be elided or |
| reordered across other externally observable events (see above). This implies that the |
| operation will actually access memory and not e.g. be lowered to reusing data from a |
| previous load. Other than that, all the usual rules for memory accesses apply (including |
| provenance). |
| |
| - Volatile operations, however, may also be used to access memory that is _outside_ of any Rust |
| allocation. In this use-case, the pointer does *not* have to be [valid] for reads. This is |
| typically used for CPU and peripheral registers that must be accessed via an I/O memory mapping, |
| most commonly at fixed addresses reserved by the hardware. These often have special semantics |
| associated to their manipulation, and cannot be used as general purpose memory. Here, any address |
| value is possible, including 0 and [`usize::MAX`], so long as the semantics of such a read are |
| well-defined by the target hardware. The provenance of the pointer is irrelevant, and it can be |
| created with [`without_provenance`][crate::ptr::without_provenance]. The access is allowed to |
| trap, which must immediately abort the process. It can also cause other side-effects, but those |
| must not affect Rust-allocated memory in any way. |
| |
| In both cases, the access is also considered atomic with the given `order`. This allows |
| synchronization with other threads or devices that share memory with this program. |
| |
| When invoked during const evaluation, this behaves like a regular atomic load. In |
| particular, such reads must always follow the first of the two cases above. |
| |
| [allocation]: crate::ptr#allocated-object |