Coverage Report

Created: 2026-09-14 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/safe_arch-1.2.0/src/lib.rs
Line
Count
Source
1
#![no_std]
2
#![warn(missing_docs)]
3
#![allow(unused_imports)]
4
#![allow(clippy::too_many_arguments)]
5
#![allow(clippy::transmute_ptr_to_ptr)]
6
#![cfg_attr(docsrs, feature(doc_cfg))]
7
8
//! A crate that safely exposes arch intrinsics via `#[cfg()]`.
9
//!
10
//! `safe_arch` lets you safely use CPU intrinsics. Those things in the
11
//! [`core::arch`](core::arch) modules. It works purely via `#[cfg()]` and
12
//! compile time CPU feature declaration. If you want to check for a feature at
13
//! runtime and then call an intrinsic or use a fallback path based on that then
14
//! this crate is sadly not for you.
15
//!
16
//! SIMD register types are "newtype'd" so that better trait impls can be given
17
//! to them, but the inner value is a `pub` field so feel free to just grab it
18
//! out if you need to. Trait impls of the newtypes include: `Default` (zeroed),
19
//! `From`/`Into` of appropriate data types, and appropriate operator
20
//! overloading.
21
//!
22
//! * Most intrinsics (like addition and multiplication) are totally safe to use
23
//!   as long as the CPU feature is available. In this case, what you get is 1:1
24
//!   with the actual intrinsic.
25
//! * Some intrinsics take a pointer of an assumed minimum alignment and
26
//!   validity span. For these, the `safe_arch` function takes a reference of an
27
//!   appropriate type to uphold safety.
28
//!   * Try the [bytemuck](https://docs.rs/bytemuck) crate (and turn on the
29
//!     `bytemuck` feature of this crate) if you want help safely casting
30
//!     between reference types.
31
//! * Some intrinsics are not safe unless you're _very_ careful about how you
32
//!   use them, such as the streaming operations requiring you to use them in
33
//!   combination with an appropriate memory fence. Those operations aren't
34
//!   exposed here.
35
//! * Some intrinsics mess with the processor state, such as changing the
36
//!   floating point flags, saving and loading special register state, and so
37
//!   on. LLVM doesn't really support you messing with that within a high level
38
//!   language, so those operations aren't exposed here. Use assembly or
39
//!   something if you want to do that.
40
//!
41
//! ## Naming Conventions
42
//! The `safe_arch` crate does not simply use the "official" names for each
43
//! intrinsic, because the official names are generally poor. Instead, the
44
//! operations have been given better names that makes things hopefully easier
45
//! to understand then you're reading the code.
46
//!
47
//! For a full explanation of the naming used, see the [Naming
48
//! Conventions](crate::naming_conventions) page.
49
//!
50
//! ## Current Support
51
//! * `x86` / `x86_64` (Intel, AMD, etc)
52
//!   * 128-bit: `sse`, `sse2`, `sse3`, `ssse3`, `sse4.1`, `sse4.2`
53
//!   * 256-bit: `avx`, `avx2`
54
//!   * Other: `adx`, `aes`, `bmi1`, `bmi2`, `fma`, `lzcnt`, `pclmulqdq`,
55
//!     `popcnt`, `rdrand`, `rdseed`
56
//!
57
//! ## Compile Time CPU Target Features
58
//!
59
//! At the time of me writing this, Rust enables the `sse` and `sse2` CPU
60
//! features by default for all `i686` (x86) and `x86_64` builds. Those CPU
61
//! features are built into the design of `x86_64`, and you'd need a _super_ old
62
//! `x86` CPU for it to not support at least `sse` and `sse2`, so they're a safe
63
//! bet for the language to enable all the time. In fact, because the standard
64
//! library is compiled with them enabled, simply trying to _disable_ those
65
//! features would actually cause ABI issues and fill your program with UB
66
//! ([link][rustc_docs]).
67
//!
68
//! If you want additional CPU features available at compile time you'll have to
69
//! enable them with an additional arg to `rustc`. For a feature named `name`
70
//! you pass `-C target-feature=+name`, such as `-C target-feature=+sse3` for
71
//! `sse3`.
72
//!
73
//! You can alternately enable _all_ target features of the current CPU with `-C
74
//! target-cpu=native`. This is primarily of use if you're building a program
75
//! you'll only run on your own system.
76
//!
77
//! It's sometimes hard to know if your target platform will support a given
78
//! feature set, but the [Steam Hardware Survey][steam-survey] is generally
79
//! taken as a guide to what you can expect people to have available. If you
80
//! click "Other Settings" it'll expand into a list of CPU target features and
81
//! how common they are. These days, it seems that `sse3` can be safely assumed,
82
//! and `ssse3`, `sse4.1`, and `sse4.2` are pretty safe bets as well. The stuff
83
//! above 128-bit isn't as common yet, give it another few years.
84
//!
85
//! **Please note that executing a program on a CPU that doesn't support the
86
//! target features it was compiles for is Undefined Behavior.**
87
//!
88
//! Currently, Rust doesn't actually support an easy way for you to check that a
89
//! feature enabled at compile time is _actually_ available at runtime. There is
90
//! the "[feature_detected][feature_detected]" family of macros, but if you
91
//! enable a feature they will evaluate to a constant `true` instead of actually
92
//! deferring the check for the feature to runtime. This means that, if you
93
//! _did_ want a check at the start of your program, to confirm that all the
94
//! assumed features are present and error out when the assumptions don't hold,
95
//! you can't use that macro. You gotta use CPUID and check manually. rip.
96
//! Hopefully we can make that process easier in a future version of this crate.
97
//!
98
//! [steam-survey]:
99
//! https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam
100
//! [feature_detected]:
101
//! https://doc.rust-lang.org/std/index.html?search=feature_detected
102
//! [rustc_docs]: https://doc.rust-lang.org/rustc/targets/known-issues.html
103
//!
104
//! ### A Note On Working With Cfg
105
//!
106
//! There's two main ways to use `cfg`:
107
//! * Via an attribute placed on an item, block, or expression:
108
//!   * `#[cfg(debug_assertions)] println!("hello");`
109
//! * Via a macro used within an expression position:
110
//!   * `if cfg!(debug_assertions) { println!("hello"); }`
111
//!
112
//! The difference might seem small but it's actually very important:
113
//! * The attribute form will include code or not _before_ deciding if all the
114
//!   items named and so forth really exist or not. This means that code that is
115
//!   configured via attribute can safely name things that don't always exist as
116
//!   long as the things they name do exist whenever that code is configured
117
//!   into the build.
118
//! * The macro form will include the configured code _no matter what_, and then
119
//!   the macro resolves to a constant `true` or `false` and the compiler uses
120
//!   dead code elimination to cut out the path not taken.
121
//!
122
//! This crate uses `cfg` via the attribute, so the functions it exposes don't
123
//! exist at all when the appropriate CPU target features aren't enabled.
124
//! Accordingly, if you plan to call this crate or not depending on what
125
//! features are enabled in the build you'll also need to control your use of
126
//! this crate via cfg attribute, not cfg macro.
127
128
use core::{
129
  convert::AsRef,
130
  fmt::{Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex},
131
  ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign, Mul, MulAssign, Neg, Not, Sub, SubAssign},
132
};
133
134
pub mod naming_conventions;
135
136
/// Turns a round operator token to the correct constant value.
137
#[macro_export]
138
#[cfg_attr(docsrs, doc(cfg(target_feature = "avx")))]
139
// Note(Lokathor): keep this at the crate root.
140
macro_rules! round_op {
141
  (Nearest) => {{
142
    #[cfg(target_arch = "x86")]
143
    use ::core::arch::x86::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_NEAREST_INT};
144
    #[cfg(target_arch = "x86_64")]
145
    use ::core::arch::x86_64::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_NEAREST_INT};
146
    _MM_FROUND_NO_EXC | _MM_FROUND_TO_NEAREST_INT
147
  }};
148
  (NegInf) => {{
149
    #[cfg(target_arch = "x86")]
150
    use ::core::arch::x86::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_NEG_INF};
151
    #[cfg(target_arch = "x86_64")]
152
    use ::core::arch::x86_64::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_NEG_INF};
153
    _MM_FROUND_NO_EXC | _MM_FROUND_TO_NEG_INF
154
  }};
155
  (PosInf) => {{
156
    #[cfg(target_arch = "x86")]
157
    use ::core::arch::x86::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_POS_INF};
158
    #[cfg(target_arch = "x86_64")]
159
    use ::core::arch::x86_64::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_POS_INF};
160
    _MM_FROUND_NO_EXC | _MM_FROUND_TO_POS_INF
161
  }};
162
  (Zero) => {{
163
    #[cfg(target_arch = "x86")]
164
    use ::core::arch::x86::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_ZERO, _mm256_round_pd};
165
    #[cfg(target_arch = "x86_64")]
166
    use ::core::arch::x86_64::{_MM_FROUND_NO_EXC, _MM_FROUND_TO_ZERO, _mm256_round_pd};
167
    _MM_FROUND_NO_EXC | _MM_FROUND_TO_ZERO
168
  }};
169
}
170
171
/// Declares a private mod and then a glob `use` with the visibility specified.
172
macro_rules! submodule {
173
  ($v:vis $name:ident) => {
174
    mod $name;
175
    $v use $name::*;
176
  };
177
  ($v:vis $name:ident { $($content:tt)* }) => {
178
    mod $name { $($content)* }
179
    $v use $name::*;
180
  };
181
}
182
183
// Note(Lokathor): Stupid as it sounds, we need to put the imports here at the
184
// crate root because the arch-specific macros that we define in our inner
185
// modules are actually "scoped" to also be at the crate root. We want the
186
// rustdoc generation of the macros to "see" these imports so that the docs link
187
// over to the `core::arch` module correctly.
188
// https://github.com/rust-lang/rust/issues/72243
189
190
#[cfg(target_arch = "x86")]
191
use core::arch::x86::*;
192
#[cfg(target_arch = "x86_64")]
193
use core::arch::x86_64::*;
194
195
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
196
submodule!(pub x86_x64 {
197
  //! Types and functions for safe `x86` / `x86_64` intrinsic usage.
198
  //!
199
  //! `x86_64` is essentially a superset of `x86`, so we just lump it all into
200
  //! one module. Anything not available on `x86` simply won't be in the build
201
  //! on that arch.
202
  use super::*;
203
204
  submodule!(pub m128_);
205
  submodule!(pub m128d_);
206
  submodule!(pub m128i_);
207
208
  submodule!(pub m256_);
209
  submodule!(pub m256d_);
210
  submodule!(pub m256i_);
211
212
  submodule!(pub m512_);
213
  submodule!(pub m512d_);
214
  submodule!(pub m512i_);
215
216
  // Note(Lokathor): We only include these sub-modules with the actual functions
217
  // if the feature is enabled. Ae *also* have a cfg attribute on the inside of
218
  // the modules as a "double-verification" of sorts. Technically either way on
219
  // its own would also be fine.
220
221
  // These CPU features follow a fairly clear and strict progression that's easy
222
  // to remember. Most of them offer a fair pile of new functions.
223
  #[cfg(target_feature = "sse")]
224
  submodule!(pub sse);
225
  #[cfg(target_feature = "sse2")]
226
  submodule!(pub sse2);
227
  #[cfg(target_feature = "sse3")]
228
  submodule!(pub sse3);
229
  #[cfg(target_feature = "ssse3")]
230
  submodule!(pub ssse3);
231
  #[cfg(target_feature = "sse4.1")]
232
  submodule!(pub sse4_1);
233
  #[cfg(target_feature = "sse4.2")]
234
  submodule!(pub sse4_2);
235
  #[cfg(target_feature = "avx")]
236
  submodule!(pub avx);
237
  #[cfg(target_feature = "avx2")]
238
  submodule!(pub avx2);
239
  #[cfg(target_feature = "avx512f")]
240
  submodule!(pub avx512);
241
242
  // These features aren't as easy to remember the progression of and they each
243
  // only add a small handful of functions.
244
  #[cfg(target_feature = "adx")]
245
  submodule!(pub adx);
246
  #[cfg(target_feature = "aes")]
247
  submodule!(pub aes);
248
  #[cfg(target_feature = "avx512ifma")]
249
  submodule!(pub avx512ifma);
250
  #[cfg(target_feature = "bmi1")]
251
  submodule!(pub bmi1);
252
  #[cfg(target_feature = "bmi2")]
253
  submodule!(pub bmi2);
254
  #[cfg(target_feature = "fma")]
255
  submodule!(pub fma);
256
  #[cfg(target_feature = "lzcnt")]
257
  submodule!(pub lzcnt);
258
  #[cfg(target_feature = "pclmulqdq")]
259
  submodule!(pub pclmulqdq);
260
  #[cfg(target_feature = "popcnt")]
261
  submodule!(pub popcnt);
262
  #[cfg(target_feature = "rdrand")]
263
  submodule!(pub rdrand);
264
  #[cfg(target_feature = "rdseed")]
265
  submodule!(pub rdseed);
266
267
  /// Reads the CPU's timestamp counter value.
268
  ///
269
  /// This is a monotonically increasing time-stamp that goes up every clock
270
  /// cycle of the CPU. However, since modern CPUs are variable clock rate
271
  /// depending on demand this can't actually be used for telling the time. It
272
  /// also does _not_ fully serialize all operations, so previous instructions
273
  /// might still be in progress when this reads the timestamp.
274
  ///
275
  /// * **Intrinsic:** `_rdtsc`
276
  /// * **Assembly:** `rdtsc`
277
0
  pub fn read_timestamp_counter() -> u64 {
278
    // Note(Lokathor): This was changed from i64 to u64 at some point, but
279
    // everyone ever was already casting this value to `u64` so crater didn't
280
    // even consider it a problem. We will follow suit.
281
    #[allow(clippy::unnecessary_cast)]
282
0
    unsafe { _rdtsc() as u64 }
283
0
  }
284
285
  /// Reads the CPU's timestamp counter value and store the processor signature.
286
  ///
287
  /// This works similar to [`read_timestamp_counter`] with two main
288
  /// differences:
289
  /// * It and also stores the `IA32_TSC_AUX MSR` value to the reference given.
290
  /// * It waits on all previous instructions to finish before reading the
291
  ///   timestamp (though it doesn't prevent other instructions from starting).
292
  ///
293
  /// As with `read_timestamp_counter`, you can't actually use this to tell the
294
  /// time.
295
  ///
296
  /// * **Intrinsic:** `__rdtscp`
297
  /// * **Assembly:** `rdtscp`
298
0
  pub fn read_timestamp_counter_p(aux: &mut u32) -> u64 {
299
0
    unsafe { __rdtscp(aux) }
300
0
  }
301
302
  /// Swap the bytes of the given 32-bit value.
303
  ///
304
  /// ```
305
  /// # use safe_arch::*;
306
  /// assert_eq!(byte_swap_i32(0x0A123456), 0x5634120A);
307
  /// ```
308
  /// * **Intrinsic:** `_bswap`
309
  /// * **Assembly:** `bswap r32`
310
0
  pub fn byte_swap_i32(i: i32) -> i32 {
311
    #[allow(unused_unsafe)]
312
0
    unsafe { _bswap(i)}
313
0
  }
314
315
  /// Swap the bytes of the given 64-bit value.
316
  ///
317
  /// ```
318
  /// # use safe_arch::*;
319
  /// assert_eq!(byte_swap_i64(0x0A123456_789ABC01), 0x01BC9A78_5634120A);
320
  /// ```
321
  /// * **Intrinsic:** `_bswap64`
322
  /// * **Assembly:** `bswap r64`
323
  #[cfg(target_arch="x86_64")]
324
0
  pub fn byte_swap_i64(i: i64) -> i64 {
325
    #[allow(unused_unsafe)]
326
0
    unsafe { _bswap64(i) }
327
0
  }
328
});