Skip to content

Commit

Permalink
Use cfg!(target_feature) for static AARCH64 feature detection.
Browse files Browse the repository at this point in the history
Our MSRV supports `if` in const expressions. Implement workarounds for
versions of Rust prior to Rust 1.61.
  • Loading branch information
briansmith committed Oct 31, 2022
1 parent b97db43 commit e7c214a
Showing 1 changed file with 72 additions and 56 deletions.
128 changes: 72 additions & 56 deletions src/cpu/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ pub fn setup() {
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
pub fn setup() {
// We do not need to check for the presence of NEON, as Armv8-A always has it
let mut features = NEON.mask;
const _ASSERT_NEON_DETECTED: () = assert!((ARMCAP_STATIC & NEON.mask) == NEON.mask);
let mut features = ARMCAP_STATIC;

let result = unsafe {
winapi::um::processthreadsapi::IsProcessorFeaturePresent(
Expand All @@ -137,17 +138,8 @@ pub fn setup() {
macro_rules! features {
{
$(
$name:ident {
$target_feature_name:expr => $name:ident {
mask: $mask:expr,

// Should we assume that the feature is always available
// for aarch64-apple-* targets? The first AArch64 iOS
// device used the Apple A7 chip.
// TODO: When we can use `if` in const expressions:
// ```
// aarch64_apple: $aarch64_apple,
// ```
aarch64_apple: true,
}
),+
, // trailing comma is required.
Expand All @@ -159,33 +151,16 @@ macro_rules! features {
};
)+

// TODO: When we can use `if` in const expressions, do this:
// ```
// const ARMCAP_STATIC: u32 = 0
// $(
// | ( if $aarch64_apple &&
// cfg!(all(target_arch = "aarch64",
// target_vendor = "apple")) {
// $name.mask
// } else {
// 0
// }
// )
// )+;
// ```
//
// TODO: Add static feature detection to other targets.
// TODO: Combine static feature detection with runtime feature
// detection.
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
const ARMCAP_STATIC: u32 = 0
$( | $name.mask
)+;
#[cfg(not(all(target_arch = "aarch64", target_vendor = "apple")))]
const ARMCAP_STATIC: u32 = 0;

const _ALL_FEATURES_MASK: u32 = 0
$( | $name.mask
const ARMCAP_STATIC: u32 = ARMCAP_STATIC_OVERRIDE
$(
| (
if cfg!(all(any(target_arch = "aarch64", target_arch = "arm"),
target_feature = $target_feature_name)) {
$name.mask
} else {
0
}
)
)+;

#[cfg(all(test, any(target_arch = "arm", target_arch = "aarch64")))]
Expand All @@ -197,6 +172,45 @@ macro_rules! features {
}
}

// TODO(MSRV 1.61.0): `cfg!(target_feature = X)` is false for many
// AArch64 targets prior to Rust 1.61.0. Since we don't support dynamic
// detection for Apple targets, implement a special workaround for them.
// See https://github.com/rust-lang/rust/pull/91608 for background.
//
// ```
// $ rustc +1.61.0 --print cfg --target=aarch64-apple-ios | grep -E "neon|aes|sha|pmull"
// target_feature="aes"
// target_feature="neon"
// target_feature="sha2"
// $ rustc +1.61.0 --print cfg --target=aarch64-apple-darwin | grep -E "neon|aes|sha|pmull"
// target_feature="aes"
// target_feature="neon"
// target_feature="sha2"
// target_feature="sha3"
// ```
//
// In the case of non-Apple targets, presently we do assume that NEON
// is present (e.g. see the Windows logic above):
// ```
// $ rustc +1.61.0 --print cfg --target=aarch64-unknown-linux-gnu | grep -E "neon|aes|sha|pmull"
// target_feature="neon"
// $ rustc +1.61.0 --print cfg --target=aarch64-pc-windows-msvc | grep -E "neon|aes|sha|pmull"
// target_feature="neon"
// ```
const ARMCAP_STATIC_OVERRIDE: u32 = if cfg!(target_arch = "aarch64") {
NEON.mask
| if cfg!(all(
target_arch = "aarch64",
any(target_os = "ios", target_os = "macos")
)) {
AES.mask | PMULL.mask | SHA256.mask
} else {
0
}
} else {
0
};

pub(crate) struct Feature {
mask: u32,
}
Expand Down Expand Up @@ -227,29 +241,33 @@ impl Feature {
}
}

// Assumes all target feature names are the same for ARM and AAarch64.
features! {
// Keep in sync with `ARMV7_NEON`.
NEON {
"neon" => NEON {
mask: 1 << 0,
aarch64_apple: true,
},

// Keep in sync with `ARMV8_AES`.
AES {
"aes" => AES {
mask: 1 << 2,
aarch64_apple: true,
},

// Keep in sync with `ARMV8_SHA256`.
SHA256 {
"sha2" => SHA256 {
mask: 1 << 4,
aarch64_apple: true,
},

// Keep in sync with `ARMV8_PMULL`.
PMULL {
//
// TODO(MSRV): There is no "pmull" feature listed from
// `rustc --print cfg --target=aarch64-apple-darwin`. Originally ARMv8 tied
// PMULL detection into AES detection, but later versions split it; see
// https://developer.arm.com/downloads/-/exploration-tools/feature-names-for-a-profile
// "Features introduced prior to 2020." Change this to use "pmull" when
// that is supported.
"aes" => PMULL {
mask: 1 << 5,
aarch64_apple: true,
},
}

Expand All @@ -266,11 +284,17 @@ prefixed_export! {
static mut OPENSSL_armcap_P: u32 = ARMCAP_STATIC;
}

const _APPLE_TARGETS_HAVE_ALL_FEATURES: () = assert!(
(ARMCAP_STATIC == _ALL_FEATURES_MASK)
const _AARCH64_APPLE_IOS_FEATURES: u32 = NEON.mask | AES.mask | SHA256.mask | PMULL.mask;
const _AARCH64_APPLE_TARGETS_EXPECTED_FEATURES: () = assert!(
((ARMCAP_STATIC & _AARCH64_APPLE_IOS_FEATURES) == _AARCH64_APPLE_IOS_FEATURES)
|| !cfg!(all(target_arch = "aarch64", target_vendor = "apple"))
);

const _NON_AARCH64_HAS_ZERO_ARMCAP_STATIC: () =
assert!(cfg!(not(target_arch = "aarch64")) == (ARMCAP_STATIC == 0));
const _AARCH64_HAS_NEON: () =
assert!(((ARMCAP_STATIC & NEON.mask) == NEON.mask) || !cfg!(target_arch = "aarch64"));

#[cfg(all(test, any(target_arch = "arm", target_arch = "aarch64")))]
mod tests {
use super::*;
Expand All @@ -283,14 +307,6 @@ mod tests {
assert_eq!(PMULL.mask, 32);
}

#[cfg(target_vendor = "apple")]
#[test]
fn test_apple_minimum_features() {
ALL_FEATURES.iter().for_each(|feature| {
assert_eq!(ARMCAP_STATIC & feature.mask, feature.mask);
});
}

#[test]
fn test_armcap_static_is_subset_of_armcap_dynamic() {
// Ensure `OPENSSL_armcap_P` is initialized.
Expand Down

0 comments on commit e7c214a

Please sign in to comment.