Format modules defined inside cfg_if macro calls (#3600)

This commit is contained in:
Seiichi Uchida 2019-06-08 18:47:18 +09:00 committed by GitHub
parent 87565c42ce
commit e71bffb008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
60 changed files with 5592 additions and 53 deletions

View File

@ -99,8 +99,8 @@ fn format_project<T: FormatHandler>(
let mut context = FormatContext::new(&krate, report, parse_session, config, handler);
let files = modules::ModResolver::new(
context.parse_session.source_map(),
directory_ownership.unwrap_or(parse::DirectoryOwnership::UnownedViaMod(false)),
&context.parse_session,
directory_ownership.unwrap_or(parse::DirectoryOwnership::UnownedViaMod(true)),
!(input_is_stdin || config.skip_children()),
)
.visit_crate(&krate)
@ -112,7 +112,7 @@ fn format_project<T: FormatHandler>(
}
should_emit_verbose(input_is_stdin, config, || println!("Formatting {}", path));
let is_root = path == main_file;
context.format_file(path, module, is_root)?;
context.format_file(path, &module, is_root)?;
}
timer = timer.done_formatting();

View File

@ -1,23 +1,27 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use syntax::ast;
use syntax::parse::{parser, DirectoryOwnership};
use syntax::parse::{parser, DirectoryOwnership, ParseSess};
use syntax::source_map;
use syntax::symbol::sym;
use syntax::visit::Visitor;
use syntax_pos::symbol::Symbol;
use crate::config::FileName;
use crate::items::is_mod_decl;
use crate::utils::contains_skip;
type FileModMap<'a> = BTreeMap<FileName, (&'a ast::Mod, String)>;
mod visitor;
type FileModMap<'ast> = BTreeMap<FileName, (Cow<'ast, ast::Mod>, String)>;
/// Maps each module to the corresponding file.
pub(crate) struct ModResolver<'a, 'b> {
source_map: &'b source_map::SourceMap,
pub(crate) struct ModResolver<'ast, 'sess> {
parse_sess: &'sess ParseSess,
directory: Directory,
file_map: FileModMap<'a>,
file_map: FileModMap<'ast>,
recursive: bool,
}
@ -27,10 +31,28 @@ struct Directory {
ownership: DirectoryOwnership,
}
impl<'a, 'b> ModResolver<'a, 'b> {
impl<'a> Directory {
fn to_syntax_directory(&'a self) -> syntax::parse::Directory<'a> {
syntax::parse::Directory {
path: Cow::Borrowed(&self.path),
ownership: self.ownership.clone(),
}
}
}
enum SubModKind {
/// `mod foo;`
External(PathBuf, DirectoryOwnership),
/// `#[path = "..."] mod foo {}`
InternalWithPath(PathBuf),
/// `mod foo {}`
Internal,
}
impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
/// Creates a new `ModResolver`.
pub(crate) fn new(
source_map: &'b source_map::SourceMap,
parse_sess: &'sess ParseSess,
directory_ownership: DirectoryOwnership,
recursive: bool,
) -> Self {
@ -40,14 +62,17 @@ impl<'a, 'b> ModResolver<'a, 'b> {
ownership: directory_ownership,
},
file_map: BTreeMap::new(),
source_map,
parse_sess,
recursive,
}
}
/// Creates a map that maps a file name to the module in AST.
pub(crate) fn visit_crate(mut self, krate: &'a ast::Crate) -> Result<FileModMap<'a>, String> {
let root_filename = self.source_map.span_to_filename(krate.span);
pub(crate) fn visit_crate(
mut self,
krate: &'ast ast::Crate,
) -> Result<FileModMap<'ast>, String> {
let root_filename = self.parse_sess.source_map().span_to_filename(krate.span);
self.directory.path = match root_filename {
source_map::FileName::Real(ref path) => path
.parent()
@ -58,54 +83,125 @@ impl<'a, 'b> ModResolver<'a, 'b> {
// Skip visiting sub modules when the input is from stdin.
if self.recursive {
self.visit_mod(&krate.module)?;
self.visit_mod_from_ast(&krate.module)?;
}
self.file_map
.insert(root_filename.into(), (&krate.module, String::new()));
self.file_map.insert(
root_filename.into(),
(Cow::Borrowed(&krate.module), String::new()),
);
Ok(self.file_map)
}
fn visit_mod(&mut self, module: &'a ast::Mod) -> Result<(), String> {
for item in &module.items {
if let ast::ItemKind::Mod(ref sub_mod) = item.node {
if contains_skip(&item.attrs) {
continue;
}
let old_direcotry = self.directory.clone();
if is_mod_decl(item) {
// mod foo;
// Look for an extern file.
let (mod_path, directory_ownership) =
self.find_external_module(item.ident, &item.attrs)?;
self.file_map.insert(
FileName::Real(mod_path.clone()),
(sub_mod, item.ident.as_str().get().to_owned()),
);
self.directory = Directory {
path: mod_path.parent().unwrap().to_path_buf(),
ownership: directory_ownership,
}
} else {
// An internal module (`mod foo { /* ... */ }`);
if let Some(path) = find_path_value(&item.attrs) {
// All `#[path]` files are treated as though they are a `mod.rs` file.
self.directory = Directory {
path: Path::new(&path.as_str()).to_path_buf(),
ownership: DirectoryOwnership::Owned { relative: None },
};
} else {
self.push_inline_mod_directory(item.ident, &item.attrs);
}
}
self.visit_mod(sub_mod)?;
self.directory = old_direcotry;
/// Visit macro calls and look for module declarations. Currently only supports `cfg_if` macro.
fn visit_mac(&mut self, item: Cow<'ast, ast::Item>) -> Result<(), String> {
let mut visitor =
visitor::CfgIfVisitor::new(self.parse_sess, self.directory.to_syntax_directory());
visitor.visit_item(&item);
for module_item in visitor.mods() {
if let ast::ItemKind::Mod(ref sub_mod) = module_item.item.node {
self.visit_mod_from_mac_inner(&item, Cow::Owned(sub_mod.clone()))?;
}
}
Ok(())
}
/// Visit modules defined inside macro calls.
fn visit_mod_from_macro(&mut self, module: Cow<'ast, ast::Mod>) -> Result<(), String> {
for item in &module.items {
if let ast::ItemKind::Mac(..) = item.node {
self.visit_mac(Cow::Owned(item.clone().into_inner()))?;
}
if let ast::ItemKind::Mod(ref sub_mod) = item.node {
self.visit_mod_from_mac_inner(item, Cow::Owned(sub_mod.clone()))?;
}
}
Ok(())
}
fn visit_mod_from_mac_inner(
&mut self,
item: &'c ast::Item,
sub_mod: Cow<'ast, ast::Mod>,
) -> Result<(), String> {
let old_directory = self.directory.clone();
self.visit_sub_mod(item, &sub_mod)?;
self.visit_mod_from_macro(sub_mod)?;
self.directory = old_directory;
Ok(())
}
/// Visit modules from AST.
fn visit_mod_from_ast(&mut self, module: &'ast ast::Mod) -> Result<(), String> {
for item in &module.items {
if let ast::ItemKind::Mac(..) = item.node {
self.visit_mac(Cow::Borrowed(item))?;
}
if let ast::ItemKind::Mod(ref sub_mod) = item.node {
let old_directory = self.directory.clone();
self.visit_sub_mod(item, &Cow::Borrowed(sub_mod))?;
self.visit_mod_from_ast(sub_mod)?;
self.directory = old_directory;
}
}
Ok(())
}
fn visit_sub_mod(
&mut self,
item: &'c ast::Item,
sub_mod: &Cow<'ast, ast::Mod>,
) -> Result<(), String> {
match self.peek_sub_mod(item)? {
Some(SubModKind::External(mod_path, directory_ownership)) => {
self.file_map.insert(
FileName::Real(mod_path.clone()),
(sub_mod.clone(), item.ident.name.as_str().get().to_owned()),
);
self.directory = Directory {
path: mod_path.parent().unwrap().to_path_buf(),
ownership: directory_ownership,
};
}
Some(SubModKind::InternalWithPath(mod_path)) => {
// All `#[path]` files are treated as though they are a `mod.rs` file.
self.directory = Directory {
path: mod_path,
ownership: DirectoryOwnership::Owned { relative: None },
};
}
Some(SubModKind::Internal) => self.push_inline_mod_directory(item.ident, &item.attrs),
None => (), // rustfmt::skip
}
Ok(())
}
/// Inspect the given sub-module which we are about to visit and returns its kind.
fn peek_sub_mod(&self, item: &'c ast::Item) -> Result<Option<SubModKind>, String> {
if contains_skip(&item.attrs) {
return Ok(None);
}
if is_mod_decl(item) {
// mod foo;
// Look for an extern file.
let (mod_path, directory_ownership) =
self.find_external_module(item.ident, &item.attrs)?;
Ok(Some(SubModKind::External(mod_path, directory_ownership)))
} else {
// An internal module (`mod foo { /* ... */ }`);
if let Some(path) = find_path_value(&item.attrs) {
let path = Path::new(&path.as_str()).to_path_buf();
Ok(Some(SubModKind::InternalWithPath(path)))
} else {
Ok(Some(SubModKind::Internal))
}
}
}
/// Find a file path in the filesystem which corresponds to the given module.
fn find_external_module(
&self,
mod_name: ast::Ident,
@ -123,7 +219,7 @@ impl<'a, 'b> ModResolver<'a, 'b> {
mod_name,
relative,
&self.directory.path,
self.source_map,
self.parse_sess.source_map(),
)
.result
{

105
src/modules/visitor.rs Normal file
View File

@ -0,0 +1,105 @@
use syntax::ast;
use syntax::parse::token::{DelimToken, Token};
use syntax::parse::{stream_to_parser_with_base_dir, Directory, ParseSess};
use syntax::symbol::kw;
use syntax::visit::Visitor;
use syntax_pos::Symbol;
pub(crate) struct ModItem {
pub(crate) item: ast::Item,
}
/// Traverse `cfg_if!` macro and fetch modules.
pub(crate) struct CfgIfVisitor<'a> {
parse_sess: &'a ParseSess,
mods: Vec<ModItem>,
base_dir: Directory<'a>,
}
impl<'a> CfgIfVisitor<'a> {
pub(crate) fn new(parse_sess: &'a ParseSess, base_dir: Directory<'a>) -> CfgIfVisitor<'a> {
CfgIfVisitor {
mods: vec![],
parse_sess,
base_dir,
}
}
pub(crate) fn mods(self) -> Vec<ModItem> {
self.mods
}
}
impl<'a, 'ast: 'a> Visitor<'ast> for CfgIfVisitor<'a> {
fn visit_mac(&mut self, mac: &'ast ast::Mac) {
match self.visit_mac_inner(mac) {
Ok(()) => (),
Err(e) => debug!("{}", e),
}
}
}
impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
fn visit_mac_inner(&mut self, mac: &'ast ast::Mac) -> Result<(), &'static str> {
if mac.node.path != Symbol::intern("cfg_if") {
return Err("Expected cfg_if");
}
let mut parser = stream_to_parser_with_base_dir(
self.parse_sess,
mac.node.tts.clone(),
self.base_dir.clone(),
);
parser.cfg_mods = false;
let mut process_if_cfg = true;
while parser.token != Token::Eof {
if process_if_cfg {
if !parser.eat_keyword(kw::If) {
return Err("Expected `if`");
}
parser
.parse_attribute(false)
.map_err(|_| "Failed to parse attributes")?;
}
if !parser.eat(&Token::OpenDelim(DelimToken::Brace)) {
return Err("Expected an opening brace");
}
while parser.token != Token::CloseDelim(DelimToken::Brace) && parser.token != Token::Eof
{
let item = match parser.parse_item() {
Ok(Some(item_ptr)) => item_ptr.into_inner(),
Ok(None) => continue,
Err(mut err) => {
err.cancel();
parser.sess.span_diagnostic.reset_err_count();
return Err(
"Expected item inside cfg_if block, but failed to parse it as an item",
);
}
};
if let ast::ItemKind::Mod(..) = item.node {
self.mods.push(ModItem { item });
}
}
if !parser.eat(&Token::CloseDelim(DelimToken::Brace)) {
return Err("Expected a closing brace");
}
if parser.eat(&Token::Eof) {
break;
}
if !parser.eat_keyword(kw::Else) {
return Err("Expected `else`");
}
process_if_cfg = parser.token.is_keyword(kw::If);
}
Ok(())
}
}

View File

@ -25,6 +25,9 @@ const SKIP_FILE_WHITE_LIST: &[&str] = &[
// so we do not want to test this file directly.
"configs/skip_children/foo/mod.rs",
"issue-3434/no_entry.rs",
// These files and directory are a part of modules defined inside `cfg_if!`.
"cfg_if/mod.rs",
"cfg_if/detect",
];
struct TestSetting {
@ -53,10 +56,19 @@ where
.expect("Failed to join a test thread")
}
fn is_subpath<P>(path: &Path, subpath: &P) -> bool
where
P: AsRef<Path>,
{
(0..path.components().count())
.map(|i| path.components().take(i))
.any(|c| c.zip(subpath.as_ref().components()).all(|(a, b)| a == b))
}
fn is_file_skip(path: &Path) -> bool {
SKIP_FILE_WHITE_LIST
.iter()
.any(|file_path| path.ends_with(file_path))
.any(|file_path| is_subpath(path, file_path))
}
// Returns a `Vec` containing `PathBuf`s of files with an `rs` extension in the

View File

@ -0,0 +1,106 @@
//! Aarch64 run-time features.
/// Checks if `aarch64` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_aarch64_feature_detected {
("neon") => {
// FIXME: this should be removed once we rename Aarch64 neon to asimd
cfg!(target_feature = "neon") ||
$crate::detect::check_for($crate::detect::Feature::asimd)
};
("asimd") => {
cfg!(target_feature = "neon") ||
$crate::detect::check_for($crate::detect::Feature::asimd)
};
("pmull") => {
cfg!(target_feature = "pmull") ||
$crate::detect::check_for($crate::detect::Feature::pmull)
};
("fp") => {
cfg!(target_feature = "fp") ||
$crate::detect::check_for($crate::detect::Feature::fp)
};
("fp16") => {
cfg!(target_feature = "fp16") ||
$crate::detect::check_for($crate::detect::Feature::fp16)
};
("sve") => {
cfg!(target_feature = "sve") ||
$crate::detect::check_for($crate::detect::Feature::sve)
};
("crc") => {
cfg!(target_feature = "crc") ||
$crate::detect::check_for($crate::detect::Feature::crc)
};
("crypto") => {
cfg!(target_feature = "crypto") ||
$crate::detect::check_for($crate::detect::Feature::crypto)
};
("lse") => {
cfg!(target_feature = "lse") ||
$crate::detect::check_for($crate::detect::Feature::lse)
};
("rdm") => {
cfg!(target_feature = "rdm") ||
$crate::detect::check_for($crate::detect::Feature::rdm)
};
("rcpc") => {
cfg!(target_feature = "rcpc") ||
$crate::detect::check_for($crate::detect::Feature::rcpc)
};
("dotprod") => {
cfg!(target_feature = "dotprod") ||
$crate::detect::check_for($crate::detect::Feature::dotprod)
};
("ras") => {
compile_error!("\"ras\" feature cannot be detected at run-time")
};
("v8.1a") => {
compile_error!("\"v8.1a\" feature cannot be detected at run-time")
};
("v8.2a") => {
compile_error!("\"v8.2a\" feature cannot be detected at run-time")
};
("v8.3a") => {
compile_error!("\"v8.3a\" feature cannot be detected at run-time")
};
($t:tt,) => {
is_aarch64_feature_detected!($t);
};
($t:tt) => { compile_error!(concat!("unknown aarch64 target feature: ", $t)) };
}
/// ARM Aarch64 CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// ARM Advanced SIMD (ASIMD)
asimd,
/// Polynomial Multiply
pmull,
/// Floating point support
fp,
/// Half-float support.
fp16,
/// Scalable Vector Extension (SVE)
sve,
/// CRC32 (Cyclic Redundancy Check)
crc,
/// Crypto: AES + PMULL + SHA1 + SHA2
crypto,
/// Atomics (Large System Extension)
lse,
/// Rounding Double Multiply (ASIMDRDM)
rdm,
/// Release consistent Processor consistent (RcPc)
rcpc,
/// Vector Dot-Product (ASIMDDP)
dotprod,
}

View File

@ -0,0 +1,39 @@
//! Run-time feature detection on ARM Aarch32.
/// Checks if `arm` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_arm_feature_detected {
("neon") => {
cfg!(target_feature = "neon") ||
$crate::detect::check_for($crate::detect::Feature::neon)
};
("pmull") => {
cfg!(target_feature = "pmull") ||
$crate::detect::check_for($crate::detect::Feature::pmull)
};
("v7") => { compile_error!("\"v7\" feature cannot be detected at run-time") };
("vfp2") => { compile_error!("\"vfp2\" feature cannot be detected at run-time") };
("vfp3") => { compile_error!("\"vfp3\" feature cannot be detected at run-time") };
("vfp4") => { compile_error!("\"vfp4\" feature cannot be detected at run-time") };
($t:tt,) => {
is_arm_feature_detected!($t);
};
($t:tt) => { compile_error!(concat!("unknown arm target feature: ", $t)) };
}
/// ARM CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// ARM Advanced SIMD (NEON) - Aarch32
neon,
/// Polynomial Multiply
pmull,
}

View File

@ -0,0 +1,29 @@
//! Run-time feature detection on MIPS.
/// Checks if `mips` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_mips_feature_detected {
("msa") => {
cfg!(target_feature = "msa") ||
$crate::detect::check_for($crate::detect::Feature::msa)
};
($t:tt,) => {
is_mips_feature_detected!($t);
};
($t:tt) => { compile_error!(concat!("unknown mips target feature: ", $t)) };
}
/// MIPS CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// MIPS SIMD Architecture (MSA)
msa,
}

View File

@ -0,0 +1,29 @@
//! Run-time feature detection on MIPS64.
/// Checks if `mips64` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_mips64_feature_detected {
("msa") => {
cfg!(target_feature = "msa") ||
$crate::detect::check_for($crate::detect::Feature::msa)
};
($t:tt,) => {
is_mips64_feature_detected!($t);
};
($t:tt) => { compile_error!(concat!("unknown mips64 target feature: ", $t)) };
}
/// MIPS64 CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// MIPS SIMD Architecture (MSA)
msa,
}

View File

@ -0,0 +1,42 @@
//! Run-time feature detection on PowerPC.
/// Checks if `powerpc` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_powerpc_feature_detected {
("altivec") => {
cfg!(target_feature = "altivec") ||
$crate::detect::check_for($crate::detect::Feature::altivec)
};
("vsx") => {
cfg!(target_feature = "vsx") ||
$crate::detect::check_for($crate::detect::Feature::vsx)
};
("power8") => {
cfg!(target_feature = "power8") ||
$crate::detect::check_for($crate::detect::Feature::power8)
};
($t:tt,) => {
is_powerpc_feature_detected!($t);
};
($t:tt) => { compile_error!(concat!("unknown powerpc target feature: ", $t)) };
}
/// PowerPC CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// Altivec
altivec,
/// VSX
vsx,
/// Power8
power8,
}

View File

@ -0,0 +1,42 @@
//! Run-time feature detection on PowerPC64.
/// Checks if `powerpc64` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_powerpc64_feature_detected {
("altivec") => {
cfg!(target_feature = "altivec") ||
$crate::detect::check_for($crate::detect::Feature::altivec)
};
("vsx") => {
cfg!(target_feature = "vsx") ||
$crate::detect::check_for($crate::detect::Feature::vsx)
};
("power8") => {
cfg!(target_feature = "power8") ||
$crate::detect::check_for($crate::detect::Feature::power8)
};
($t:tt,) => {
is_powerpc64_feature_detected!($t);
};
($t:tt) => { compile_error!(concat!("unknown powerpc64 target feature: ", $t)) };
}
/// PowerPC64 CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// Altivec
altivec,
/// VSX
vsx,
/// Power8
power8,
}

View File

@ -0,0 +1,348 @@
//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in an `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavaiable (bit is cleared).
//!
//! The enum `Feature` is used to map bit positions to feature names, and the
//! the `__crate::detect::check_for!` macro is used to map string literals (e.g.,
//! "avx") to these bit positions (e.g., `Feature::avx`).
//!
//! The run-time feature detection is performed by the
//! `__crate::detect::check_for(Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.
/// A macro to test at *runtime* whether a CPU feature is available on
/// x86/x86-64 platforms.
///
/// This macro is provided in the standard library and will detect at runtime
/// whether the specified CPU feature is detected. This does **not** resolve at
/// compile time unless the specified feature is already enabled for the entire
/// crate. Runtime detection currently relies mostly on the `cpuid` instruction.
///
/// This macro only takes one argument which is a string literal of the feature
/// being tested for. The feature names supported are the lowercase versions of
/// the ones defined by Intel in [their documentation][docs].
///
/// ## Supported arguments
///
/// This macro supports the same names that `#[target_feature]` supports. Unlike
/// `#[target_feature]`, however, this macro does not support names separated
/// with a comma. Instead testing for multiple features must be done through
/// separate macro invocations for now.
///
/// Supported arguments are:
///
/// * `"aes"`
/// * `"pclmulqdq"`
/// * `"rdrand"`
/// * `"rdseed"`
/// * `"tsc"`
/// * `"mmx"`
/// * `"sse"`
/// * `"sse2"`
/// * `"sse3"`
/// * `"ssse3"`
/// * `"sse4.1"`
/// * `"sse4.2"`
/// * `"sse4a"`
/// * `"sha"`
/// * `"avx"`
/// * `"avx2"`
/// * `"avx512f"`
/// * `"avx512cd"`
/// * `"avx512er"`
/// * `"avx512pf"`
/// * `"avx512bw"`
/// * `"avx512dq"`
/// * `"avx512vl"`
/// * `"avx512ifma"`
/// * `"avx512vbmi"`
/// * `"avx512vpopcntdq"`
/// * `"f16c"`
/// * `"fma"`
/// * `"bmi1"`
/// * `"bmi2"`
/// * `"abm"`
/// * `"lzcnt"`
/// * `"tbm"`
/// * `"popcnt"`
/// * `"fxsr"`
/// * `"xsave"`
/// * `"xsaveopt"`
/// * `"xsaves"`
/// * `"xsavec"`
/// * `"adx"`
/// * `"rtm"`
///
/// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide
#[macro_export]
#[stable(feature = "simd_x86", since = "1.27.0")]
#[allow_internal_unstable(stdsimd_internal,stdsimd)]
macro_rules! is_x86_feature_detected {
("aes") => {
cfg!(target_feature = "aes") || $crate::detect::check_for(
$crate::detect::Feature::aes) };
("pclmulqdq") => {
cfg!(target_feature = "pclmulqdq") || $crate::detect::check_for(
$crate::detect::Feature::pclmulqdq) };
("rdrand") => {
cfg!(target_feature = "rdrand") || $crate::detect::check_for(
$crate::detect::Feature::rdrand) };
("rdseed") => {
cfg!(target_feature = "rdseed") || $crate::detect::check_for(
$crate::detect::Feature::rdseed) };
("tsc") => {
cfg!(target_feature = "tsc") || $crate::detect::check_for(
$crate::detect::Feature::tsc) };
("mmx") => {
cfg!(target_feature = "mmx") || $crate::detect::check_for(
$crate::detect::Feature::mmx) };
("sse") => {
cfg!(target_feature = "sse") || $crate::detect::check_for(
$crate::detect::Feature::sse) };
("sse2") => {
cfg!(target_feature = "sse2") || $crate::detect::check_for(
$crate::detect::Feature::sse2)
};
("sse3") => {
cfg!(target_feature = "sse3") || $crate::detect::check_for(
$crate::detect::Feature::sse3)
};
("ssse3") => {
cfg!(target_feature = "ssse3") || $crate::detect::check_for(
$crate::detect::Feature::ssse3)
};
("sse4.1") => {
cfg!(target_feature = "sse4.1") || $crate::detect::check_for(
$crate::detect::Feature::sse4_1)
};
("sse4.2") => {
cfg!(target_feature = "sse4.2") || $crate::detect::check_for(
$crate::detect::Feature::sse4_2)
};
("sse4a") => {
cfg!(target_feature = "sse4a") || $crate::detect::check_for(
$crate::detect::Feature::sse4a)
};
("sha") => {
cfg!(target_feature = "sha") || $crate::detect::check_for(
$crate::detect::Feature::sha)
};
("avx") => {
cfg!(target_feature = "avx") || $crate::detect::check_for(
$crate::detect::Feature::avx)
};
("avx2") => {
cfg!(target_feature = "avx2") || $crate::detect::check_for(
$crate::detect::Feature::avx2)
};
("avx512f") => {
cfg!(target_feature = "avx512f") || $crate::detect::check_for(
$crate::detect::Feature::avx512f)
};
("avx512cd") => {
cfg!(target_feature = "avx512cd") || $crate::detect::check_for(
$crate::detect::Feature::avx512cd)
};
("avx512er") => {
cfg!(target_feature = "avx512er") || $crate::detect::check_for(
$crate::detect::Feature::avx512er)
};
("avx512pf") => {
cfg!(target_feature = "avx512pf") || $crate::detect::check_for(
$crate::detect::Feature::avx512pf)
};
("avx512bw") => {
cfg!(target_feature = "avx512bw") || $crate::detect::check_for(
$crate::detect::Feature::avx512bw)
};
("avx512dq") => {
cfg!(target_feature = "avx512dq") || $crate::detect::check_for(
$crate::detect::Feature::avx512dq)
};
("avx512vl") => {
cfg!(target_Feature = "avx512vl") || $crate::detect::check_for(
$crate::detect::Feature::avx512vl)
};
("avx512ifma") => {
cfg!(target_feature = "avx512ifma") || $crate::detect::check_for(
$crate::detect::Feature::avx512_ifma)
};
("avx512vbmi") => {
cfg!(target_feature = "avx512vbmi") || $crate::detect::check_for(
$crate::detect::Feature::avx512_vbmi)
};
("avx512vpopcntdq") => {
cfg!(target_feature = "avx512vpopcntdq") || $crate::detect::check_for(
$crate::detect::Feature::avx512_vpopcntdq)
};
("f16c") => {
cfg!(target_feature = "f16c") || $crate::detect::check_for(
$crate::detect::Feature::f16c)
};
("fma") => {
cfg!(target_feature = "fma") || $crate::detect::check_for(
$crate::detect::Feature::fma)
};
("bmi1") => {
cfg!(target_feature = "bmi1") || $crate::detect::check_for(
$crate::detect::Feature::bmi)
};
("bmi2") => {
cfg!(target_feature = "bmi2") || $crate::detect::check_for(
$crate::detect::Feature::bmi2)
};
("abm") => {
cfg!(target_feature = "abm") || $crate::detect::check_for(
$crate::detect::Feature::abm)
};
("lzcnt") => {
cfg!(target_feature = "lzcnt") || $crate::detect::check_for(
$crate::detect::Feature::abm)
};
("tbm") => {
cfg!(target_feature = "tbm") || $crate::detect::check_for(
$crate::detect::Feature::tbm)
};
("popcnt") => {
cfg!(target_feature = "popcnt") || $crate::detect::check_for(
$crate::detect::Feature::popcnt)
};
("fxsr") => {
cfg!(target_feature = "fxsr") || $crate::detect::check_for(
$crate::detect::Feature::fxsr)
};
("xsave") => {
cfg!(target_feature = "xsave") || $crate::detect::check_for(
$crate::detect::Feature::xsave)
};
("xsaveopt") => {
cfg!(target_feature = "xsaveopt") || $crate::detect::check_for(
$crate::detect::Feature::xsaveopt)
};
("xsaves") => {
cfg!(target_feature = "xsaves") || $crate::detect::check_for(
$crate::detect::Feature::xsaves)
};
("xsavec") => {
cfg!(target_feature = "xsavec") || $crate::detect::check_for(
$crate::detect::Feature::xsavec)
};
("cmpxchg16b") => {
cfg!(target_feature = "cmpxchg16b") || $crate::detect::check_for(
$crate::detect::Feature::cmpxchg16b)
};
("adx") => {
cfg!(target_feature = "adx") || $crate::detect::check_for(
$crate::detect::Feature::adx)
};
("rtm") => {
cfg!(target_feature = "rtm") || $crate::detect::check_for(
$crate::detect::Feature::rtm)
};
($t:tt,) => {
is_x86_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown target feature: ", $t))
};
}
/// X86 CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// This is an unstable implementation detail subject to change.
#[allow(non_camel_case_types)]
#[repr(u8)]
#[doc(hidden)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// AES (Advanced Encryption Standard New Instructions AES-NI)
aes,
/// CLMUL (Carry-less Multiplication)
pclmulqdq,
/// RDRAND
rdrand,
/// RDSEED
rdseed,
/// TSC (Time Stamp Counter)
tsc,
/// MMX
mmx,
/// SSE (Streaming SIMD Extensions)
sse,
/// SSE2 (Streaming SIMD Extensions 2)
sse2,
/// SSE3 (Streaming SIMD Extensions 3)
sse3,
/// SSSE3 (Supplemental Streaming SIMD Extensions 3)
ssse3,
/// SSE4.1 (Streaming SIMD Extensions 4.1)
sse4_1,
/// SSE4.2 (Streaming SIMD Extensions 4.2)
sse4_2,
/// SSE4a (Streaming SIMD Extensions 4a)
sse4a,
/// SHA
sha,
/// AVX (Advanced Vector Extensions)
avx,
/// AVX2 (Advanced Vector Extensions 2)
avx2,
/// AVX-512 F (Foundation)
avx512f,
/// AVX-512 CD (Conflict Detection Instructions)
avx512cd,
/// AVX-512 ER (Exponential and Reciprocal Instructions)
avx512er,
/// AVX-512 PF (Prefetch Instructions)
avx512pf,
/// AVX-512 BW (Byte and Word Instructions)
avx512bw,
/// AVX-512 DQ (Doubleword and Quadword)
avx512dq,
/// AVX-512 VL (Vector Length Extensions)
avx512vl,
/// AVX-512 IFMA (Integer Fused Multiply Add)
avx512_ifma,
/// AVX-512 VBMI (Vector Byte Manipulation Instructions)
avx512_vbmi,
/// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and
/// Quadword)
avx512_vpopcntdq,
/// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
f16c,
/// FMA (Fused Multiply Add)
fma,
/// BMI1 (Bit Manipulation Instructions 1)
bmi,
/// BMI1 (Bit Manipulation Instructions 2)
bmi2,
/// ABM (Advanced Bit Manipulation) on AMD / LZCNT (Leading Zero
/// Count) on Intel
abm,
/// TBM (Trailing Bit Manipulation)
tbm,
/// POPCNT (Population Count)
popcnt,
/// FXSR (Floating-point context fast save and restor)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,
/// XSAVEOPT (Save Processor Extended States Optimized)
xsaveopt,
/// XSAVES (Save Processor Extended States Supervisor)
xsaves,
/// XSAVEC (Save Processor Extended States Compacted)
xsavec,
/// CMPXCH16B, a 16-byte compare-and-swap instruction
cmpxchg16b,
/// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
adx,
/// RTM, Intel (Restricted Transactional Memory)
rtm,
}

View File

@ -0,0 +1,9 @@
//! Bit manipulation utilities.
/// Tests the `bit` of `x`.
#[allow(dead_code)]
#[inline]
pub(crate) fn test(x: usize, bit: u32) -> bool {
debug_assert!(bit < 32, "bit index out-of-bounds");
x & (1 << bit) != 0
}

View File

@ -0,0 +1,164 @@
//! Caches run-time feature detection so that it only needs to be computed
//! once.
#![allow(dead_code)] // not used on all platforms
use crate::sync::atomic::Ordering;
#[cfg(target_pointer_width = "64")]
use crate::sync::atomic::AtomicU64;
#[cfg(target_pointer_width = "32")]
use crate::sync::atomic::AtomicU32;
/// Sets the `bit` of `x`.
#[inline]
const fn set_bit(x: u64, bit: u32) -> u64 {
x | 1 << bit
}
/// Tests the `bit` of `x`.
#[inline]
const fn test_bit(x: u64, bit: u32) -> bool {
x & (1 << bit) != 0
}
/// Maximum number of features that can be cached.
const CACHE_CAPACITY: u32 = 63;
/// This type is used to initialize the cache
#[derive(Copy, Clone)]
pub(crate) struct Initializer(u64);
#[allow(clippy::use_self)]
impl Default for Initializer {
fn default() -> Self {
Initializer(0)
}
}
impl Initializer {
/// Tests the `bit` of the cache.
#[allow(dead_code)]
#[inline]
pub(crate) fn test(self, bit: u32) -> bool {
// FIXME: this way of making sure that the cache is large enough is
// brittle.
debug_assert!(
bit < CACHE_CAPACITY,
"too many features, time to increase the cache size!"
);
test_bit(self.0, bit)
}
/// Sets the `bit` of the cache.
#[inline]
pub(crate) fn set(&mut self, bit: u32) {
// FIXME: this way of making sure that the cache is large enough is
// brittle.
debug_assert!(
bit < CACHE_CAPACITY,
"too many features, time to increase the cache size!"
);
let v = self.0;
self.0 = set_bit(v, bit);
}
}
/// This global variable is a cache of the features supported by the CPU.
static CACHE: Cache = Cache::uninitialized();
/// Feature cache with capacity for `CACHE_CAPACITY` features.
///
/// Note: the last feature bit is used to represent an
/// uninitialized cache.
#[cfg(target_pointer_width = "64")]
struct Cache(AtomicU64);
#[cfg(target_pointer_width = "64")]
#[allow(clippy::use_self)]
impl Cache {
/// Creates an uninitialized cache.
#[allow(clippy::declare_interior_mutable_const)]
const fn uninitialized() -> Self {
Cache(AtomicU64::new(u64::max_value()))
}
/// Is the cache uninitialized?
#[inline]
pub(crate) fn is_uninitialized(&self) -> bool {
self.0.load(Ordering::Relaxed) == u64::max_value()
}
/// Is the `bit` in the cache set?
#[inline]
pub(crate) fn test(&self, bit: u32) -> bool {
test_bit(CACHE.0.load(Ordering::Relaxed), bit)
}
/// Initializes the cache.
#[inline]
pub(crate) fn initialize(&self, value: Initializer) {
self.0.store(value.0, Ordering::Relaxed);
}
}
/// Feature cache with capacity for `CACHE_CAPACITY` features.
///
/// Note: the last feature bit is used to represent an
/// uninitialized cache.
#[cfg(target_pointer_width = "32")]
struct Cache(AtomicU32, AtomicU32);
#[cfg(target_pointer_width = "32")]
impl Cache {
/// Creates an uninitialized cache.
const fn uninitialized() -> Self {
Cache(
AtomicU32::new(u32::max_value()),
AtomicU32::new(u32::max_value()),
)
}
/// Is the cache uninitialized?
#[inline]
pub(crate) fn is_uninitialized(&self) -> bool {
self.1.load(Ordering::Relaxed) == u32::max_value()
}
/// Is the `bit` in the cache set?
#[inline]
pub(crate) fn test(&self, bit: u32) -> bool {
if bit < 32 {
test_bit(CACHE.0.load(Ordering::Relaxed) as u64, bit)
} else {
test_bit(CACHE.1.load(Ordering::Relaxed) as u64, bit - 32)
}
}
/// Initializes the cache.
#[inline]
pub(crate) fn initialize(&self, value: Initializer) {
let lo: u32 = value.0 as u32;
let hi: u32 = (value.0 >> 32) as u32;
self.0.store(lo, Ordering::Relaxed);
self.1.store(hi, Ordering::Relaxed);
}
}
/// Tests the `bit` of the storage. If the storage has not been initialized,
/// initializes it with the result of `f()`.
///
/// On its first invocation, it detects the CPU features and caches them in the
/// `CACHE` global variable as an `AtomicU64`.
///
/// It uses the `Feature` variant to index into this variable as a bitset. If
/// the bit is set, the feature is enabled, and otherwise it is disabled.
#[inline]
pub(crate) fn test<F>(bit: u32, f: F) -> bool
where
F: FnOnce() -> Initializer,
{
if CACHE.is_uninitialized() {
CACHE.initialize(f());
}
CACHE.test(bit)
}

View File

@ -0,0 +1,150 @@
//! The `is_{target_arch}_feature_detected!` macro are only available on their
//! architecture. These macros provide a better error messages when the user
//! attempts to call them in a different architecture.
/// Prevents compilation if `is_x86_feature_detected` is used somewhere
/// else than `x86` and `x86_64` targets.
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
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(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_arm_feature_detected` is used somewhere else
/// than `ARM` targets.
#[cfg(not(target_arch = "arm"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_arm_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_arm_feature_detected can only be used on ARM targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "arm")] {
if is_arm_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_aarch64_feature_detected` is used somewhere else
/// than `aarch64` targets.
#[cfg(not(target_arch = "aarch64"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_aarch64_feature_detected {
($t: tt) => {
compile_error!(
r#"
is_aarch64_feature_detected can only be used on AArch64 targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "aarch64")] {
if is_aarch64_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_powerpc_feature_detected` is used somewhere else
/// than `PowerPC` targets.
#[cfg(not(target_arch = "powerpc"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_powerpc_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_powerpc_feature_detected can only be used on PowerPC targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "powerpc")] {
if is_powerpc_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_powerpc64_feature_detected` is used somewhere
/// else than `PowerPC64` targets.
#[cfg(not(target_arch = "powerpc64"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_powerpc64_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_powerpc64_feature_detected can only be used on PowerPC64 targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "powerpc64")] {
if is_powerpc64_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_mips_feature_detected` is used somewhere else
/// than `MIPS` targets.
#[cfg(not(target_arch = "mips"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_mips_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_mips_feature_detected can only be used on MIPS targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "mips")] {
if is_mips_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_mips64_feature_detected` is used somewhere else
/// than `MIPS64` targets.
#[cfg(not(target_arch = "mips64"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_mips64_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_mips64_feature_detected can only be used on MIPS64 targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "mips64")] {
if is_mips64_feature_detected(...) { ... }
}
"#
)
};
}

View File

@ -0,0 +1,85 @@
//! This module implements run-time feature detection.
//!
//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a
//! feature as a string-literal, and return a boolean indicating whether the
//! feature is enabled at run-time or not.
//!
//! These macros do two things:
//! * map the string-literal into an integer stored as a `Feature` enum,
//! * call a `os::check_for(x: Feature)` function that returns `true` if the
//! feature is enabled.
//!
//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs`
//! modules.
//!
//! The `check_for` functions are, in general, Operating System dependent. Most
//! architectures do not allow user-space programs to query the feature bits
//! due to security concerns (x86 is the big exception). These functions are
//! implemented in the `os/{target_os}.rs` modules.
#[macro_use]
mod error_macros;
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
#[path = "arch/x86.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "arm")] {
#[path = "arch/arm.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "aarch64")] {
#[path = "arch/aarch64.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "powerpc")] {
#[path = "arch/powerpc.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "powerpc64")] {
#[path = "arch/powerpc64.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "mips")] {
#[path = "arch/mips.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "mips64")] {
#[path = "arch/mips64.rs"]
#[macro_use]
mod arch;
} else {
// Unimplemented architecture:
mod arch {
pub enum Feature {
Null
}
}
}
}
pub use self::arch::Feature;
mod bit;
mod cache;
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
// On x86/x86_64 no OS specific functionality is required.
#[path = "os/x86.rs"]
mod os;
} else if #[cfg(all(target_os = "linux", feature = "use_std"))] {
#[path = "os/linux/mod.rs"]
mod os;
} else if #[cfg(target_os = "freebsd")] {
#[cfg(target_arch = "aarch64")]
#[path = "os/aarch64.rs"]
mod aarch64;
#[path = "os/freebsd/mod.rs"]
mod os;
} else {
#[path = "os/other.rs"]
mod os;
}
}
pub use self::os::check_for;

View File

@ -0,0 +1,79 @@
//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction.
//!
//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use
//! privileged system registers from userspace to check CPU feature support.
//!
//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1
//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc.
//! Each part of the register indicates the level of support for a certain feature, e.g.
//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported.
//!
//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb),
//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947).
//!
//! References:
//!
//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp)
//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt)
use crate::detect::{Feature, cache};
/// Try to read the features from the system registers.
///
/// This will cause SIGILL if the current OS is not trapping the mrs instruction.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
let aa64isar0: u64;
unsafe { asm!("mrs $0, ID_AA64ISAR0_EL1" : "=r"(aa64isar0)); }
let aes = bits_shift(aa64isar0, 7, 4) >= 1;
let pmull = bits_shift(aa64isar0, 7, 4) >= 2;
let sha1 = bits_shift(aa64isar0, 11, 8) >= 1;
let sha2 = bits_shift(aa64isar0, 15, 12) >= 1;
enable_feature(Feature::pmull, pmull);
// Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp
enable_feature(Feature::crypto, aes && pmull && sha1 && sha2);
enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1);
enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1);
// ID_AA64PFR0_EL1 - Processor Feature Register 0
let aa64pfr0: u64;
unsafe { asm!("mrs $0, ID_AA64PFR0_EL1" : "=r"(aa64pfr0)); }
let fp = bits_shift(aa64pfr0, 19, 16) < 0xF;
let fphp = bits_shift(aa64pfr0, 19, 16) >= 1;
let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF;
let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1;
enable_feature(Feature::fp, fp);
enable_feature(Feature::fp16, fphp);
// SIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp));
// SIMD extensions require SIMD support:
enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1);
enable_feature(Feature::dotprod, asimd && bits_shift(aa64isar0, 47, 44) >= 1);
enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1);
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
let aa64isar1: u64;
unsafe { asm!("mrs $0, ID_AA64ISAR1_EL1" : "=r"(aa64isar1)); }
enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1);
}
value
}
#[inline]
fn bits_shift(x: u64, high: usize, low: usize) -> u64 {
(x >> low) & ((1 << (high - low + 1)) - 1)
}

View File

@ -0,0 +1,28 @@
//! Run-time feature detection for Aarch64 on FreeBSD.
use crate::detect::{Feature, cache};
use super::super::aarch64::detect_features;
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
#[cfg(test)]
mod tests {
#[test]
fn dump() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
println!("sve: {:?}", is_aarch64_feature_detected!("sve"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("crypto: {:?}", is_aarch64_feature_detected!("crypto"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
}
}

View File

@ -0,0 +1,27 @@
//! Run-time feature detection for ARM on FreeBSD
use crate::detect::{Feature, cache};
use super::{auxvec};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::neon, auxv.hwcap & 0x00001000 != 0);
enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & 0x00000002 != 0);
return value;
}
value
}

View File

@ -0,0 +1,86 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(any(target_arch = "arm", target_arch = "powerpc64"), allow(dead_code))]
/// Key to access the CPU Hardware capabilities bitfield.
pub(crate) const AT_HWCAP: usize = 25;
/// Key to access the CPU Hardware capabilities 2 bitfield.
pub(crate) const AT_HWCAP2: usize = 26;
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For FreeBSD, they are
/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time.
///
/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707
pub(crate) fn auxv() -> Result<AuxVec, ()> {
if let Ok(hwcap) = archauxv(AT_HWCAP) {
if let Ok(hwcap2) = archauxv(AT_HWCAP2) {
if hwcap != 0 && hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
Err(())
}
/// Tries to read the `key` from the auxiliary vector.
fn archauxv(key: usize) -> Result<usize, ()> {
use crate::mem;
#[derive (Copy, Clone)]
#[repr(C)]
pub struct Elf_Auxinfo {
pub a_type: usize,
pub a_un: unnamed,
}
#[derive (Copy, Clone)]
#[repr(C)]
pub union unnamed {
pub a_val: libc::c_long,
pub a_ptr: *mut libc::c_void,
pub a_fcn: Option<unsafe extern "C" fn() -> ()>,
}
let mut auxv: [Elf_Auxinfo; 27] =
[Elf_Auxinfo{a_type: 0, a_un: unnamed{a_val: 0,},}; 27];
let mut len: libc::c_uint = mem::size_of_val(&auxv) as libc::c_uint;
unsafe {
let mut mib = [libc::CTL_KERN, libc::KERN_PROC, libc::KERN_PROC_AUXV, libc::getpid()];
let ret = libc::sysctl(mib.as_mut_ptr(),
mib.len() as u32,
&mut auxv as *mut _ as *mut _,
&mut len as *mut _ as *mut _,
0 as *mut libc::c_void,
0,
);
if ret != -1 {
for i in 0..auxv.len() {
if auxv[i].a_type == key {
return Ok(auxv[i].a_un.a_val as usize);
}
}
}
}
return Ok(0);
}

View File

@ -0,0 +1,22 @@
//! Run-time feature detection on FreeBSD
mod auxvec;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
mod aarch64;
pub use self::aarch64::check_for;
} else if #[cfg(target_arch = "arm")] {
mod arm;
pub use self::arm::check_for;
} else if #[cfg(target_arch = "powerpc64")] {
mod powerpc;
pub use self::powerpc::check_for;
} else {
use crate::arch::detect::Feature;
/// Performs run-time feature detection.
pub fn check_for(_x: Feature) -> bool {
false
}
}
}

View File

@ -0,0 +1,27 @@
//! Run-time feature detection for PowerPC on FreeBSD.
use crate::detect::{Feature, cache};
use super::{auxvec};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
return value;
}
value
}

View File

@ -0,0 +1,157 @@
//! Run-time feature detection for Aarch64 on Linux.
use crate::detect::{Feature, cache, bit};
use super::{auxvec, cpuinfo};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from /proc/cpuinfo.
fn detect_features() -> cache::Initializer {
if let Ok(auxv) = auxvec::auxv() {
let hwcap: AtHwcap = auxv.into();
return hwcap.cache();
}
if let Ok(c) = cpuinfo::CpuInfo::new() {
let hwcap: AtHwcap = c.into();
return hwcap.cache();
}
cache::Initializer::default()
}
/// These values are part of the platform-specific [asm/hwcap.h][hwcap] .
///
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
struct AtHwcap {
fp: bool, // 0
asimd: bool, // 1
// evtstrm: bool, // 2
aes: bool, // 3
pmull: bool, // 4
sha1: bool, // 5
sha2: bool, // 6
crc32: bool, // 7
atomics: bool, // 8
fphp: bool, // 9
asimdhp: bool, // 10
// cpuid: bool, // 11
asimdrdm: bool, // 12
// jscvt: bool, // 13
// fcma: bool, // 14
lrcpc: bool, // 15
// dcpop: bool, // 16
// sha3: bool, // 17
// sm3: bool, // 18
// sm4: bool, // 19
asimddp: bool, // 20
// sha512: bool, // 21
sve: bool, // 22
}
impl From<auxvec::AuxVec> for AtHwcap {
/// Reads AtHwcap from the auxiliary vector.
fn from(auxv: auxvec::AuxVec) -> Self {
AtHwcap {
fp: bit::test(auxv.hwcap, 0),
asimd: bit::test(auxv.hwcap, 1),
// evtstrm: bit::test(auxv.hwcap, 2),
aes: bit::test(auxv.hwcap, 3),
pmull: bit::test(auxv.hwcap, 4),
sha1: bit::test(auxv.hwcap, 5),
sha2: bit::test(auxv.hwcap, 6),
crc32: bit::test(auxv.hwcap, 7),
atomics: bit::test(auxv.hwcap, 8),
fphp: bit::test(auxv.hwcap, 9),
asimdhp: bit::test(auxv.hwcap, 10),
// cpuid: bit::test(auxv.hwcap, 11),
asimdrdm: bit::test(auxv.hwcap, 12),
// jscvt: bit::test(auxv.hwcap, 13),
// fcma: bit::test(auxv.hwcap, 14),
lrcpc: bit::test(auxv.hwcap, 15),
// dcpop: bit::test(auxv.hwcap, 16),
// sha3: bit::test(auxv.hwcap, 17),
// sm3: bit::test(auxv.hwcap, 18),
// sm4: bit::test(auxv.hwcap, 19),
asimddp: bit::test(auxv.hwcap, 20),
// sha512: bit::test(auxv.hwcap, 21),
sve: bit::test(auxv.hwcap, 22),
}
}
}
impl From<cpuinfo::CpuInfo> for AtHwcap {
/// Reads AtHwcap from /proc/cpuinfo .
fn from(c: cpuinfo::CpuInfo) -> Self {
let f = &c.field("Features");
AtHwcap {
// 64-bit names. FIXME: In 32-bit compatibility mode /proc/cpuinfo will
// map some of the 64-bit names to some 32-bit feature names. This does not
// cover that yet.
fp: f.has("fp"),
asimd: f.has("asimd"),
// evtstrm: f.has("evtstrm"),
aes: f.has("aes"),
pmull: f.has("pmull"),
sha1: f.has("sha1"),
sha2: f.has("sha2"),
crc32: f.has("crc32"),
atomics: f.has("atomics"),
fphp: f.has("fphp"),
asimdhp: f.has("asimdhp"),
// cpuid: f.has("cpuid"),
asimdrdm: f.has("asimdrdm"),
// jscvt: f.has("jscvt"),
// fcma: f.has("fcma"),
lrcpc: f.has("lrcpc"),
// dcpop: f.has("dcpop"),
// sha3: f.has("sha3"),
// sm3: f.has("sm3"),
// sm4: f.has("sm4"),
asimddp: f.has("asimddp"),
// sha512: f.has("sha512"),
sve: f.has("sve"),
}
}
}
impl AtHwcap {
/// Initializes the cache from the feature -bits.
///
/// The features are enabled approximately like in LLVM host feature detection:
/// https://github.com/llvm-mirror/llvm/blob/master/lib/Support/Host.cpp#L1273
fn cache(self) -> cache::Initializer {
let mut value = cache::Initializer::default();
{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
enable_feature(Feature::fp, self.fp);
// Half-float support requires float support
enable_feature(Feature::fp16, self.fp && self.fphp);
enable_feature(Feature::pmull, self.pmull);
enable_feature(Feature::crc, self.crc32);
enable_feature(Feature::lse, self.atomics);
enable_feature(Feature::rcpc, self.lrcpc);
// SIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
enable_feature(Feature::asimd, asimd);
// SIMD extensions require SIMD support:
enable_feature(Feature::rdm, self.asimdrdm && asimd);
enable_feature(Feature::dotprod, self.asimddp && asimd);
enable_feature(Feature::sve, self.sve && asimd);
// Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp
enable_feature(Feature::crypto, self.aes && self.pmull && self.sha1 && self.sha2);
}
value
}
}

View File

@ -0,0 +1,49 @@
//! Run-time feature detection for ARM on Linux.
use crate::detect::{Feature, cache, bit};
use super::{auxvec, cpuinfo};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from /proc/cpuinfo.
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12));
enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1));
return value;
}
if let Ok(c) = cpuinfo::CpuInfo::new() {
enable_feature(&mut value, Feature::neon, c.field("Features").has("neon") &&
!has_broken_neon(&c));
enable_feature(&mut value, Feature::pmull, c.field("Features").has("pmull"));
return value;
}
value
}
/// Is the CPU known to have a broken NEON unit?
///
/// See https://crbug.com/341598.
fn has_broken_neon(cpuinfo: &cpuinfo::CpuInfo) -> bool {
cpuinfo.field("CPU implementer") == "0x51"
&& cpuinfo.field("CPU architecture") == "7"
&& cpuinfo.field("CPU variant") == "0x1"
&& cpuinfo.field("CPU part") == "0x04d"
&& cpuinfo.field("CPU revision") == "0"
}

View File

@ -0,0 +1,307 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
#[cfg(feature = "std_detect_file_io")]
use crate::{fs::File, io::Read};
/// Key to access the CPU Hardware capabilities bitfield.
pub(crate) const AT_HWCAP: usize = 16;
/// Key to access the CPU Hardware capabilities 2 bitfield.
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
pub(crate) const AT_HWCAP2: usize = 26;
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For Linux, they are
/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// There is no perfect way of reading the auxiliary vector.
///
/// - If the `std_detect_dlsym_getauxval` cargo feature is enabled, this will use
/// `getauxval` if its linked to the binary, and otherwise proceed to a fallback implementation.
/// When `std_detect_dlsym_getauxval` is disabled, this will assume that `getauxval` is
/// linked to the binary - if that is not the case the behavior is undefined.
/// - Otherwise, if the `std_detect_file_io` cargo feature is enabled, it will
/// try to read `/proc/self/auxv`.
/// - If that fails, this function returns an error.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time. Also note that if this function returns an
/// error, cpuinfo still can (and will) be used to try to perform run-time
/// feature detecton on some platforms.
///
/// For more information about when `getauxval` is available check the great
/// [`auxv` crate documentation][auxv_docs].
///
/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h
/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/
pub(crate) fn auxv() -> Result<AuxVec, ()> {
#[cfg(feature = "std_detect_dlsym_getauxval")] {
// Try to call a dynamically-linked getauxval function.
if let Ok(hwcap) = getauxval(AT_HWCAP) {
// Targets with only AT_HWCAP:
#[cfg(any(target_arch = "aarch64", target_arch = "mips",
target_arch = "mips64"))]
{
if hwcap != 0 {
return Ok(AuxVec { hwcap });
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
if hwcap != 0 && hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
drop(hwcap);
}
#[cfg(feature = "std_detect_file_io")] {
// If calling getauxval fails, try to read the auxiliary vector from
// its file:
auxv_from_file("/proc/self/auxv")
}
#[cfg(not(feature = "std_detect_file_io"))] {
Err(())
}
}
#[cfg(not(feature = "std_detect_dlsym_getauxval"))] {
let hwcap = unsafe { ffi_getauxval(AT_HWCAP) };
// Targets with only AT_HWCAP:
#[cfg(any(target_arch = "aarch64", target_arch = "mips",
target_arch = "mips64"))]
{
if hwcap != 0 {
return Ok(AuxVec { hwcap });
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
let hwcap2 = unsafe { ffi_getauxval(AT_HWCAP2) };
if hwcap != 0 && hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
}
/// Tries to read the `key` from the auxiliary vector by calling the
/// dynamically-linked `getauxval` function. If the function is not linked,
/// this function return `Err`.
#[cfg(feature = "std_detect_dlsym_getauxval")]
fn getauxval(key: usize) -> Result<usize, ()> {
use libc;
pub type F = unsafe extern "C" fn(usize) -> usize;
unsafe {
let ptr = libc::dlsym(
libc::RTLD_DEFAULT,
"getauxval\0".as_ptr() as *const _,
);
if ptr.is_null() {
return Err(());
}
let ffi_getauxval: F = mem::transmute(ptr);
Ok(ffi_getauxval(key))
}
}
/// Tries to read the auxiliary vector from the `file`. If this fails, this
/// function returns `Err`.
#[cfg(feature = "std_detect_file_io")]
fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
let mut file = File::open(file).map_err(|_| ())?;
// See <https://github.com/torvalds/linux/blob/v3.19/include/uapi/linux/auxvec.h>.
//
// The auxiliary vector contains at most 32 (key,value) fields: from
// `AT_EXECFN = 31` to `AT_NULL = 0`. That is, a buffer of
// 2*32 `usize` elements is enough to read the whole vector.
let mut buf = [0_usize; 64];
{
let raw: &mut [u8; 64 * mem::size_of::<usize>()] =
unsafe { mem::transmute(&mut buf) };
file.read(raw).map_err(|_| ())?;
}
auxv_from_buf(&buf)
}
/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this
/// function returns `Err`.
#[cfg(feature = "std_detect_file_io")]
fn auxv_from_buf(buf: &[usize; 64]) -> Result<AuxVec, ()> {
// Targets with only AT_HWCAP:
#[cfg(any(target_arch = "aarch64", target_arch = "mips",
target_arch = "mips64"))]
{
for el in buf.chunks(2) {
match el[0] {
AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }),
_ => (),
}
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
let mut hwcap = None;
let mut hwcap2 = None;
for el in buf.chunks(2) {
match el[0] {
AT_HWCAP => hwcap = Some(el[1]),
AT_HWCAP2 => hwcap2 = Some(el[1]),
_ => (),
}
}
if let (Some(hwcap), Some(hwcap2)) = (hwcap, hwcap2) {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
drop(buf);
Err(())
}
#[cfg(test)]
mod tests {
extern crate auxv as auxv_crate;
use super::*;
// Reads the Auxiliary Vector key from /proc/self/auxv
// using the auxv crate.
#[cfg(feature = "std_detect_file_io")]
fn auxv_crate_getprocfs(key: usize) -> Option<usize> {
use self::auxv_crate::AuxvType;
use self::auxv_crate::procfs::search_procfs_auxv;
let k = key as AuxvType;
match search_procfs_auxv(&[k]) {
Ok(v) => Some(v[&k] as usize),
Err(_) => None,
}
}
// Reads the Auxiliary Vector key from getauxval()
// using the auxv crate.
#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
fn auxv_crate_getauxval(key: usize) -> Option<usize> {
use self::auxv_crate::AuxvType;
use self::auxv_crate::getauxval::Getauxval;
let q = auxv_crate::getauxval::NativeGetauxval {};
match q.getauxval(key as AuxvType) {
Ok(v) => Some(v as usize),
Err(_) => None,
}
}
// FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv
// does not always contain the AT_HWCAP key under qemu.
#[cfg(not(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc")))]
#[test]
fn auxv_crate() {
let v = auxv();
if let Some(hwcap) = auxv_crate_getauxval(AT_HWCAP) {
let rt_hwcap = v.expect("failed to find hwcap key").hwcap;
assert_eq!(rt_hwcap, hwcap);
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
if let Some(hwcap2) = auxv_crate_getauxval(AT_HWCAP2) {
let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2;
assert_eq!(rt_hwcap2, hwcap2);
}
}
}
#[test]
fn auxv_dump() {
if let Ok(auxvec) = auxv() {
println!("{:?}", auxvec);
} else {
println!("both getauxval() and reading /proc/self/auxv failed!");
}
}
#[cfg(feature = "std_detect_file_io")]
cfg_if! {
if #[cfg(target_arch = "arm")] {
#[test]
fn linux_rpi3() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv");
println!("file: {}", file);
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 4174038);
assert_eq!(v.hwcap2, 16);
}
#[test]
#[should_panic]
fn linux_macos_vb() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv");
println!("file: {}", file);
let v = auxv_from_file(file).unwrap();
// this file is incomplete (contains hwcap but not hwcap2), we
// want to fall back to /proc/cpuinfo in this case, so
// reading should fail. assert_eq!(v.hwcap, 126614527);
// assert_eq!(v.hwcap2, 0);
}
} else if #[cfg(target_arch = "aarch64")] {
#[test]
fn linux_x64() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-x64-i7-6850k.auxv");
println!("file: {}", file);
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 3219913727);
}
}
}
#[test]
#[cfg(feature = "std_detect_file_io")]
fn auxv_dump_procfs() {
if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") {
println!("{:?}", auxvec);
} else {
println!("reading /proc/self/auxv failed!");
}
}
#[test]
fn auxv_crate_procfs() {
let v = auxv();
if let Some(hwcap) = auxv_crate_getprocfs(AT_HWCAP) {
assert_eq!(v.unwrap().hwcap, hwcap);
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
if let Some(hwcap2) = auxv_crate_getprocfs(AT_HWCAP2) {
assert_eq!(v.unwrap().hwcap2, hwcap2);
}
}
}
}

View File

@ -0,0 +1,301 @@
//! Parses /proc/cpuinfo
#![cfg_attr(not(target_arch = "arm"), allow(dead_code))]
extern crate std;
use self::std::{prelude::v1::*, fs::File, io, io::Read};
/// cpuinfo
pub(crate) struct CpuInfo {
raw: String,
}
impl CpuInfo {
/// Reads /proc/cpuinfo into CpuInfo.
pub(crate) fn new() -> Result<Self, io::Error> {
let mut file = File::open("/proc/cpuinfo")?;
let mut cpui = Self { raw: String::new() };
file.read_to_string(&mut cpui.raw)?;
Ok(cpui)
}
/// Returns the value of the cpuinfo `field`.
pub(crate) fn field(&self, field: &str) -> CpuInfoField {
for l in self.raw.lines() {
if l.trim().starts_with(field) {
return CpuInfoField::new(l.split(": ").nth(1));
}
}
CpuInfoField(None)
}
/// Returns the `raw` contents of `/proc/cpuinfo`
#[cfg(test)]
fn raw(&self) -> &String {
&self.raw
}
#[cfg(test)]
fn from_str(other: &str) -> Result<Self, ::std::io::Error> {
Ok(Self {
raw: String::from(other),
})
}
}
/// Field of cpuinfo
#[derive(Debug)]
pub(crate) struct CpuInfoField<'a>(Option<&'a str>);
impl<'a> PartialEq<&'a str> for CpuInfoField<'a> {
fn eq(&self, other: &&'a str) -> bool {
match self.0 {
None => other.is_empty(),
Some(f) => f == other.trim(),
}
}
}
impl<'a> CpuInfoField<'a> {
pub(crate) fn new<'b>(v: Option<&'b str>) -> CpuInfoField<'b> {
match v {
None => CpuInfoField::<'b>(None),
Some(f) => CpuInfoField::<'b>(Some(f.trim())),
}
}
/// Does the field exist?
#[cfg(test)]
pub(crate) fn exists(&self) -> bool {
self.0.is_some()
}
/// Does the field contain `other`?
pub(crate) fn has(&self, other: &str) -> bool {
match self.0 {
None => other.is_empty(),
Some(f) => {
let other = other.trim();
for v in f.split(' ') {
if v == other {
return true;
}
}
false
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_dump() {
let cpuinfo = CpuInfo::new().unwrap();
if cpuinfo.field("vendor_id") == "GenuineIntel" {
assert!(cpuinfo.field("flags").exists());
assert!(!cpuinfo.field("vendor33_id").exists());
assert!(cpuinfo.field("flags").has("sse"));
assert!(!cpuinfo.field("flags").has("avx314"));
}
println!("{}", cpuinfo.raw());
}
const CORE_DUO_T6500: &str = r"processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz
stepping : 10
microcode : 0xa0b
cpu MHz : 1600.000
cache size : 2048 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm
bogomips : 4190.43
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
";
#[test]
fn core_duo_t6500() {
let cpuinfo = CpuInfo::from_str(CORE_DUO_T6500).unwrap();
assert_eq!(cpuinfo.field("vendor_id"), "GenuineIntel");
assert_eq!(cpuinfo.field("cpu family"), "6");
assert_eq!(cpuinfo.field("model"), "23");
assert_eq!(
cpuinfo.field("model name"),
"Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz"
);
assert_eq!(
cpuinfo.field("flags"),
"fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm"
);
assert!(cpuinfo.field("flags").has("fpu"));
assert!(cpuinfo.field("flags").has("dtherm"));
assert!(cpuinfo.field("flags").has("sse2"));
assert!(!cpuinfo.field("flags").has("avx"));
}
const ARM_CORTEX_A53: &str =
r"Processor : AArch64 Processor rev 3 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: AArch64
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 3
Hardware : HiKey Development Board
";
#[test]
fn arm_cortex_a53() {
let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A53).unwrap();
assert_eq!(
cpuinfo.field("Processor"),
"AArch64 Processor rev 3 (aarch64)"
);
assert_eq!(
cpuinfo.field("Features"),
"fp asimd evtstrm aes pmull sha1 sha2 crc32"
);
assert!(cpuinfo.field("Features").has("pmull"));
assert!(!cpuinfo.field("Features").has("neon"));
assert!(cpuinfo.field("Features").has("asimd"));
}
const ARM_CORTEX_A57: &str = r"Processor : Cortex A57 Processor rev 1 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
Features : fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x1
CPU part : 0xd07
CPU revision : 1";
#[test]
fn arm_cortex_a57() {
let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A57).unwrap();
assert_eq!(
cpuinfo.field("Processor"),
"Cortex A57 Processor rev 1 (aarch64)"
);
assert_eq!(
cpuinfo.field("Features"),
"fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt"
);
assert!(cpuinfo.field("Features").has("pmull"));
assert!(cpuinfo.field("Features").has("neon"));
assert!(cpuinfo.field("Features").has("asimd"));
}
const POWER8E_POWERKVM: &str = r"processor : 0
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
processor : 1
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
processor : 2
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
processor : 3
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
timebase : 512000000
platform : pSeries
model : IBM pSeries (emulated by qemu)
machine : CHRP IBM pSeries (emulated by qemu)";
#[test]
fn power8_powerkvm() {
let cpuinfo = CpuInfo::from_str(POWER8E_POWERKVM).unwrap();
assert_eq!(cpuinfo.field("cpu"), "POWER8E (raw), altivec supported");
assert!(cpuinfo.field("cpu").has("altivec"));
}
const POWER5P: &str = r"processor : 0
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 1
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 2
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 3
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 4
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 5
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 6
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 7
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
timebase : 237331000
platform : pSeries
machine : CHRP IBM,9133-55A";
#[test]
fn power5p() {
let cpuinfo = CpuInfo::from_str(POWER5P).unwrap();
assert_eq!(cpuinfo.field("cpu"), "POWER5+ (gs)");
assert!(!cpuinfo.field("cpu").has("altivec"));
}
}

View File

@ -0,0 +1,31 @@
//! Run-time feature detection for MIPS on Linux.
use crate::detect::{Feature, cache, bit};
use super::auxvec;
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from `/proc/cpuinfo`.
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1));
return value;
}
// TODO: fall back via `cpuinfo`.
value
}

View File

@ -0,0 +1,28 @@
//! Run-time feature detection on Linux
mod auxvec;
#[cfg(feature = "std_detect_file_io")]
mod cpuinfo;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
mod aarch64;
pub use self::aarch64::check_for;
} else if #[cfg(target_arch = "arm")] {
mod arm;
pub use self::arm::check_for;
} else if #[cfg(any(target_arch = "mips", target_arch = "mips64"))] {
mod mips;
pub use self::mips::check_for;
} else if #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] {
mod powerpc;
pub use self::powerpc::check_for;
} else {
use crate::detect::Feature;
/// Performs run-time feature detection.
pub fn check_for(_x: Feature) -> bool {
false
}
}
}

View File

@ -0,0 +1,41 @@
//! Run-time feature detection for PowerPC on Linux.
use crate::detect::{Feature, cache};
use super::{auxvec, cpuinfo};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from /proc/cpuinfo.
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/cputable.h][cputable]
//
// [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h
if let Ok(auxv) = auxvec::auxv() {
// note: the PowerPC values are the mask to do the test (instead of the
// index of the bit to test like in ARM and Aarch64)
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
return value;
}
// PowerPC's /proc/cpuinfo lacks a proper Feature field,
// but `altivec` support is indicated in the `cpu` field.
if let Ok(c) = cpuinfo::CpuInfo::new() {
enable_feature(&mut value, Feature::altivec, c.field("cpu").has("altivec"));
return value;
}
value
}

View File

@ -0,0 +1,9 @@
//! Other operating systems
use crate::detect::Feature;
/// Performs run-time feature detection.
#[inline]
pub fn check_for(_x: Feature) -> bool {
false
}

View File

@ -0,0 +1,375 @@
//! x86 run-time feature detection is OS independent.
#[cfg(target_arch = "x86")]
use crate::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::*;
use crate::mem;
use crate::detect::{Feature, cache, bit};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Run-time feature detection on x86 works by using the CPUID instruction.
///
/// The [CPUID Wikipedia page][wiki_cpuid] contains
/// all the information about which flags to set to query which values, and in
/// which registers these are reported.
///
/// The definitive references are:
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
/// Instruction Set Reference, A-Z][intel64_ref].
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
/// System Instructions][amd64_ref].
///
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
#[allow(clippy::similar_names)]
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
// If the x86 CPU does not support the CPUID instruction then it is too
// old to support any of the currently-detectable features.
if !has_cpuid() {
return value;
}
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
// has `cpuid` support.
// 0. EAX = 0: Basic Information:
// - EAX returns the "Highest Function Parameter", that is, the maximum
// leaf value for subsequent calls of `cpuinfo` in range [0,
// 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars,
// returned in EBX, EDX, and ECX (in that order):
let (max_basic_leaf, vendor_id) = unsafe {
let CpuidResult {
eax: max_basic_leaf,
ebx,
ecx,
edx,
} = __cpuid(0);
let vendor_id: [[u8; 4]; 3] = [
mem::transmute(ebx),
mem::transmute(edx),
mem::transmute(ecx),
];
let vendor_id: [u8; 12] = mem::transmute(vendor_id);
(max_basic_leaf, vendor_id)
};
if max_basic_leaf < 1 {
// Earlier Intel 486, CPUID not implemented
return value;
}
// EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits";
// Contains information about most x86 features.
let CpuidResult {
ecx: proc_info_ecx,
edx: proc_info_edx,
..
} = unsafe { __cpuid(0x0000_0001_u32) };
// EAX = 7, ECX = 0: Queries "Extended Features";
// Contains information about bmi,bmi2, and avx2 support.
let (extended_features_ebx, extended_features_ecx) = if max_basic_leaf >= 7
{
let CpuidResult { ebx, ecx, .. } = unsafe { __cpuid(0x0000_0007_u32) };
(ebx, ecx)
} else {
(0, 0) // CPUID does not support "Extended Features"
};
// EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported
// - EAX returns the max leaf value for extended information, that is,
// `cpuid` calls in range [0x8000_0000; u32::MAX]:
let CpuidResult {
eax: extended_max_basic_leaf,
..
} = unsafe { __cpuid(0x8000_0000_u32) };
// EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature
// Bits"
let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 {
let CpuidResult { ecx, .. } = unsafe { __cpuid(0x8000_0001_u32) };
ecx
} else {
0
};
{
// borrows value till the end of this scope:
let mut enable = |r, rb, f| {
if bit::test(r as usize, rb) {
value.set(f as u32);
}
};
enable(proc_info_ecx, 0, Feature::sse3);
enable(proc_info_ecx, 1, Feature::pclmulqdq);
enable(proc_info_ecx, 9, Feature::ssse3);
enable(proc_info_ecx, 13, Feature::cmpxchg16b);
enable(proc_info_ecx, 19, Feature::sse4_1);
enable(proc_info_ecx, 20, Feature::sse4_2);
enable(proc_info_ecx, 23, Feature::popcnt);
enable(proc_info_ecx, 25, Feature::aes);
enable(proc_info_ecx, 29, Feature::f16c);
enable(proc_info_ecx, 30, Feature::rdrand);
enable(extended_features_ebx, 18, Feature::rdseed);
enable(extended_features_ebx, 19, Feature::adx);
enable(extended_features_ebx, 11, Feature::rtm);
enable(proc_info_edx, 4, Feature::tsc);
enable(proc_info_edx, 23, Feature::mmx);
enable(proc_info_edx, 24, Feature::fxsr);
enable(proc_info_edx, 25, Feature::sse);
enable(proc_info_edx, 26, Feature::sse2);
enable(extended_features_ebx, 29, Feature::sha);
enable(extended_features_ebx, 3, Feature::bmi);
enable(extended_features_ebx, 8, Feature::bmi2);
// `XSAVE` and `AVX` support:
let cpu_xsave = bit::test(proc_info_ecx as usize, 26);
if cpu_xsave {
// 0. Here the CPU supports `XSAVE`.
// 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
// supports saving the state of the AVX/AVX2 vector registers on
// context-switches, see:
//
// - [intel: is avx enabled?][is_avx_enabled],
// - [mozilla: sse.cpp][mozilla_sse_cpp].
//
// [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
// [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190
let cpu_osxsave = bit::test(proc_info_ecx as usize, 27);
if cpu_osxsave {
// 2. The OS must have signaled the CPU that it supports saving and
// restoring the:
//
// * SSE -> `XCR0.SSE[1]`
// * AVX -> `XCR0.AVX[2]`
// * AVX-512 -> `XCR0.AVX-512[7:5]`.
//
// by setting the corresponding bits of `XCR0` to `1`.
//
// This is safe because the CPU supports `xsave`
// and the OS has set `osxsave`.
let xcr0 = unsafe { _xgetbv(0) };
// Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`:
let os_avx_support = xcr0 & 6 == 6;
// Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 224`:
let os_avx512_support = xcr0 & 224 == 224;
// Only if the OS and the CPU support saving/restoring the AVX
// registers we enable `xsave` support:
if os_avx_support {
// See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
// FEATURES" in the "Intel® 64 and IA-32 Architectures Software
// Developers Manual, Volume 1: Basic Architecture":
//
// "Software enables the XSAVE feature set by setting
// CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4
// instruction). If this bit is 0, execution of any of XGETBV,
// XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV
// causes an invalid-opcode exception (#UD)"
//
enable(proc_info_ecx, 26, Feature::xsave);
// For `xsaveopt`, `xsavec`, and `xsaves` we need to query:
// Processor Extended State Enumeration Sub-leaf (EAX = 0DH,
// ECX = 1):
if max_basic_leaf >= 0xd {
let CpuidResult {
eax: proc_extended_state1_eax,
..
} = unsafe { __cpuid_count(0xd_u32, 1) };
enable(proc_extended_state1_eax, 0, Feature::xsaveopt);
enable(proc_extended_state1_eax, 1, Feature::xsavec);
enable(proc_extended_state1_eax, 3, Feature::xsaves);
}
// FMA (uses 256-bit wide registers):
enable(proc_info_ecx, 12, Feature::fma);
// And AVX/AVX2:
enable(proc_info_ecx, 28, Feature::avx);
enable(extended_features_ebx, 5, Feature::avx2);
// For AVX-512 the OS also needs to support saving/restoring
// the extended state, only then we enable AVX-512 support:
if os_avx512_support {
enable(extended_features_ebx, 16, Feature::avx512f);
enable(extended_features_ebx, 17, Feature::avx512dq);
enable(extended_features_ebx, 21, Feature::avx512_ifma);
enable(extended_features_ebx, 26, Feature::avx512pf);
enable(extended_features_ebx, 27, Feature::avx512er);
enable(extended_features_ebx, 28, Feature::avx512cd);
enable(extended_features_ebx, 30, Feature::avx512bw);
enable(extended_features_ebx, 31, Feature::avx512vl);
enable(extended_features_ecx, 1, Feature::avx512_vbmi);
enable(
extended_features_ecx,
14,
Feature::avx512_vpopcntdq,
);
}
}
}
}
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
// On intel CPUs with popcnt, lzcnt implements the
// "missing part" of ABM, so we map both to the same
// internal feature.
//
// The `is_x86_feature_detected!("lzcnt")` macro then
// internally maps to Feature::abm.
enable(extended_proc_info_ecx, 5, Feature::abm);
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
// number(Family 18h).
//
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
// family 17h.
//
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
// Related Hygon kernel patch can be found on
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
// These features are available on AMD arch CPUs:
enable(extended_proc_info_ecx, 6, Feature::sse4a);
enable(extended_proc_info_ecx, 21, Feature::tbm);
}
}
value
}
#[cfg(test)]
mod tests {
extern crate cupid;
#[test]
fn dump() {
println!("aes: {:?}", is_x86_feature_detected!("aes"));
println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq"));
println!("rdrand: {:?}", is_x86_feature_detected!("rdrand"));
println!("rdseed: {:?}", is_x86_feature_detected!("rdseed"));
println!("tsc: {:?}", is_x86_feature_detected!("tsc"));
println!("sse: {:?}", is_x86_feature_detected!("sse"));
println!("sse2: {:?}", is_x86_feature_detected!("sse2"));
println!("sse3: {:?}", is_x86_feature_detected!("sse3"));
println!("ssse3: {:?}", is_x86_feature_detected!("ssse3"));
println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1"));
println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2"));
println!("sse4a: {:?}", is_x86_feature_detected!("sse4a"));
println!("sha: {:?}", is_x86_feature_detected!("sha"));
println!("avx: {:?}", is_x86_feature_detected!("avx"));
println!("avx2: {:?}", is_x86_feature_detected!("avx2"));
println!("avx512f {:?}", is_x86_feature_detected!("avx512f"));
println!("avx512cd {:?}", is_x86_feature_detected!("avx512cd"));
println!("avx512er {:?}", is_x86_feature_detected!("avx512er"));
println!("avx512pf {:?}", is_x86_feature_detected!("avx512pf"));
println!("avx512bw {:?}", is_x86_feature_detected!("avx512bw"));
println!("avx512dq {:?}", is_x86_feature_detected!("avx512dq"));
println!("avx512vl {:?}", is_x86_feature_detected!("avx512vl"));
println!("avx512_ifma {:?}", is_x86_feature_detected!("avx512ifma"));
println!("avx512_vbmi {:?}", is_x86_feature_detected!("avx512vbmi"));
println!(
"avx512_vpopcntdq {:?}",
is_x86_feature_detected!("avx512vpopcntdq")
);
println!("fma: {:?}", is_x86_feature_detected!("fma"));
println!("abm: {:?}", is_x86_feature_detected!("abm"));
println!("bmi: {:?}", is_x86_feature_detected!("bmi1"));
println!("bmi2: {:?}", is_x86_feature_detected!("bmi2"));
println!("tbm: {:?}", is_x86_feature_detected!("tbm"));
println!("popcnt: {:?}", is_x86_feature_detected!("popcnt"));
println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt"));
println!("fxsr: {:?}", is_x86_feature_detected!("fxsr"));
println!("xsave: {:?}", is_x86_feature_detected!("xsave"));
println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt"));
println!("xsaves: {:?}", is_x86_feature_detected!("xsaves"));
println!("xsavec: {:?}", is_x86_feature_detected!("xsavec"));
println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b"));
println!("adx: {:?}", is_x86_feature_detected!("adx"));
println!("rtm: {:?}", is_x86_feature_detected!("rtm"));
}
#[test]
fn compare_with_cupid() {
let information = cupid::master().unwrap();
assert_eq!(is_x86_feature_detected!("aes"), information.aesni());
assert_eq!(is_x86_feature_detected!("pclmulqdq"), information.pclmulqdq());
assert_eq!(is_x86_feature_detected!("rdrand"), information.rdrand());
assert_eq!(is_x86_feature_detected!("rdseed"), information.rdseed());
assert_eq!(is_x86_feature_detected!("tsc"), information.tsc());
assert_eq!(is_x86_feature_detected!("sse"), information.sse());
assert_eq!(is_x86_feature_detected!("sse2"), information.sse2());
assert_eq!(is_x86_feature_detected!("sse3"), information.sse3());
assert_eq!(is_x86_feature_detected!("ssse3"), information.ssse3());
assert_eq!(is_x86_feature_detected!("sse4.1"), information.sse4_1());
assert_eq!(is_x86_feature_detected!("sse4.2"), information.sse4_2());
assert_eq!(is_x86_feature_detected!("sse4a"), information.sse4a());
assert_eq!(is_x86_feature_detected!("sha"), information.sha());
assert_eq!(is_x86_feature_detected!("avx"), information.avx());
assert_eq!(is_x86_feature_detected!("avx2"), information.avx2());
assert_eq!(is_x86_feature_detected!("avx512f"), information.avx512f());
assert_eq!(is_x86_feature_detected!("avx512cd"), information.avx512cd());
assert_eq!(is_x86_feature_detected!("avx512er"), information.avx512er());
assert_eq!(is_x86_feature_detected!("avx512pf"), information.avx512pf());
assert_eq!(is_x86_feature_detected!("avx512bw"), information.avx512bw());
assert_eq!(is_x86_feature_detected!("avx512dq"), information.avx512dq());
assert_eq!(is_x86_feature_detected!("avx512vl"), information.avx512vl());
assert_eq!(
is_x86_feature_detected!("avx512ifma"),
information.avx512_ifma()
);
assert_eq!(
is_x86_feature_detected!("avx512vbmi"),
information.avx512_vbmi()
);
assert_eq!(
is_x86_feature_detected!("avx512vpopcntdq"),
information.avx512_vpopcntdq()
);
assert_eq!(is_x86_feature_detected!("fma"), information.fma());
assert_eq!(is_x86_feature_detected!("bmi1"), information.bmi1());
assert_eq!(is_x86_feature_detected!("bmi2"), information.bmi2());
assert_eq!(is_x86_feature_detected!("popcnt"), information.popcnt());
assert_eq!(is_x86_feature_detected!("abm"), information.lzcnt());
assert_eq!(is_x86_feature_detected!("tbm"), information.tbm());
assert_eq!(is_x86_feature_detected!("lzcnt"), information.lzcnt());
assert_eq!(is_x86_feature_detected!("xsave"), information.xsave());
assert_eq!(is_x86_feature_detected!("xsaveopt"), information.xsaveopt());
assert_eq!(
is_x86_feature_detected!("xsavec"),
information.xsavec_and_xrstor()
);
assert_eq!(
is_x86_feature_detected!("xsaves"),
information.xsaves_xrstors_and_ia32_xss()
);
assert_eq!(
is_x86_feature_detected!("cmpxchg16b"),
information.cmpxchg16b(),
);
assert_eq!(
is_x86_feature_detected!("adx"),
information.adx(),
);
assert_eq!(
is_x86_feature_detected!("rtm"),
information.rtm(),
);
}
}

View File

@ -0,0 +1,49 @@
//! Run-time feature detection for the Rust standard library.
//!
//! To detect whether a feature is enabled in the system running the binary
//! use one of the appropriate macro for the target:
//!
//! * `x86` and `x86_64`: [`is_x86_feature_detected`]
//! * `arm`: [`is_arm_feature_detected`]
//! * `aarch64`: [`is_aarch64_feature_detected`]
//! * `mips`: [`is_mips_feature_detected`]
//! * `mips64`: [`is_mips64_feature_detected`]
//! * `powerpc`: [`is_powerpc_feature_detected`]
//! * `powerpc64`: [`is_powerpc64_feature_detected`]
#![unstable(feature = "stdsimd", issue = "27731")]
#![feature(const_fn, staged_api, stdsimd, doc_cfg, allow_internal_unstable)]
#![allow(clippy::shadow_reuse)]
#![deny(clippy::missing_inline_in_public_items)]
#![cfg_attr(target_os = "linux", feature(linkage))]
#![cfg_attr(all(target_os = "freebsd", target_arch = "aarch64"), feature(asm))]
#![cfg_attr(stdsimd_strict, deny(warnings))]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]
#[macro_use]
extern crate cfg_if;
cfg_if! {
if #[cfg(feature = "std_detect_file_io")] {
#[cfg_attr(test, macro_use(println))]
extern crate std;
#[allow(unused_imports)]
use std::{arch, fs, io, mem, sync};
} else {
#[cfg(test)]
#[macro_use(println)]
extern crate std;
#[allow(unused_imports)]
use core::{arch, mem, sync};
}
}
#[cfg(feature = "std_detect_dlsym_getauxval")]
extern crate libc;
#[doc(hidden)]
#[unstable(feature = "stdsimd", issue = "27731")]
pub mod detect;

View File

@ -0,0 +1,5 @@
//! `std_detect`
#[doc(hidden)] // unstable implementation detail
#[unstable(feature = "stdsimd", issue = "27731")]
pub mod detect;

View File

@ -0,0 +1,98 @@
//! Aarch64 run-time features.
/// Checks if `aarch64` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_aarch64_feature_detected {
("neon") => {
// FIXME: this should be removed once we rename Aarch64 neon to asimd
cfg!(target_feature = "neon") || $crate::detect::check_for($crate::detect::Feature::asimd)
};
("asimd") => {
cfg!(target_feature = "neon") || $crate::detect::check_for($crate::detect::Feature::asimd)
};
("pmull") => {
cfg!(target_feature = "pmull") || $crate::detect::check_for($crate::detect::Feature::pmull)
};
("fp") => {
cfg!(target_feature = "fp") || $crate::detect::check_for($crate::detect::Feature::fp)
};
("fp16") => {
cfg!(target_feature = "fp16") || $crate::detect::check_for($crate::detect::Feature::fp16)
};
("sve") => {
cfg!(target_feature = "sve") || $crate::detect::check_for($crate::detect::Feature::sve)
};
("crc") => {
cfg!(target_feature = "crc") || $crate::detect::check_for($crate::detect::Feature::crc)
};
("crypto") => {
cfg!(target_feature = "crypto")
|| $crate::detect::check_for($crate::detect::Feature::crypto)
};
("lse") => {
cfg!(target_feature = "lse") || $crate::detect::check_for($crate::detect::Feature::lse)
};
("rdm") => {
cfg!(target_feature = "rdm") || $crate::detect::check_for($crate::detect::Feature::rdm)
};
("rcpc") => {
cfg!(target_feature = "rcpc") || $crate::detect::check_for($crate::detect::Feature::rcpc)
};
("dotprod") => {
cfg!(target_feature = "dotprod")
|| $crate::detect::check_for($crate::detect::Feature::dotprod)
};
("ras") => {
compile_error!("\"ras\" feature cannot be detected at run-time")
};
("v8.1a") => {
compile_error!("\"v8.1a\" feature cannot be detected at run-time")
};
("v8.2a") => {
compile_error!("\"v8.2a\" feature cannot be detected at run-time")
};
("v8.3a") => {
compile_error!("\"v8.3a\" feature cannot be detected at run-time")
};
($t:tt,) => {
is_aarch64_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown aarch64 target feature: ", $t))
};
}
/// ARM Aarch64 CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// ARM Advanced SIMD (ASIMD)
asimd,
/// Polynomial Multiply
pmull,
/// Floating point support
fp,
/// Half-float support.
fp16,
/// Scalable Vector Extension (SVE)
sve,
/// CRC32 (Cyclic Redundancy Check)
crc,
/// Crypto: AES + PMULL + SHA1 + SHA2
crypto,
/// Atomics (Large System Extension)
lse,
/// Rounding Double Multiply (ASIMDRDM)
rdm,
/// Release consistent Processor consistent (RcPc)
rcpc,
/// Vector Dot-Product (ASIMDDP)
dotprod,
}

View File

@ -0,0 +1,47 @@
//! Run-time feature detection on ARM Aarch32.
/// Checks if `arm` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_arm_feature_detected {
("neon") => {
cfg!(target_feature = "neon") || $crate::detect::check_for($crate::detect::Feature::neon)
};
("pmull") => {
cfg!(target_feature = "pmull") || $crate::detect::check_for($crate::detect::Feature::pmull)
};
("v7") => {
compile_error!("\"v7\" feature cannot be detected at run-time")
};
("vfp2") => {
compile_error!("\"vfp2\" feature cannot be detected at run-time")
};
("vfp3") => {
compile_error!("\"vfp3\" feature cannot be detected at run-time")
};
("vfp4") => {
compile_error!("\"vfp4\" feature cannot be detected at run-time")
};
($t:tt,) => {
is_arm_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown arm target feature: ", $t))
};
}
/// ARM CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// ARM Advanced SIMD (NEON) - Aarch32
neon,
/// Polynomial Multiply
pmull,
}

View File

@ -0,0 +1,30 @@
//! Run-time feature detection on MIPS.
/// Checks if `mips` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_mips_feature_detected {
("msa") => {
cfg!(target_feature = "msa") || $crate::detect::check_for($crate::detect::Feature::msa)
};
($t:tt,) => {
is_mips_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown mips target feature: ", $t))
};
}
/// MIPS CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// MIPS SIMD Architecture (MSA)
msa,
}

View File

@ -0,0 +1,30 @@
//! Run-time feature detection on MIPS64.
/// Checks if `mips64` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_mips64_feature_detected {
("msa") => {
cfg!(target_feature = "msa") || $crate::detect::check_for($crate::detect::Feature::msa)
};
($t:tt,) => {
is_mips64_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown mips64 target feature: ", $t))
};
}
/// MIPS64 CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// MIPS SIMD Architecture (MSA)
msa,
}

View File

@ -0,0 +1,42 @@
//! Run-time feature detection on PowerPC.
/// Checks if `powerpc` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_powerpc_feature_detected {
("altivec") => {
cfg!(target_feature = "altivec")
|| $crate::detect::check_for($crate::detect::Feature::altivec)
};
("vsx") => {
cfg!(target_feature = "vsx") || $crate::detect::check_for($crate::detect::Feature::vsx)
};
("power8") => {
cfg!(target_feature = "power8")
|| $crate::detect::check_for($crate::detect::Feature::power8)
};
($t:tt,) => {
is_powerpc_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown powerpc target feature: ", $t))
};
}
/// PowerPC CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// Altivec
altivec,
/// VSX
vsx,
/// Power8
power8,
}

View File

@ -0,0 +1,42 @@
//! Run-time feature detection on PowerPC64.
/// Checks if `powerpc64` feature is enabled.
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_powerpc64_feature_detected {
("altivec") => {
cfg!(target_feature = "altivec")
|| $crate::detect::check_for($crate::detect::Feature::altivec)
};
("vsx") => {
cfg!(target_feature = "vsx") || $crate::detect::check_for($crate::detect::Feature::vsx)
};
("power8") => {
cfg!(target_feature = "power8")
|| $crate::detect::check_for($crate::detect::Feature::power8)
};
($t:tt,) => {
is_powerpc64_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown powerpc64 target feature: ", $t))
};
}
/// PowerPC64 CPU Feature enum. Each variant denotes a position in a bitset
/// for a particular feature.
///
/// PLEASE: do not use this, it is an implementation detail subject to change.
#[doc(hidden)]
#[allow(non_camel_case_types)]
#[repr(u8)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// Altivec
altivec,
/// VSX
vsx,
/// Power8
power8,
}

View File

@ -0,0 +1,333 @@
//! This module implements minimal run-time feature detection for x86.
//!
//! The features are detected using the `detect_features` function below.
//! This function uses the CPUID instruction to read the feature flags from the
//! CPU and encodes them in an `usize` where each bit position represents
//! whether a feature is available (bit is set) or unavaiable (bit is cleared).
//!
//! The enum `Feature` is used to map bit positions to feature names, and the
//! the `__crate::detect::check_for!` macro is used to map string literals (e.g.,
//! "avx") to these bit positions (e.g., `Feature::avx`).
//!
//! The run-time feature detection is performed by the
//! `__crate::detect::check_for(Feature) -> bool` function. On its first call,
//! this functions queries the CPU for the available features and stores them
//! in a global `AtomicUsize` variable. The query is performed by just checking
//! whether the feature bit in this global variable is set or cleared.
/// A macro to test at *runtime* whether a CPU feature is available on
/// x86/x86-64 platforms.
///
/// This macro is provided in the standard library and will detect at runtime
/// whether the specified CPU feature is detected. This does **not** resolve at
/// compile time unless the specified feature is already enabled for the entire
/// crate. Runtime detection currently relies mostly on the `cpuid` instruction.
///
/// This macro only takes one argument which is a string literal of the feature
/// being tested for. The feature names supported are the lowercase versions of
/// the ones defined by Intel in [their documentation][docs].
///
/// ## Supported arguments
///
/// This macro supports the same names that `#[target_feature]` supports. Unlike
/// `#[target_feature]`, however, this macro does not support names separated
/// with a comma. Instead testing for multiple features must be done through
/// separate macro invocations for now.
///
/// Supported arguments are:
///
/// * `"aes"`
/// * `"pclmulqdq"`
/// * `"rdrand"`
/// * `"rdseed"`
/// * `"tsc"`
/// * `"mmx"`
/// * `"sse"`
/// * `"sse2"`
/// * `"sse3"`
/// * `"ssse3"`
/// * `"sse4.1"`
/// * `"sse4.2"`
/// * `"sse4a"`
/// * `"sha"`
/// * `"avx"`
/// * `"avx2"`
/// * `"avx512f"`
/// * `"avx512cd"`
/// * `"avx512er"`
/// * `"avx512pf"`
/// * `"avx512bw"`
/// * `"avx512dq"`
/// * `"avx512vl"`
/// * `"avx512ifma"`
/// * `"avx512vbmi"`
/// * `"avx512vpopcntdq"`
/// * `"f16c"`
/// * `"fma"`
/// * `"bmi1"`
/// * `"bmi2"`
/// * `"abm"`
/// * `"lzcnt"`
/// * `"tbm"`
/// * `"popcnt"`
/// * `"fxsr"`
/// * `"xsave"`
/// * `"xsaveopt"`
/// * `"xsaves"`
/// * `"xsavec"`
/// * `"adx"`
/// * `"rtm"`
///
/// [docs]: https://software.intel.com/sites/landingpage/IntrinsicsGuide
#[macro_export]
#[stable(feature = "simd_x86", since = "1.27.0")]
#[allow_internal_unstable(stdsimd_internal, stdsimd)]
macro_rules! is_x86_feature_detected {
("aes") => {
cfg!(target_feature = "aes") || $crate::detect::check_for($crate::detect::Feature::aes)
};
("pclmulqdq") => {
cfg!(target_feature = "pclmulqdq")
|| $crate::detect::check_for($crate::detect::Feature::pclmulqdq)
};
("rdrand") => {
cfg!(target_feature = "rdrand")
|| $crate::detect::check_for($crate::detect::Feature::rdrand)
};
("rdseed") => {
cfg!(target_feature = "rdseed")
|| $crate::detect::check_for($crate::detect::Feature::rdseed)
};
("tsc") => {
cfg!(target_feature = "tsc") || $crate::detect::check_for($crate::detect::Feature::tsc)
};
("mmx") => {
cfg!(target_feature = "mmx") || $crate::detect::check_for($crate::detect::Feature::mmx)
};
("sse") => {
cfg!(target_feature = "sse") || $crate::detect::check_for($crate::detect::Feature::sse)
};
("sse2") => {
cfg!(target_feature = "sse2") || $crate::detect::check_for($crate::detect::Feature::sse2)
};
("sse3") => {
cfg!(target_feature = "sse3") || $crate::detect::check_for($crate::detect::Feature::sse3)
};
("ssse3") => {
cfg!(target_feature = "ssse3") || $crate::detect::check_for($crate::detect::Feature::ssse3)
};
("sse4.1") => {
cfg!(target_feature = "sse4.1")
|| $crate::detect::check_for($crate::detect::Feature::sse4_1)
};
("sse4.2") => {
cfg!(target_feature = "sse4.2")
|| $crate::detect::check_for($crate::detect::Feature::sse4_2)
};
("sse4a") => {
cfg!(target_feature = "sse4a") || $crate::detect::check_for($crate::detect::Feature::sse4a)
};
("sha") => {
cfg!(target_feature = "sha") || $crate::detect::check_for($crate::detect::Feature::sha)
};
("avx") => {
cfg!(target_feature = "avx") || $crate::detect::check_for($crate::detect::Feature::avx)
};
("avx2") => {
cfg!(target_feature = "avx2") || $crate::detect::check_for($crate::detect::Feature::avx2)
};
("avx512f") => {
cfg!(target_feature = "avx512f")
|| $crate::detect::check_for($crate::detect::Feature::avx512f)
};
("avx512cd") => {
cfg!(target_feature = "avx512cd")
|| $crate::detect::check_for($crate::detect::Feature::avx512cd)
};
("avx512er") => {
cfg!(target_feature = "avx512er")
|| $crate::detect::check_for($crate::detect::Feature::avx512er)
};
("avx512pf") => {
cfg!(target_feature = "avx512pf")
|| $crate::detect::check_for($crate::detect::Feature::avx512pf)
};
("avx512bw") => {
cfg!(target_feature = "avx512bw")
|| $crate::detect::check_for($crate::detect::Feature::avx512bw)
};
("avx512dq") => {
cfg!(target_feature = "avx512dq")
|| $crate::detect::check_for($crate::detect::Feature::avx512dq)
};
("avx512vl") => {
cfg!(target_Feature = "avx512vl")
|| $crate::detect::check_for($crate::detect::Feature::avx512vl)
};
("avx512ifma") => {
cfg!(target_feature = "avx512ifma")
|| $crate::detect::check_for($crate::detect::Feature::avx512_ifma)
};
("avx512vbmi") => {
cfg!(target_feature = "avx512vbmi")
|| $crate::detect::check_for($crate::detect::Feature::avx512_vbmi)
};
("avx512vpopcntdq") => {
cfg!(target_feature = "avx512vpopcntdq")
|| $crate::detect::check_for($crate::detect::Feature::avx512_vpopcntdq)
};
("f16c") => {
cfg!(target_feature = "f16c") || $crate::detect::check_for($crate::detect::Feature::f16c)
};
("fma") => {
cfg!(target_feature = "fma") || $crate::detect::check_for($crate::detect::Feature::fma)
};
("bmi1") => {
cfg!(target_feature = "bmi1") || $crate::detect::check_for($crate::detect::Feature::bmi)
};
("bmi2") => {
cfg!(target_feature = "bmi2") || $crate::detect::check_for($crate::detect::Feature::bmi2)
};
("abm") => {
cfg!(target_feature = "abm") || $crate::detect::check_for($crate::detect::Feature::abm)
};
("lzcnt") => {
cfg!(target_feature = "lzcnt") || $crate::detect::check_for($crate::detect::Feature::abm)
};
("tbm") => {
cfg!(target_feature = "tbm") || $crate::detect::check_for($crate::detect::Feature::tbm)
};
("popcnt") => {
cfg!(target_feature = "popcnt")
|| $crate::detect::check_for($crate::detect::Feature::popcnt)
};
("fxsr") => {
cfg!(target_feature = "fxsr") || $crate::detect::check_for($crate::detect::Feature::fxsr)
};
("xsave") => {
cfg!(target_feature = "xsave") || $crate::detect::check_for($crate::detect::Feature::xsave)
};
("xsaveopt") => {
cfg!(target_feature = "xsaveopt")
|| $crate::detect::check_for($crate::detect::Feature::xsaveopt)
};
("xsaves") => {
cfg!(target_feature = "xsaves")
|| $crate::detect::check_for($crate::detect::Feature::xsaves)
};
("xsavec") => {
cfg!(target_feature = "xsavec")
|| $crate::detect::check_for($crate::detect::Feature::xsavec)
};
("cmpxchg16b") => {
cfg!(target_feature = "cmpxchg16b")
|| $crate::detect::check_for($crate::detect::Feature::cmpxchg16b)
};
("adx") => {
cfg!(target_feature = "adx") || $crate::detect::check_for($crate::detect::Feature::adx)
};
("rtm") => {
cfg!(target_feature = "rtm") || $crate::detect::check_for($crate::detect::Feature::rtm)
};
($t:tt,) => {
is_x86_feature_detected!($t);
};
($t:tt) => {
compile_error!(concat!("unknown target feature: ", $t))
};
}
/// X86 CPU Feature enum. Each variant denotes a position in a bitset for a
/// particular feature.
///
/// This is an unstable implementation detail subject to change.
#[allow(non_camel_case_types)]
#[repr(u8)]
#[doc(hidden)]
#[unstable(feature = "stdsimd_internal", issue = "0")]
pub enum Feature {
/// AES (Advanced Encryption Standard New Instructions AES-NI)
aes,
/// CLMUL (Carry-less Multiplication)
pclmulqdq,
/// RDRAND
rdrand,
/// RDSEED
rdseed,
/// TSC (Time Stamp Counter)
tsc,
/// MMX
mmx,
/// SSE (Streaming SIMD Extensions)
sse,
/// SSE2 (Streaming SIMD Extensions 2)
sse2,
/// SSE3 (Streaming SIMD Extensions 3)
sse3,
/// SSSE3 (Supplemental Streaming SIMD Extensions 3)
ssse3,
/// SSE4.1 (Streaming SIMD Extensions 4.1)
sse4_1,
/// SSE4.2 (Streaming SIMD Extensions 4.2)
sse4_2,
/// SSE4a (Streaming SIMD Extensions 4a)
sse4a,
/// SHA
sha,
/// AVX (Advanced Vector Extensions)
avx,
/// AVX2 (Advanced Vector Extensions 2)
avx2,
/// AVX-512 F (Foundation)
avx512f,
/// AVX-512 CD (Conflict Detection Instructions)
avx512cd,
/// AVX-512 ER (Exponential and Reciprocal Instructions)
avx512er,
/// AVX-512 PF (Prefetch Instructions)
avx512pf,
/// AVX-512 BW (Byte and Word Instructions)
avx512bw,
/// AVX-512 DQ (Doubleword and Quadword)
avx512dq,
/// AVX-512 VL (Vector Length Extensions)
avx512vl,
/// AVX-512 IFMA (Integer Fused Multiply Add)
avx512_ifma,
/// AVX-512 VBMI (Vector Byte Manipulation Instructions)
avx512_vbmi,
/// AVX-512 VPOPCNTDQ (Vector Population Count Doubleword and
/// Quadword)
avx512_vpopcntdq,
/// F16C (Conversions between IEEE-754 `binary16` and `binary32` formats)
f16c,
/// FMA (Fused Multiply Add)
fma,
/// BMI1 (Bit Manipulation Instructions 1)
bmi,
/// BMI1 (Bit Manipulation Instructions 2)
bmi2,
/// ABM (Advanced Bit Manipulation) on AMD / LZCNT (Leading Zero
/// Count) on Intel
abm,
/// TBM (Trailing Bit Manipulation)
tbm,
/// POPCNT (Population Count)
popcnt,
/// FXSR (Floating-point context fast save and restor)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,
/// XSAVEOPT (Save Processor Extended States Optimized)
xsaveopt,
/// XSAVES (Save Processor Extended States Supervisor)
xsaves,
/// XSAVEC (Save Processor Extended States Compacted)
xsavec,
/// CMPXCH16B, a 16-byte compare-and-swap instruction
cmpxchg16b,
/// ADX, Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
adx,
/// RTM, Intel (Restricted Transactional Memory)
rtm,
}

View File

@ -0,0 +1,9 @@
//! Bit manipulation utilities.
/// Tests the `bit` of `x`.
#[allow(dead_code)]
#[inline]
pub(crate) fn test(x: usize, bit: u32) -> bool {
debug_assert!(bit < 32, "bit index out-of-bounds");
x & (1 << bit) != 0
}

View File

@ -0,0 +1,164 @@
//! Caches run-time feature detection so that it only needs to be computed
//! once.
#![allow(dead_code)] // not used on all platforms
use crate::sync::atomic::Ordering;
#[cfg(target_pointer_width = "64")]
use crate::sync::atomic::AtomicU64;
#[cfg(target_pointer_width = "32")]
use crate::sync::atomic::AtomicU32;
/// Sets the `bit` of `x`.
#[inline]
const fn set_bit(x: u64, bit: u32) -> u64 {
x | 1 << bit
}
/// Tests the `bit` of `x`.
#[inline]
const fn test_bit(x: u64, bit: u32) -> bool {
x & (1 << bit) != 0
}
/// Maximum number of features that can be cached.
const CACHE_CAPACITY: u32 = 63;
/// This type is used to initialize the cache
#[derive(Copy, Clone)]
pub(crate) struct Initializer(u64);
#[allow(clippy::use_self)]
impl Default for Initializer {
fn default() -> Self {
Initializer(0)
}
}
impl Initializer {
/// Tests the `bit` of the cache.
#[allow(dead_code)]
#[inline]
pub(crate) fn test(self, bit: u32) -> bool {
// FIXME: this way of making sure that the cache is large enough is
// brittle.
debug_assert!(
bit < CACHE_CAPACITY,
"too many features, time to increase the cache size!"
);
test_bit(self.0, bit)
}
/// Sets the `bit` of the cache.
#[inline]
pub(crate) fn set(&mut self, bit: u32) {
// FIXME: this way of making sure that the cache is large enough is
// brittle.
debug_assert!(
bit < CACHE_CAPACITY,
"too many features, time to increase the cache size!"
);
let v = self.0;
self.0 = set_bit(v, bit);
}
}
/// This global variable is a cache of the features supported by the CPU.
static CACHE: Cache = Cache::uninitialized();
/// Feature cache with capacity for `CACHE_CAPACITY` features.
///
/// Note: the last feature bit is used to represent an
/// uninitialized cache.
#[cfg(target_pointer_width = "64")]
struct Cache(AtomicU64);
#[cfg(target_pointer_width = "64")]
#[allow(clippy::use_self)]
impl Cache {
/// Creates an uninitialized cache.
#[allow(clippy::declare_interior_mutable_const)]
const fn uninitialized() -> Self {
Cache(AtomicU64::new(u64::max_value()))
}
/// Is the cache uninitialized?
#[inline]
pub(crate) fn is_uninitialized(&self) -> bool {
self.0.load(Ordering::Relaxed) == u64::max_value()
}
/// Is the `bit` in the cache set?
#[inline]
pub(crate) fn test(&self, bit: u32) -> bool {
test_bit(CACHE.0.load(Ordering::Relaxed), bit)
}
/// Initializes the cache.
#[inline]
pub(crate) fn initialize(&self, value: Initializer) {
self.0.store(value.0, Ordering::Relaxed);
}
}
/// Feature cache with capacity for `CACHE_CAPACITY` features.
///
/// Note: the last feature bit is used to represent an
/// uninitialized cache.
#[cfg(target_pointer_width = "32")]
struct Cache(AtomicU32, AtomicU32);
#[cfg(target_pointer_width = "32")]
impl Cache {
/// Creates an uninitialized cache.
const fn uninitialized() -> Self {
Cache(
AtomicU32::new(u32::max_value()),
AtomicU32::new(u32::max_value()),
)
}
/// Is the cache uninitialized?
#[inline]
pub(crate) fn is_uninitialized(&self) -> bool {
self.1.load(Ordering::Relaxed) == u32::max_value()
}
/// Is the `bit` in the cache set?
#[inline]
pub(crate) fn test(&self, bit: u32) -> bool {
if bit < 32 {
test_bit(CACHE.0.load(Ordering::Relaxed) as u64, bit)
} else {
test_bit(CACHE.1.load(Ordering::Relaxed) as u64, bit - 32)
}
}
/// Initializes the cache.
#[inline]
pub(crate) fn initialize(&self, value: Initializer) {
let lo: u32 = value.0 as u32;
let hi: u32 = (value.0 >> 32) as u32;
self.0.store(lo, Ordering::Relaxed);
self.1.store(hi, Ordering::Relaxed);
}
}
/// Tests the `bit` of the storage. If the storage has not been initialized,
/// initializes it with the result of `f()`.
///
/// On its first invocation, it detects the CPU features and caches them in the
/// `CACHE` global variable as an `AtomicU64`.
///
/// It uses the `Feature` variant to index into this variable as a bitset. If
/// the bit is set, the feature is enabled, and otherwise it is disabled.
#[inline]
pub(crate) fn test<F>(bit: u32, f: F) -> bool
where
F: FnOnce() -> Initializer,
{
if CACHE.is_uninitialized() {
CACHE.initialize(f());
}
CACHE.test(bit)
}

View File

@ -0,0 +1,150 @@
//! The `is_{target_arch}_feature_detected!` macro are only available on their
//! architecture. These macros provide a better error messages when the user
//! attempts to call them in a different architecture.
/// Prevents compilation if `is_x86_feature_detected` is used somewhere
/// else than `x86` and `x86_64` targets.
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
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(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_arm_feature_detected` is used somewhere else
/// than `ARM` targets.
#[cfg(not(target_arch = "arm"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_arm_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_arm_feature_detected can only be used on ARM targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "arm")] {
if is_arm_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_aarch64_feature_detected` is used somewhere else
/// than `aarch64` targets.
#[cfg(not(target_arch = "aarch64"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_aarch64_feature_detected {
($t: tt) => {
compile_error!(
r#"
is_aarch64_feature_detected can only be used on AArch64 targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "aarch64")] {
if is_aarch64_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_powerpc_feature_detected` is used somewhere else
/// than `PowerPC` targets.
#[cfg(not(target_arch = "powerpc"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_powerpc_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_powerpc_feature_detected can only be used on PowerPC targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "powerpc")] {
if is_powerpc_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_powerpc64_feature_detected` is used somewhere
/// else than `PowerPC64` targets.
#[cfg(not(target_arch = "powerpc64"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_powerpc64_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_powerpc64_feature_detected can only be used on PowerPC64 targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "powerpc64")] {
if is_powerpc64_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_mips_feature_detected` is used somewhere else
/// than `MIPS` targets.
#[cfg(not(target_arch = "mips"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_mips_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_mips_feature_detected can only be used on MIPS targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "mips")] {
if is_mips_feature_detected(...) { ... }
}
"#
)
};
}
/// Prevents compilation if `is_mips64_feature_detected` is used somewhere else
/// than `MIPS64` targets.
#[cfg(not(target_arch = "mips64"))]
#[macro_export]
#[unstable(feature = "stdsimd", issue = "27731")]
macro_rules! is_mips64_feature_detected {
($t:tt) => {
compile_error!(
r#"
is_mips64_feature_detected can only be used on MIPS64 targets.
You can prevent it from being used in other architectures by
guarding it behind a cfg(target_arch) as follows:
#[cfg(target_arch = "mips64")] {
if is_mips64_feature_detected(...) { ... }
}
"#
)
};
}

View File

@ -0,0 +1,85 @@
//! This module implements run-time feature detection.
//!
//! The `is_{arch}_feature_detected!("feature-name")` macros take the name of a
//! feature as a string-literal, and return a boolean indicating whether the
//! feature is enabled at run-time or not.
//!
//! These macros do two things:
//! * map the string-literal into an integer stored as a `Feature` enum,
//! * call a `os::check_for(x: Feature)` function that returns `true` if the
//! feature is enabled.
//!
//! The `Feature` enums are also implemented in the `arch/{target_arch}.rs`
//! modules.
//!
//! The `check_for` functions are, in general, Operating System dependent. Most
//! architectures do not allow user-space programs to query the feature bits
//! due to security concerns (x86 is the big exception). These functions are
//! implemented in the `os/{target_os}.rs` modules.
#[macro_use]
mod error_macros;
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
#[path = "arch/x86.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "arm")] {
#[path = "arch/arm.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "aarch64")] {
#[path = "arch/aarch64.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "powerpc")] {
#[path = "arch/powerpc.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "powerpc64")] {
#[path = "arch/powerpc64.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "mips")] {
#[path = "arch/mips.rs"]
#[macro_use]
mod arch;
} else if #[cfg(target_arch = "mips64")] {
#[path = "arch/mips64.rs"]
#[macro_use]
mod arch;
} else {
// Unimplemented architecture:
mod arch {
pub enum Feature {
Null
}
}
}
}
pub use self::arch::Feature;
mod bit;
mod cache;
cfg_if! {
if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
// On x86/x86_64 no OS specific functionality is required.
#[path = "os/x86.rs"]
mod os;
} else if #[cfg(all(target_os = "linux", feature = "use_std"))] {
#[path = "os/linux/mod.rs"]
mod os;
} else if #[cfg(target_os = "freebsd")] {
#[cfg(target_arch = "aarch64")]
#[path = "os/aarch64.rs"]
mod aarch64;
#[path = "os/freebsd/mod.rs"]
mod os;
} else {
#[path = "os/other.rs"]
mod os;
}
}
pub use self::os::check_for;

View File

@ -0,0 +1,88 @@
//! Run-time feature detection for Aarch64 on any OS that emulates the mrs instruction.
//!
//! On FreeBSD >= 12.0, Linux >= 4.11 and other operating systems, it is possible to use
//! privileged system registers from userspace to check CPU feature support.
//!
//! AArch64 system registers ID_AA64ISAR0_EL1, ID_AA64PFR0_EL1, ID_AA64ISAR1_EL1
//! have bits dedicated to features like AdvSIMD, CRC32, AES, atomics (LSE), etc.
//! Each part of the register indicates the level of support for a certain feature, e.g.
//! when ID_AA64ISAR0_EL1\[7:4\] is >= 1, AES is supported; when it's >= 2, PMULL is supported.
//!
//! For proper support of [SoCs where different cores have different capabilities](https://medium.com/@jadr2ddude/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb),
//! the OS has to always report only the features supported by all cores, like [FreeBSD does](https://reviews.freebsd.org/D17137#393947).
//!
//! References:
//!
//! - [Zircon implementation](https://fuchsia.googlesource.com/zircon/+/master/kernel/arch/arm64/feature.cpp)
//! - [Linux documentation](https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt)
use crate::detect::{cache, Feature};
/// Try to read the features from the system registers.
///
/// This will cause SIGILL if the current OS is not trapping the mrs instruction.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
let aa64isar0: u64;
unsafe {
asm!("mrs $0, ID_AA64ISAR0_EL1" : "=r"(aa64isar0));
}
let aes = bits_shift(aa64isar0, 7, 4) >= 1;
let pmull = bits_shift(aa64isar0, 7, 4) >= 2;
let sha1 = bits_shift(aa64isar0, 11, 8) >= 1;
let sha2 = bits_shift(aa64isar0, 15, 12) >= 1;
enable_feature(Feature::pmull, pmull);
// Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp
enable_feature(Feature::crypto, aes && pmull && sha1 && sha2);
enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1);
enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1);
// ID_AA64PFR0_EL1 - Processor Feature Register 0
let aa64pfr0: u64;
unsafe {
asm!("mrs $0, ID_AA64PFR0_EL1" : "=r"(aa64pfr0));
}
let fp = bits_shift(aa64pfr0, 19, 16) < 0xF;
let fphp = bits_shift(aa64pfr0, 19, 16) >= 1;
let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF;
let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1;
enable_feature(Feature::fp, fp);
enable_feature(Feature::fp16, fphp);
// SIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp));
// SIMD extensions require SIMD support:
enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1);
enable_feature(
Feature::dotprod,
asimd && bits_shift(aa64isar0, 47, 44) >= 1,
);
enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1);
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
let aa64isar1: u64;
unsafe {
asm!("mrs $0, ID_AA64ISAR1_EL1" : "=r"(aa64isar1));
}
enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1);
}
value
}
#[inline]
fn bits_shift(x: u64, high: usize, low: usize) -> u64 {
(x >> low) & ((1 << (high - low + 1)) - 1)
}

View File

@ -0,0 +1,28 @@
//! Run-time feature detection for Aarch64 on FreeBSD.
use super::super::aarch64::detect_features;
use crate::detect::{cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
#[cfg(test)]
mod tests {
#[test]
fn dump() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
println!("sve: {:?}", is_aarch64_feature_detected!("sve"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("crypto: {:?}", is_aarch64_feature_detected!("crypto"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
}
}

View File

@ -0,0 +1,27 @@
//! Run-time feature detection for ARM on FreeBSD
use super::auxvec;
use crate::detect::{cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::neon, auxv.hwcap & 0x00001000 != 0);
enable_feature(&mut value, Feature::pmull, auxv.hwcap2 & 0x00000002 != 0);
return value;
}
value
}

View File

@ -0,0 +1,94 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(any(target_arch = "arm", target_arch = "powerpc64"), allow(dead_code))]
/// Key to access the CPU Hardware capabilities bitfield.
pub(crate) const AT_HWCAP: usize = 25;
/// Key to access the CPU Hardware capabilities 2 bitfield.
pub(crate) const AT_HWCAP2: usize = 26;
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For FreeBSD, they are
/// defined in [sys/elf_common.h][elf_common_h]. The hardware capabilities of a given
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time.
///
/// [elf_common.h]: https://svnweb.freebsd.org/base/release/12.0.0/sys/sys/elf_common.h?revision=341707
pub(crate) fn auxv() -> Result<AuxVec, ()> {
if let Ok(hwcap) = archauxv(AT_HWCAP) {
if let Ok(hwcap2) = archauxv(AT_HWCAP2) {
if hwcap != 0 && hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
Err(())
}
/// Tries to read the `key` from the auxiliary vector.
fn archauxv(key: usize) -> Result<usize, ()> {
use crate::mem;
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Elf_Auxinfo {
pub a_type: usize,
pub a_un: unnamed,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub union unnamed {
pub a_val: libc::c_long,
pub a_ptr: *mut libc::c_void,
pub a_fcn: Option<unsafe extern "C" fn() -> ()>,
}
let mut auxv: [Elf_Auxinfo; 27] = [Elf_Auxinfo {
a_type: 0,
a_un: unnamed { a_val: 0 },
}; 27];
let mut len: libc::c_uint = mem::size_of_val(&auxv) as libc::c_uint;
unsafe {
let mut mib = [
libc::CTL_KERN,
libc::KERN_PROC,
libc::KERN_PROC_AUXV,
libc::getpid(),
];
let ret = libc::sysctl(
mib.as_mut_ptr(),
mib.len() as u32,
&mut auxv as *mut _ as *mut _,
&mut len as *mut _ as *mut _,
0 as *mut libc::c_void,
0,
);
if ret != -1 {
for i in 0..auxv.len() {
if auxv[i].a_type == key {
return Ok(auxv[i].a_un.a_val as usize);
}
}
}
}
return Ok(0);
}

View File

@ -0,0 +1,22 @@
//! Run-time feature detection on FreeBSD
mod auxvec;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
mod aarch64;
pub use self::aarch64::check_for;
} else if #[cfg(target_arch = "arm")] {
mod arm;
pub use self::arm::check_for;
} else if #[cfg(target_arch = "powerpc64")] {
mod powerpc;
pub use self::powerpc::check_for;
} else {
use crate::arch::detect::Feature;
/// Performs run-time feature detection.
pub fn check_for(_x: Feature) -> bool {
false
}
}
}

View File

@ -0,0 +1,27 @@
//! Run-time feature detection for PowerPC on FreeBSD.
use super::auxvec;
use crate::detect::{cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
return value;
}
value
}

View File

@ -0,0 +1,160 @@
//! Run-time feature detection for Aarch64 on Linux.
use super::{auxvec, cpuinfo};
use crate::detect::{bit, cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from /proc/cpuinfo.
fn detect_features() -> cache::Initializer {
if let Ok(auxv) = auxvec::auxv() {
let hwcap: AtHwcap = auxv.into();
return hwcap.cache();
}
if let Ok(c) = cpuinfo::CpuInfo::new() {
let hwcap: AtHwcap = c.into();
return hwcap.cache();
}
cache::Initializer::default()
}
/// These values are part of the platform-specific [asm/hwcap.h][hwcap] .
///
/// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
struct AtHwcap {
fp: bool, // 0
asimd: bool, // 1
// evtstrm: bool, // 2
aes: bool, // 3
pmull: bool, // 4
sha1: bool, // 5
sha2: bool, // 6
crc32: bool, // 7
atomics: bool, // 8
fphp: bool, // 9
asimdhp: bool, // 10
// cpuid: bool, // 11
asimdrdm: bool, // 12
// jscvt: bool, // 13
// fcma: bool, // 14
lrcpc: bool, // 15
// dcpop: bool, // 16
// sha3: bool, // 17
// sm3: bool, // 18
// sm4: bool, // 19
asimddp: bool, // 20
// sha512: bool, // 21
sve: bool, // 22
}
impl From<auxvec::AuxVec> for AtHwcap {
/// Reads AtHwcap from the auxiliary vector.
fn from(auxv: auxvec::AuxVec) -> Self {
AtHwcap {
fp: bit::test(auxv.hwcap, 0),
asimd: bit::test(auxv.hwcap, 1),
// evtstrm: bit::test(auxv.hwcap, 2),
aes: bit::test(auxv.hwcap, 3),
pmull: bit::test(auxv.hwcap, 4),
sha1: bit::test(auxv.hwcap, 5),
sha2: bit::test(auxv.hwcap, 6),
crc32: bit::test(auxv.hwcap, 7),
atomics: bit::test(auxv.hwcap, 8),
fphp: bit::test(auxv.hwcap, 9),
asimdhp: bit::test(auxv.hwcap, 10),
// cpuid: bit::test(auxv.hwcap, 11),
asimdrdm: bit::test(auxv.hwcap, 12),
// jscvt: bit::test(auxv.hwcap, 13),
// fcma: bit::test(auxv.hwcap, 14),
lrcpc: bit::test(auxv.hwcap, 15),
// dcpop: bit::test(auxv.hwcap, 16),
// sha3: bit::test(auxv.hwcap, 17),
// sm3: bit::test(auxv.hwcap, 18),
// sm4: bit::test(auxv.hwcap, 19),
asimddp: bit::test(auxv.hwcap, 20),
// sha512: bit::test(auxv.hwcap, 21),
sve: bit::test(auxv.hwcap, 22),
}
}
}
impl From<cpuinfo::CpuInfo> for AtHwcap {
/// Reads AtHwcap from /proc/cpuinfo .
fn from(c: cpuinfo::CpuInfo) -> Self {
let f = &c.field("Features");
AtHwcap {
// 64-bit names. FIXME: In 32-bit compatibility mode /proc/cpuinfo will
// map some of the 64-bit names to some 32-bit feature names. This does not
// cover that yet.
fp: f.has("fp"),
asimd: f.has("asimd"),
// evtstrm: f.has("evtstrm"),
aes: f.has("aes"),
pmull: f.has("pmull"),
sha1: f.has("sha1"),
sha2: f.has("sha2"),
crc32: f.has("crc32"),
atomics: f.has("atomics"),
fphp: f.has("fphp"),
asimdhp: f.has("asimdhp"),
// cpuid: f.has("cpuid"),
asimdrdm: f.has("asimdrdm"),
// jscvt: f.has("jscvt"),
// fcma: f.has("fcma"),
lrcpc: f.has("lrcpc"),
// dcpop: f.has("dcpop"),
// sha3: f.has("sha3"),
// sm3: f.has("sm3"),
// sm4: f.has("sm4"),
asimddp: f.has("asimddp"),
// sha512: f.has("sha512"),
sve: f.has("sve"),
}
}
}
impl AtHwcap {
/// Initializes the cache from the feature -bits.
///
/// The features are enabled approximately like in LLVM host feature detection:
/// https://github.com/llvm-mirror/llvm/blob/master/lib/Support/Host.cpp#L1273
fn cache(self) -> cache::Initializer {
let mut value = cache::Initializer::default();
{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
enable_feature(Feature::fp, self.fp);
// Half-float support requires float support
enable_feature(Feature::fp16, self.fp && self.fphp);
enable_feature(Feature::pmull, self.pmull);
enable_feature(Feature::crc, self.crc32);
enable_feature(Feature::lse, self.atomics);
enable_feature(Feature::rcpc, self.lrcpc);
// SIMD support requires float support - if half-floats are
// supported, it also requires half-float support:
let asimd = self.fp && self.asimd && (!self.fphp | self.asimdhp);
enable_feature(Feature::asimd, asimd);
// SIMD extensions require SIMD support:
enable_feature(Feature::rdm, self.asimdrdm && asimd);
enable_feature(Feature::dotprod, self.asimddp && asimd);
enable_feature(Feature::sve, self.sve && asimd);
// Crypto is specified as AES + PMULL + SHA1 + SHA2 per LLVM/hosts.cpp
enable_feature(
Feature::crypto,
self.aes && self.pmull && self.sha1 && self.sha2,
);
}
value
}
}

View File

@ -0,0 +1,52 @@
//! Run-time feature detection for ARM on Linux.
use super::{auxvec, cpuinfo};
use crate::detect::{bit, cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from /proc/cpuinfo.
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::neon, bit::test(auxv.hwcap, 12));
enable_feature(&mut value, Feature::pmull, bit::test(auxv.hwcap2, 1));
return value;
}
if let Ok(c) = cpuinfo::CpuInfo::new() {
enable_feature(
&mut value,
Feature::neon,
c.field("Features").has("neon") && !has_broken_neon(&c),
);
enable_feature(&mut value, Feature::pmull, c.field("Features").has("pmull"));
return value;
}
value
}
/// Is the CPU known to have a broken NEON unit?
///
/// See https://crbug.com/341598.
fn has_broken_neon(cpuinfo: &cpuinfo::CpuInfo) -> bool {
cpuinfo.field("CPU implementer") == "0x51"
&& cpuinfo.field("CPU architecture") == "7"
&& cpuinfo.field("CPU variant") == "0x1"
&& cpuinfo.field("CPU part") == "0x04d"
&& cpuinfo.field("CPU revision") == "0"
}

View File

@ -0,0 +1,304 @@
//! Parses ELF auxiliary vectors.
#![cfg_attr(not(target_arch = "aarch64"), allow(dead_code))]
#[cfg(feature = "std_detect_file_io")]
use crate::{fs::File, io::Read};
/// Key to access the CPU Hardware capabilities bitfield.
pub(crate) const AT_HWCAP: usize = 16;
/// Key to access the CPU Hardware capabilities 2 bitfield.
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
pub(crate) const AT_HWCAP2: usize = 26;
/// Cache HWCAP bitfields of the ELF Auxiliary Vector.
///
/// If an entry cannot be read all the bits in the bitfield are set to zero.
/// This should be interpreted as all the features being disabled.
#[derive(Debug, Copy, Clone)]
pub(crate) struct AuxVec {
pub hwcap: usize,
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
pub hwcap2: usize,
}
/// ELF Auxiliary Vector
///
/// The auxiliary vector is a memory region in a running ELF program's stack
/// composed of (key: usize, value: usize) pairs.
///
/// The keys used in the aux vector are platform dependent. For Linux, they are
/// defined in [linux/auxvec.h][auxvec_h]. The hardware capabilities of a given
/// CPU can be queried with the `AT_HWCAP` and `AT_HWCAP2` keys.
///
/// There is no perfect way of reading the auxiliary vector.
///
/// - If the `std_detect_dlsym_getauxval` cargo feature is enabled, this will use
/// `getauxval` if its linked to the binary, and otherwise proceed to a fallback implementation.
/// When `std_detect_dlsym_getauxval` is disabled, this will assume that `getauxval` is
/// linked to the binary - if that is not the case the behavior is undefined.
/// - Otherwise, if the `std_detect_file_io` cargo feature is enabled, it will
/// try to read `/proc/self/auxv`.
/// - If that fails, this function returns an error.
///
/// Note that run-time feature detection is not invoked for features that can
/// be detected at compile-time. Also note that if this function returns an
/// error, cpuinfo still can (and will) be used to try to perform run-time
/// feature detecton on some platforms.
///
/// For more information about when `getauxval` is available check the great
/// [`auxv` crate documentation][auxv_docs].
///
/// [auxvec_h]: https://github.com/torvalds/linux/blob/master/include/uapi/linux/auxvec.h
/// [auxv_docs]: https://docs.rs/auxv/0.3.3/auxv/
pub(crate) fn auxv() -> Result<AuxVec, ()> {
#[cfg(feature = "std_detect_dlsym_getauxval")]
{
// Try to call a dynamically-linked getauxval function.
if let Ok(hwcap) = getauxval(AT_HWCAP) {
// Targets with only AT_HWCAP:
#[cfg(any(target_arch = "aarch64", target_arch = "mips", target_arch = "mips64"))]
{
if hwcap != 0 {
return Ok(AuxVec { hwcap });
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
if let Ok(hwcap2) = getauxval(AT_HWCAP2) {
if hwcap != 0 && hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
drop(hwcap);
}
#[cfg(feature = "std_detect_file_io")]
{
// If calling getauxval fails, try to read the auxiliary vector from
// its file:
auxv_from_file("/proc/self/auxv")
}
#[cfg(not(feature = "std_detect_file_io"))]
{
Err(())
}
}
#[cfg(not(feature = "std_detect_dlsym_getauxval"))]
{
let hwcap = unsafe { ffi_getauxval(AT_HWCAP) };
// Targets with only AT_HWCAP:
#[cfg(any(target_arch = "aarch64", target_arch = "mips", target_arch = "mips64"))]
{
if hwcap != 0 {
return Ok(AuxVec { hwcap });
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
let hwcap2 = unsafe { ffi_getauxval(AT_HWCAP2) };
if hwcap != 0 && hwcap2 != 0 {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
}
}
/// Tries to read the `key` from the auxiliary vector by calling the
/// dynamically-linked `getauxval` function. If the function is not linked,
/// this function return `Err`.
#[cfg(feature = "std_detect_dlsym_getauxval")]
fn getauxval(key: usize) -> Result<usize, ()> {
use libc;
pub type F = unsafe extern "C" fn(usize) -> usize;
unsafe {
let ptr = libc::dlsym(libc::RTLD_DEFAULT, "getauxval\0".as_ptr() as *const _);
if ptr.is_null() {
return Err(());
}
let ffi_getauxval: F = mem::transmute(ptr);
Ok(ffi_getauxval(key))
}
}
/// Tries to read the auxiliary vector from the `file`. If this fails, this
/// function returns `Err`.
#[cfg(feature = "std_detect_file_io")]
fn auxv_from_file(file: &str) -> Result<AuxVec, ()> {
let mut file = File::open(file).map_err(|_| ())?;
// See <https://github.com/torvalds/linux/blob/v3.19/include/uapi/linux/auxvec.h>.
//
// The auxiliary vector contains at most 32 (key,value) fields: from
// `AT_EXECFN = 31` to `AT_NULL = 0`. That is, a buffer of
// 2*32 `usize` elements is enough to read the whole vector.
let mut buf = [0_usize; 64];
{
let raw: &mut [u8; 64 * mem::size_of::<usize>()] = unsafe { mem::transmute(&mut buf) };
file.read(raw).map_err(|_| ())?;
}
auxv_from_buf(&buf)
}
/// Tries to interpret the `buffer` as an auxiliary vector. If that fails, this
/// function returns `Err`.
#[cfg(feature = "std_detect_file_io")]
fn auxv_from_buf(buf: &[usize; 64]) -> Result<AuxVec, ()> {
// Targets with only AT_HWCAP:
#[cfg(any(target_arch = "aarch64", target_arch = "mips", target_arch = "mips64"))]
{
for el in buf.chunks(2) {
match el[0] {
AT_HWCAP => return Ok(AuxVec { hwcap: el[1] }),
_ => (),
}
}
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
let mut hwcap = None;
let mut hwcap2 = None;
for el in buf.chunks(2) {
match el[0] {
AT_HWCAP => hwcap = Some(el[1]),
AT_HWCAP2 => hwcap2 = Some(el[1]),
_ => (),
}
}
if let (Some(hwcap), Some(hwcap2)) = (hwcap, hwcap2) {
return Ok(AuxVec { hwcap, hwcap2 });
}
}
drop(buf);
Err(())
}
#[cfg(test)]
mod tests {
extern crate auxv as auxv_crate;
use super::*;
// Reads the Auxiliary Vector key from /proc/self/auxv
// using the auxv crate.
#[cfg(feature = "std_detect_file_io")]
fn auxv_crate_getprocfs(key: usize) -> Option<usize> {
use self::auxv_crate::procfs::search_procfs_auxv;
use self::auxv_crate::AuxvType;
let k = key as AuxvType;
match search_procfs_auxv(&[k]) {
Ok(v) => Some(v[&k] as usize),
Err(_) => None,
}
}
// Reads the Auxiliary Vector key from getauxval()
// using the auxv crate.
#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
fn auxv_crate_getauxval(key: usize) -> Option<usize> {
use self::auxv_crate::getauxval::Getauxval;
use self::auxv_crate::AuxvType;
let q = auxv_crate::getauxval::NativeGetauxval {};
match q.getauxval(key as AuxvType) {
Ok(v) => Some(v as usize),
Err(_) => None,
}
}
// FIXME: on mips/mips64 getauxval returns 0, and /proc/self/auxv
// does not always contain the AT_HWCAP key under qemu.
#[cfg(not(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc")))]
#[test]
fn auxv_crate() {
let v = auxv();
if let Some(hwcap) = auxv_crate_getauxval(AT_HWCAP) {
let rt_hwcap = v.expect("failed to find hwcap key").hwcap;
assert_eq!(rt_hwcap, hwcap);
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
if let Some(hwcap2) = auxv_crate_getauxval(AT_HWCAP2) {
let rt_hwcap2 = v.expect("failed to find hwcap2 key").hwcap2;
assert_eq!(rt_hwcap2, hwcap2);
}
}
}
#[test]
fn auxv_dump() {
if let Ok(auxvec) = auxv() {
println!("{:?}", auxvec);
} else {
println!("both getauxval() and reading /proc/self/auxv failed!");
}
}
#[cfg(feature = "std_detect_file_io")]
cfg_if! {
if #[cfg(target_arch = "arm")] {
#[test]
fn linux_rpi3() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-rpi3.auxv");
println!("file: {}", file);
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 4174038);
assert_eq!(v.hwcap2, 16);
}
#[test]
#[should_panic]
fn linux_macos_vb() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/macos-virtualbox-linux-x86-4850HQ.auxv");
println!("file: {}", file);
let v = auxv_from_file(file).unwrap();
// this file is incomplete (contains hwcap but not hwcap2), we
// want to fall back to /proc/cpuinfo in this case, so
// reading should fail. assert_eq!(v.hwcap, 126614527);
// assert_eq!(v.hwcap2, 0);
}
} else if #[cfg(target_arch = "aarch64")] {
#[test]
fn linux_x64() {
let file = concat!(env!("CARGO_MANIFEST_DIR"), "/src/detect/test_data/linux-x64-i7-6850k.auxv");
println!("file: {}", file);
let v = auxv_from_file(file).unwrap();
assert_eq!(v.hwcap, 3219913727);
}
}
}
#[test]
#[cfg(feature = "std_detect_file_io")]
fn auxv_dump_procfs() {
if let Ok(auxvec) = auxv_from_file("/proc/self/auxv") {
println!("{:?}", auxvec);
} else {
println!("reading /proc/self/auxv failed!");
}
}
#[test]
fn auxv_crate_procfs() {
let v = auxv();
if let Some(hwcap) = auxv_crate_getprocfs(AT_HWCAP) {
assert_eq!(v.unwrap().hwcap, hwcap);
}
// Targets with AT_HWCAP and AT_HWCAP2:
#[cfg(any(target_arch = "arm", target_arch = "powerpc64"))]
{
if let Some(hwcap2) = auxv_crate_getprocfs(AT_HWCAP2) {
assert_eq!(v.unwrap().hwcap2, hwcap2);
}
}
}
}

View File

@ -0,0 +1,300 @@
//! Parses /proc/cpuinfo
#![cfg_attr(not(target_arch = "arm"), allow(dead_code))]
extern crate std;
use self::std::{fs::File, io, io::Read, prelude::v1::*};
/// cpuinfo
pub(crate) struct CpuInfo {
raw: String,
}
impl CpuInfo {
/// Reads /proc/cpuinfo into CpuInfo.
pub(crate) fn new() -> Result<Self, io::Error> {
let mut file = File::open("/proc/cpuinfo")?;
let mut cpui = Self { raw: String::new() };
file.read_to_string(&mut cpui.raw)?;
Ok(cpui)
}
/// Returns the value of the cpuinfo `field`.
pub(crate) fn field(&self, field: &str) -> CpuInfoField {
for l in self.raw.lines() {
if l.trim().starts_with(field) {
return CpuInfoField::new(l.split(": ").nth(1));
}
}
CpuInfoField(None)
}
/// Returns the `raw` contents of `/proc/cpuinfo`
#[cfg(test)]
fn raw(&self) -> &String {
&self.raw
}
#[cfg(test)]
fn from_str(other: &str) -> Result<Self, ::std::io::Error> {
Ok(Self {
raw: String::from(other),
})
}
}
/// Field of cpuinfo
#[derive(Debug)]
pub(crate) struct CpuInfoField<'a>(Option<&'a str>);
impl<'a> PartialEq<&'a str> for CpuInfoField<'a> {
fn eq(&self, other: &&'a str) -> bool {
match self.0 {
None => other.is_empty(),
Some(f) => f == other.trim(),
}
}
}
impl<'a> CpuInfoField<'a> {
pub(crate) fn new<'b>(v: Option<&'b str>) -> CpuInfoField<'b> {
match v {
None => CpuInfoField::<'b>(None),
Some(f) => CpuInfoField::<'b>(Some(f.trim())),
}
}
/// Does the field exist?
#[cfg(test)]
pub(crate) fn exists(&self) -> bool {
self.0.is_some()
}
/// Does the field contain `other`?
pub(crate) fn has(&self, other: &str) -> bool {
match self.0 {
None => other.is_empty(),
Some(f) => {
let other = other.trim();
for v in f.split(' ') {
if v == other {
return true;
}
}
false
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_dump() {
let cpuinfo = CpuInfo::new().unwrap();
if cpuinfo.field("vendor_id") == "GenuineIntel" {
assert!(cpuinfo.field("flags").exists());
assert!(!cpuinfo.field("vendor33_id").exists());
assert!(cpuinfo.field("flags").has("sse"));
assert!(!cpuinfo.field("flags").has("avx314"));
}
println!("{}", cpuinfo.raw());
}
const CORE_DUO_T6500: &str = r"processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz
stepping : 10
microcode : 0xa0b
cpu MHz : 1600.000
cache size : 2048 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm
bogomips : 4190.43
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
";
#[test]
fn core_duo_t6500() {
let cpuinfo = CpuInfo::from_str(CORE_DUO_T6500).unwrap();
assert_eq!(cpuinfo.field("vendor_id"), "GenuineIntel");
assert_eq!(cpuinfo.field("cpu family"), "6");
assert_eq!(cpuinfo.field("model"), "23");
assert_eq!(
cpuinfo.field("model name"),
"Intel(R) Core(TM)2 Duo CPU T6500 @ 2.10GHz"
);
assert_eq!(
cpuinfo.field("flags"),
"fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm dtherm"
);
assert!(cpuinfo.field("flags").has("fpu"));
assert!(cpuinfo.field("flags").has("dtherm"));
assert!(cpuinfo.field("flags").has("sse2"));
assert!(!cpuinfo.field("flags").has("avx"));
}
const ARM_CORTEX_A53: &str = r"Processor : AArch64 Processor rev 3 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
processor : 4
processor : 5
processor : 6
processor : 7
Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
CPU implementer : 0x41
CPU architecture: AArch64
CPU variant : 0x0
CPU part : 0xd03
CPU revision : 3
Hardware : HiKey Development Board
";
#[test]
fn arm_cortex_a53() {
let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A53).unwrap();
assert_eq!(
cpuinfo.field("Processor"),
"AArch64 Processor rev 3 (aarch64)"
);
assert_eq!(
cpuinfo.field("Features"),
"fp asimd evtstrm aes pmull sha1 sha2 crc32"
);
assert!(cpuinfo.field("Features").has("pmull"));
assert!(!cpuinfo.field("Features").has("neon"));
assert!(cpuinfo.field("Features").has("asimd"));
}
const ARM_CORTEX_A57: &str = r"Processor : Cortex A57 Processor rev 1 (aarch64)
processor : 0
processor : 1
processor : 2
processor : 3
Features : fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt
CPU implementer : 0x41
CPU architecture: 8
CPU variant : 0x1
CPU part : 0xd07
CPU revision : 1";
#[test]
fn arm_cortex_a57() {
let cpuinfo = CpuInfo::from_str(ARM_CORTEX_A57).unwrap();
assert_eq!(
cpuinfo.field("Processor"),
"Cortex A57 Processor rev 1 (aarch64)"
);
assert_eq!(
cpuinfo.field("Features"),
"fp asimd aes pmull sha1 sha2 crc32 wp half thumb fastmult vfp edsp neon vfpv3 tlsi vfpv4 idiva idivt"
);
assert!(cpuinfo.field("Features").has("pmull"));
assert!(cpuinfo.field("Features").has("neon"));
assert!(cpuinfo.field("Features").has("asimd"));
}
const POWER8E_POWERKVM: &str = r"processor : 0
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
processor : 1
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
processor : 2
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
processor : 3
cpu : POWER8E (raw), altivec supported
clock : 3425.000000MHz
revision : 2.1 (pvr 004b 0201)
timebase : 512000000
platform : pSeries
model : IBM pSeries (emulated by qemu)
machine : CHRP IBM pSeries (emulated by qemu)";
#[test]
fn power8_powerkvm() {
let cpuinfo = CpuInfo::from_str(POWER8E_POWERKVM).unwrap();
assert_eq!(cpuinfo.field("cpu"), "POWER8E (raw), altivec supported");
assert!(cpuinfo.field("cpu").has("altivec"));
}
const POWER5P: &str = r"processor : 0
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 1
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 2
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 3
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 4
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 5
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 6
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
processor : 7
cpu : POWER5+ (gs)
clock : 1900.098000MHz
revision : 2.1 (pvr 003b 0201)
timebase : 237331000
platform : pSeries
machine : CHRP IBM,9133-55A";
#[test]
fn power5p() {
let cpuinfo = CpuInfo::from_str(POWER5P).unwrap();
assert_eq!(cpuinfo.field("cpu"), "POWER5+ (gs)");
assert!(!cpuinfo.field("cpu").has("altivec"));
}
}

View File

@ -0,0 +1,31 @@
//! Run-time feature detection for MIPS on Linux.
use super::auxvec;
use crate::detect::{bit, cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from `/proc/cpuinfo`.
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/hwcap.h][hwcap]
//
// [hwcap]: https://github.com/torvalds/linux/blob/master/arch/arm64/include/uapi/asm/hwcap.h
if let Ok(auxv) = auxvec::auxv() {
enable_feature(&mut value, Feature::msa, bit::test(auxv.hwcap, 1));
return value;
}
// TODO: fall back via `cpuinfo`.
value
}

View File

@ -0,0 +1,28 @@
//! Run-time feature detection on Linux
mod auxvec;
#[cfg(feature = "std_detect_file_io")]
mod cpuinfo;
cfg_if! {
if #[cfg(target_arch = "aarch64")] {
mod aarch64;
pub use self::aarch64::check_for;
} else if #[cfg(target_arch = "arm")] {
mod arm;
pub use self::arm::check_for;
} else if #[cfg(any(target_arch = "mips", target_arch = "mips64"))] {
mod mips;
pub use self::mips::check_for;
} else if #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] {
mod powerpc;
pub use self::powerpc::check_for;
} else {
use crate::detect::Feature;
/// Performs run-time feature detection.
pub fn check_for(_x: Feature) -> bool {
false
}
}
}

View File

@ -0,0 +1,41 @@
//! Run-time feature detection for PowerPC on Linux.
use super::{auxvec, cpuinfo};
use crate::detect::{cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Try to read the features from the auxiliary vector, and if that fails, try
/// to read them from /proc/cpuinfo.
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
let enable_feature = |value: &mut cache::Initializer, f, enable| {
if enable {
value.set(f as u32);
}
};
// The values are part of the platform-specific [asm/cputable.h][cputable]
//
// [cputable]: https://github.com/torvalds/linux/blob/master/arch/powerpc/include/uapi/asm/cputable.h
if let Ok(auxv) = auxvec::auxv() {
// note: the PowerPC values are the mask to do the test (instead of the
// index of the bit to test like in ARM and Aarch64)
enable_feature(&mut value, Feature::altivec, auxv.hwcap & 0x10000000 != 0);
enable_feature(&mut value, Feature::vsx, auxv.hwcap & 0x00000080 != 0);
enable_feature(&mut value, Feature::power8, auxv.hwcap2 & 0x80000000 != 0);
return value;
}
// PowerPC's /proc/cpuinfo lacks a proper Feature field,
// but `altivec` support is indicated in the `cpu` field.
if let Ok(c) = cpuinfo::CpuInfo::new() {
enable_feature(&mut value, Feature::altivec, c.field("cpu").has("altivec"));
return value;
}
value
}

View File

@ -0,0 +1,9 @@
//! Other operating systems
use crate::detect::Feature;
/// Performs run-time feature detection.
#[inline]
pub fn check_for(_x: Feature) -> bool {
false
}

View File

@ -0,0 +1,367 @@
//! x86 run-time feature detection is OS independent.
#[cfg(target_arch = "x86")]
use crate::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::*;
use crate::mem;
use crate::detect::{bit, cache, Feature};
/// Performs run-time feature detection.
#[inline]
pub fn check_for(x: Feature) -> bool {
cache::test(x as u32, detect_features)
}
/// Run-time feature detection on x86 works by using the CPUID instruction.
///
/// The [CPUID Wikipedia page][wiki_cpuid] contains
/// all the information about which flags to set to query which values, and in
/// which registers these are reported.
///
/// The definitive references are:
/// - [Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2:
/// Instruction Set Reference, A-Z][intel64_ref].
/// - [AMD64 Architecture Programmer's Manual, Volume 3: General-Purpose and
/// System Instructions][amd64_ref].
///
/// [wiki_cpuid]: https://en.wikipedia.org/wiki/CPUID
/// [intel64_ref]: http://www.intel.de/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.pdf
/// [amd64_ref]: http://support.amd.com/TechDocs/24594.pdf
#[allow(clippy::similar_names)]
fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
// If the x86 CPU does not support the CPUID instruction then it is too
// old to support any of the currently-detectable features.
if !has_cpuid() {
return value;
}
// Calling `__cpuid`/`__cpuid_count` from here on is safe because the CPU
// has `cpuid` support.
// 0. EAX = 0: Basic Information:
// - EAX returns the "Highest Function Parameter", that is, the maximum
// leaf value for subsequent calls of `cpuinfo` in range [0,
// 0x8000_0000]. - The vendor ID is stored in 12 u8 ascii chars,
// returned in EBX, EDX, and ECX (in that order):
let (max_basic_leaf, vendor_id) = unsafe {
let CpuidResult {
eax: max_basic_leaf,
ebx,
ecx,
edx,
} = __cpuid(0);
let vendor_id: [[u8; 4]; 3] = [
mem::transmute(ebx),
mem::transmute(edx),
mem::transmute(ecx),
];
let vendor_id: [u8; 12] = mem::transmute(vendor_id);
(max_basic_leaf, vendor_id)
};
if max_basic_leaf < 1 {
// Earlier Intel 486, CPUID not implemented
return value;
}
// EAX = 1, ECX = 0: Queries "Processor Info and Feature Bits";
// Contains information about most x86 features.
let CpuidResult {
ecx: proc_info_ecx,
edx: proc_info_edx,
..
} = unsafe { __cpuid(0x0000_0001_u32) };
// EAX = 7, ECX = 0: Queries "Extended Features";
// Contains information about bmi,bmi2, and avx2 support.
let (extended_features_ebx, extended_features_ecx) = if max_basic_leaf >= 7 {
let CpuidResult { ebx, ecx, .. } = unsafe { __cpuid(0x0000_0007_u32) };
(ebx, ecx)
} else {
(0, 0) // CPUID does not support "Extended Features"
};
// EAX = 0x8000_0000, ECX = 0: Get Highest Extended Function Supported
// - EAX returns the max leaf value for extended information, that is,
// `cpuid` calls in range [0x8000_0000; u32::MAX]:
let CpuidResult {
eax: extended_max_basic_leaf,
..
} = unsafe { __cpuid(0x8000_0000_u32) };
// EAX = 0x8000_0001, ECX=0: Queries "Extended Processor Info and Feature
// Bits"
let extended_proc_info_ecx = if extended_max_basic_leaf >= 1 {
let CpuidResult { ecx, .. } = unsafe { __cpuid(0x8000_0001_u32) };
ecx
} else {
0
};
{
// borrows value till the end of this scope:
let mut enable = |r, rb, f| {
if bit::test(r as usize, rb) {
value.set(f as u32);
}
};
enable(proc_info_ecx, 0, Feature::sse3);
enable(proc_info_ecx, 1, Feature::pclmulqdq);
enable(proc_info_ecx, 9, Feature::ssse3);
enable(proc_info_ecx, 13, Feature::cmpxchg16b);
enable(proc_info_ecx, 19, Feature::sse4_1);
enable(proc_info_ecx, 20, Feature::sse4_2);
enable(proc_info_ecx, 23, Feature::popcnt);
enable(proc_info_ecx, 25, Feature::aes);
enable(proc_info_ecx, 29, Feature::f16c);
enable(proc_info_ecx, 30, Feature::rdrand);
enable(extended_features_ebx, 18, Feature::rdseed);
enable(extended_features_ebx, 19, Feature::adx);
enable(extended_features_ebx, 11, Feature::rtm);
enable(proc_info_edx, 4, Feature::tsc);
enable(proc_info_edx, 23, Feature::mmx);
enable(proc_info_edx, 24, Feature::fxsr);
enable(proc_info_edx, 25, Feature::sse);
enable(proc_info_edx, 26, Feature::sse2);
enable(extended_features_ebx, 29, Feature::sha);
enable(extended_features_ebx, 3, Feature::bmi);
enable(extended_features_ebx, 8, Feature::bmi2);
// `XSAVE` and `AVX` support:
let cpu_xsave = bit::test(proc_info_ecx as usize, 26);
if cpu_xsave {
// 0. Here the CPU supports `XSAVE`.
// 1. Detect `OSXSAVE`, that is, whether the OS is AVX enabled and
// supports saving the state of the AVX/AVX2 vector registers on
// context-switches, see:
//
// - [intel: is avx enabled?][is_avx_enabled],
// - [mozilla: sse.cpp][mozilla_sse_cpp].
//
// [is_avx_enabled]: https://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
// [mozilla_sse_cpp]: https://hg.mozilla.org/mozilla-central/file/64bab5cbb9b6/mozglue/build/SSE.cpp#l190
let cpu_osxsave = bit::test(proc_info_ecx as usize, 27);
if cpu_osxsave {
// 2. The OS must have signaled the CPU that it supports saving and
// restoring the:
//
// * SSE -> `XCR0.SSE[1]`
// * AVX -> `XCR0.AVX[2]`
// * AVX-512 -> `XCR0.AVX-512[7:5]`.
//
// by setting the corresponding bits of `XCR0` to `1`.
//
// This is safe because the CPU supports `xsave`
// and the OS has set `osxsave`.
let xcr0 = unsafe { _xgetbv(0) };
// Test `XCR0.SSE[1]` and `XCR0.AVX[2]` with the mask `0b110 == 6`:
let os_avx_support = xcr0 & 6 == 6;
// Test `XCR0.AVX-512[7:5]` with the mask `0b1110_0000 == 224`:
let os_avx512_support = xcr0 & 224 == 224;
// Only if the OS and the CPU support saving/restoring the AVX
// registers we enable `xsave` support:
if os_avx_support {
// See "13.3 ENABLING THE XSAVE FEATURE SET AND XSAVE-ENABLED
// FEATURES" in the "Intel® 64 and IA-32 Architectures Software
// Developers Manual, Volume 1: Basic Architecture":
//
// "Software enables the XSAVE feature set by setting
// CR4.OSXSAVE[bit 18] to 1 (e.g., with the MOV to CR4
// instruction). If this bit is 0, execution of any of XGETBV,
// XRSTOR, XRSTORS, XSAVE, XSAVEC, XSAVEOPT, XSAVES, and XSETBV
// causes an invalid-opcode exception (#UD)"
//
enable(proc_info_ecx, 26, Feature::xsave);
// For `xsaveopt`, `xsavec`, and `xsaves` we need to query:
// Processor Extended State Enumeration Sub-leaf (EAX = 0DH,
// ECX = 1):
if max_basic_leaf >= 0xd {
let CpuidResult {
eax: proc_extended_state1_eax,
..
} = unsafe { __cpuid_count(0xd_u32, 1) };
enable(proc_extended_state1_eax, 0, Feature::xsaveopt);
enable(proc_extended_state1_eax, 1, Feature::xsavec);
enable(proc_extended_state1_eax, 3, Feature::xsaves);
}
// FMA (uses 256-bit wide registers):
enable(proc_info_ecx, 12, Feature::fma);
// And AVX/AVX2:
enable(proc_info_ecx, 28, Feature::avx);
enable(extended_features_ebx, 5, Feature::avx2);
// For AVX-512 the OS also needs to support saving/restoring
// the extended state, only then we enable AVX-512 support:
if os_avx512_support {
enable(extended_features_ebx, 16, Feature::avx512f);
enable(extended_features_ebx, 17, Feature::avx512dq);
enable(extended_features_ebx, 21, Feature::avx512_ifma);
enable(extended_features_ebx, 26, Feature::avx512pf);
enable(extended_features_ebx, 27, Feature::avx512er);
enable(extended_features_ebx, 28, Feature::avx512cd);
enable(extended_features_ebx, 30, Feature::avx512bw);
enable(extended_features_ebx, 31, Feature::avx512vl);
enable(extended_features_ecx, 1, Feature::avx512_vbmi);
enable(extended_features_ecx, 14, Feature::avx512_vpopcntdq);
}
}
}
}
// This detects ABM on AMD CPUs and LZCNT on Intel CPUs.
// On intel CPUs with popcnt, lzcnt implements the
// "missing part" of ABM, so we map both to the same
// internal feature.
//
// The `is_x86_feature_detected!("lzcnt")` macro then
// internally maps to Feature::abm.
enable(extended_proc_info_ecx, 5, Feature::abm);
// As Hygon Dhyana originates from AMD technology and shares most of the architecture with
// AMD's family 17h, but with different CPU Vendor ID("HygonGenuine")/Family series
// number(Family 18h).
//
// For CPUID feature bits, Hygon Dhyana(family 18h) share the same definition with AMD
// family 17h.
//
// Related AMD CPUID specification is https://www.amd.com/system/files/TechDocs/25481.pdf.
// Related Hygon kernel patch can be found on
// http://lkml.kernel.org/r/5ce86123a7b9dad925ac583d88d2f921040e859b.1538583282.git.puwen@hygon.cn
if vendor_id == *b"AuthenticAMD" || vendor_id == *b"HygonGenuine" {
// These features are available on AMD arch CPUs:
enable(extended_proc_info_ecx, 6, Feature::sse4a);
enable(extended_proc_info_ecx, 21, Feature::tbm);
}
}
value
}
#[cfg(test)]
mod tests {
extern crate cupid;
#[test]
fn dump() {
println!("aes: {:?}", is_x86_feature_detected!("aes"));
println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq"));
println!("rdrand: {:?}", is_x86_feature_detected!("rdrand"));
println!("rdseed: {:?}", is_x86_feature_detected!("rdseed"));
println!("tsc: {:?}", is_x86_feature_detected!("tsc"));
println!("sse: {:?}", is_x86_feature_detected!("sse"));
println!("sse2: {:?}", is_x86_feature_detected!("sse2"));
println!("sse3: {:?}", is_x86_feature_detected!("sse3"));
println!("ssse3: {:?}", is_x86_feature_detected!("ssse3"));
println!("sse4.1: {:?}", is_x86_feature_detected!("sse4.1"));
println!("sse4.2: {:?}", is_x86_feature_detected!("sse4.2"));
println!("sse4a: {:?}", is_x86_feature_detected!("sse4a"));
println!("sha: {:?}", is_x86_feature_detected!("sha"));
println!("avx: {:?}", is_x86_feature_detected!("avx"));
println!("avx2: {:?}", is_x86_feature_detected!("avx2"));
println!("avx512f {:?}", is_x86_feature_detected!("avx512f"));
println!("avx512cd {:?}", is_x86_feature_detected!("avx512cd"));
println!("avx512er {:?}", is_x86_feature_detected!("avx512er"));
println!("avx512pf {:?}", is_x86_feature_detected!("avx512pf"));
println!("avx512bw {:?}", is_x86_feature_detected!("avx512bw"));
println!("avx512dq {:?}", is_x86_feature_detected!("avx512dq"));
println!("avx512vl {:?}", is_x86_feature_detected!("avx512vl"));
println!("avx512_ifma {:?}", is_x86_feature_detected!("avx512ifma"));
println!("avx512_vbmi {:?}", is_x86_feature_detected!("avx512vbmi"));
println!(
"avx512_vpopcntdq {:?}",
is_x86_feature_detected!("avx512vpopcntdq")
);
println!("fma: {:?}", is_x86_feature_detected!("fma"));
println!("abm: {:?}", is_x86_feature_detected!("abm"));
println!("bmi: {:?}", is_x86_feature_detected!("bmi1"));
println!("bmi2: {:?}", is_x86_feature_detected!("bmi2"));
println!("tbm: {:?}", is_x86_feature_detected!("tbm"));
println!("popcnt: {:?}", is_x86_feature_detected!("popcnt"));
println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt"));
println!("fxsr: {:?}", is_x86_feature_detected!("fxsr"));
println!("xsave: {:?}", is_x86_feature_detected!("xsave"));
println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt"));
println!("xsaves: {:?}", is_x86_feature_detected!("xsaves"));
println!("xsavec: {:?}", is_x86_feature_detected!("xsavec"));
println!("cmpxchg16b: {:?}", is_x86_feature_detected!("cmpxchg16b"));
println!("adx: {:?}", is_x86_feature_detected!("adx"));
println!("rtm: {:?}", is_x86_feature_detected!("rtm"));
}
#[test]
fn compare_with_cupid() {
let information = cupid::master().unwrap();
assert_eq!(is_x86_feature_detected!("aes"), information.aesni());
assert_eq!(
is_x86_feature_detected!("pclmulqdq"),
information.pclmulqdq()
);
assert_eq!(is_x86_feature_detected!("rdrand"), information.rdrand());
assert_eq!(is_x86_feature_detected!("rdseed"), information.rdseed());
assert_eq!(is_x86_feature_detected!("tsc"), information.tsc());
assert_eq!(is_x86_feature_detected!("sse"), information.sse());
assert_eq!(is_x86_feature_detected!("sse2"), information.sse2());
assert_eq!(is_x86_feature_detected!("sse3"), information.sse3());
assert_eq!(is_x86_feature_detected!("ssse3"), information.ssse3());
assert_eq!(is_x86_feature_detected!("sse4.1"), information.sse4_1());
assert_eq!(is_x86_feature_detected!("sse4.2"), information.sse4_2());
assert_eq!(is_x86_feature_detected!("sse4a"), information.sse4a());
assert_eq!(is_x86_feature_detected!("sha"), information.sha());
assert_eq!(is_x86_feature_detected!("avx"), information.avx());
assert_eq!(is_x86_feature_detected!("avx2"), information.avx2());
assert_eq!(is_x86_feature_detected!("avx512f"), information.avx512f());
assert_eq!(is_x86_feature_detected!("avx512cd"), information.avx512cd());
assert_eq!(is_x86_feature_detected!("avx512er"), information.avx512er());
assert_eq!(is_x86_feature_detected!("avx512pf"), information.avx512pf());
assert_eq!(is_x86_feature_detected!("avx512bw"), information.avx512bw());
assert_eq!(is_x86_feature_detected!("avx512dq"), information.avx512dq());
assert_eq!(is_x86_feature_detected!("avx512vl"), information.avx512vl());
assert_eq!(
is_x86_feature_detected!("avx512ifma"),
information.avx512_ifma()
);
assert_eq!(
is_x86_feature_detected!("avx512vbmi"),
information.avx512_vbmi()
);
assert_eq!(
is_x86_feature_detected!("avx512vpopcntdq"),
information.avx512_vpopcntdq()
);
assert_eq!(is_x86_feature_detected!("fma"), information.fma());
assert_eq!(is_x86_feature_detected!("bmi1"), information.bmi1());
assert_eq!(is_x86_feature_detected!("bmi2"), information.bmi2());
assert_eq!(is_x86_feature_detected!("popcnt"), information.popcnt());
assert_eq!(is_x86_feature_detected!("abm"), information.lzcnt());
assert_eq!(is_x86_feature_detected!("tbm"), information.tbm());
assert_eq!(is_x86_feature_detected!("lzcnt"), information.lzcnt());
assert_eq!(is_x86_feature_detected!("xsave"), information.xsave());
assert_eq!(is_x86_feature_detected!("xsaveopt"), information.xsaveopt());
assert_eq!(
is_x86_feature_detected!("xsavec"),
information.xsavec_and_xrstor()
);
assert_eq!(
is_x86_feature_detected!("xsaves"),
information.xsaves_xrstors_and_ia32_xss()
);
assert_eq!(
is_x86_feature_detected!("cmpxchg16b"),
information.cmpxchg16b(),
);
assert_eq!(is_x86_feature_detected!("adx"), information.adx(),);
assert_eq!(is_x86_feature_detected!("rtm"), information.rtm(),);
}
}

View File

@ -0,0 +1,49 @@
//! Run-time feature detection for the Rust standard library.
//!
//! To detect whether a feature is enabled in the system running the binary
//! use one of the appropriate macro for the target:
//!
//! * `x86` and `x86_64`: [`is_x86_feature_detected`]
//! * `arm`: [`is_arm_feature_detected`]
//! * `aarch64`: [`is_aarch64_feature_detected`]
//! * `mips`: [`is_mips_feature_detected`]
//! * `mips64`: [`is_mips64_feature_detected`]
//! * `powerpc`: [`is_powerpc_feature_detected`]
//! * `powerpc64`: [`is_powerpc64_feature_detected`]
#![unstable(feature = "stdsimd", issue = "27731")]
#![feature(const_fn, staged_api, stdsimd, doc_cfg, allow_internal_unstable)]
#![allow(clippy::shadow_reuse)]
#![deny(clippy::missing_inline_in_public_items)]
#![cfg_attr(target_os = "linux", feature(linkage))]
#![cfg_attr(all(target_os = "freebsd", target_arch = "aarch64"), feature(asm))]
#![cfg_attr(stdsimd_strict, deny(warnings))]
#![cfg_attr(test, allow(unused_imports))]
#![no_std]
#[macro_use]
extern crate cfg_if;
cfg_if! {
if #[cfg(feature = "std_detect_file_io")] {
#[cfg_attr(test, macro_use(println))]
extern crate std;
#[allow(unused_imports)]
use std::{arch, fs, io, mem, sync};
} else {
#[cfg(test)]
#[macro_use(println)]
extern crate std;
#[allow(unused_imports)]
use core::{arch, mem, sync};
}
}
#[cfg(feature = "std_detect_dlsym_getauxval")]
extern crate libc;
#[doc(hidden)]
#[unstable(feature = "stdsimd", issue = "27731")]
pub mod detect;

View File

@ -0,0 +1,5 @@
//! `std_detect`
#[doc(hidden)] // unstable implementation detail
#[unstable(feature = "stdsimd", issue = "27731")]
pub mod detect;