1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
//! This crate provides a `no_std` version of std's [`is_x86_feature_detected!`] //! macro. //! //! This is possible because x86 chips can just use the `cpuid` instruction to //! detect CPU features, whereas most other architectures require either reading //! files or querying the OS. //! //! # Usage //! //! ``` //! # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] //! # fn main() { //! if core_detect::is_x86_feature_detected!("ssse3") { //! println!("SSSE3 is available"); //! } //! # } //! # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] //! # fn main() {} //! ``` //! //! Note that like the [equivalent macro in `std`][stddetect], this will error //! on architectures other than x86/x86_64, so you should put the code behind a //! `#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]` check. //! //! [stddetect]: https://doc.rust-lang.org/nightly/std/macro.is_x86_feature_detected.html //! //! (In the future, this crate may provide another macro which returns false in //! these cases instead, and supports testing multiple features simultaneously). //! //! # Caveats //! //! If you use this library on a machine older than the introduction of the //! `cpuid` instruction (that is, a machine from before around 1994), we'll end //! up executing the instruction regardless. There's no stable way to detect //! this currently, and in practice it's pretty difficult to target a machine //! this old with Rust/LLVM anyway, so it's probably fine. //! //! If you do run this code on a machine that old, we'll exit with a `SIGILL`. //! This might sound bad, but in practice this is how `core::intrinsics::abort` //! typically exits, so there should be no security concerns or anything like //! that. Ideally we'd use [`arch::x86::has_cpuid`], but this function is //! unstable, and requires inline asm to implement. //! //! [`arch::x86::has_cpuid`]: https://doc.rust-lang.org/nightly/core/arch/x86/fn.has_cpuid.html //! //! If this is unacceptable, you have two options (both of which are ignored //! if we can determine this statically): //! //! - If are on nightly, you can enable the `unstable_has_cpuid` feature. //! //! - Otherwise, you can disable the on-by-default `assume_has_cpuid` feature. //! (This is ignored if both it and `unstable_has_cpuid` are on). //! //! (Also, file an issue if you really care about 30 year old machines, I have //! some other workarounds that I can finish up in that case). //! //! (Note: For clarity, we *do* handle newer machines known to not have `cpuid` //! correctly — for example `#[cfg(target_env = "sgx")]`) #![no_std] #![allow(dead_code)] #![cfg_attr( all( target_arch = "x86", not(target_env = "sgx"), not(target_feature = "sse"), feature = "unstable_has_cpuid", ), feature(stdsimd) )] #[macro_use] mod macros; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[path = "arch/x86.rs"] #[macro_use] mod arch; // Unimplemented architecture: #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] mod arch { #[doc(hidden)] pub(crate) enum Feature { Null, } #[doc(hidden)] pub mod __is_feature_detected {} impl Feature { #[doc(hidden)] pub(crate) fn from_str(_s: &str) -> Result<Feature, ()> { Err(()) } #[doc(hidden)] pub(crate) fn to_str(self) -> &'static str { "" } } } pub(crate) use crate::arch::Feature; #[doc(hidden)] pub use crate::arch::__is_feature_detected; /// Performs run-time feature detection. #[inline] #[allow(dead_code)] fn check_for(x: Feature) -> bool { cache::test(x as u32) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] #[path = "os/x86.rs"] mod os; #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] mod os { #[inline] pub(crate) fn detect_features() -> crate::cache::Initializer { Default::default() } } #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] #[macro_export] macro_rules! is_x86_feature_detected { ($t: tt) => { compile_error!( r#" is_x86_feature_detected can only be used on x86 and x86_64 targets. You can prevent it from being used in other architectures by guarding it behind a cfg(target_arch) as follows: #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { if is_x86_feature_detected(...) { ... } } "# ) }; } mod cache;