tc_runtime 0.1.0

no_std CPU feature detection and capability proof tokens for x86 and AArch64.
Documentation
  • Coverage
  • 100%
    59 out of 59 items documented57 out of 57 items with examples
  • Size
  • Source code size: 72.05 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.24 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 2s Average build duration of successful builds.
  • all releases: 2s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • TomiCheng/tc_core
    0 0 2
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • TomiCheng

tc_runtime

tc_runtime provides CPU feature detection and capability proof tokens for x86 and AArch64. Callers use these tokens to select supported optimized backends. It is no_std unless its optional std feature is enabled, and supports Rust 1.85 and later.

It has no dependencies by default, on any target. Opting into aarch64 runtime detection with aarch64-detect adds cpufeatures, which in turn pulls in libc on Linux, Android, and Apple targets only. Neither requires std.

The x86 API mirrors the capabilities currently queried by Bouncy Castle's Org.BouncyCastle.Runtime.Intrinsics.X86 namespace. Every capability provides:

use tc_runtime::intrinsics::x86::{Aes, Avx2, Sse2};

if let Some(sse2) = Sse2::detect() {
    // Pass the proof token to an SSE2 backend.
    let _ = sse2;
}

assert_eq!(Aes::detect().is_some(), Aes::is_enabled());
assert_eq!(Avx2::detect().is_some(), Avx2::is_enabled());

The private field in each proof token prevents safe caller code from creating one without first performing detection.

The types remain available on every architecture, where is_enabled() returns false and detect() returns None. Off its own architecture a capability's is_enabled() is a const fn, so the caller's branch folds away at compile time and a portable selection costs nothing:

use tc_runtime::intrinsics::{aarch64, x86};

let backend = if x86::Pclmulqdq::detect().is_some() {
    "pclmulqdq"
} else if aarch64::Aes::detect().is_some() {
    "pmull"
} else {
    "portable"
};

x86 capabilities

Bouncy Castle capability Rust capability Detection
Aes Aes (AesNi alias) CPUID AES-NI
Avx2 Avx2 CPUID AVX/AVX2 and OS XMM/YMM state
Bmi1.X64 bmi1::X64 / Bmi1X64 64-bit process and CPUID BMI1
Bmi2 Bmi2 CPUID BMI2
Bmi2.X64 bmi2::X64 / Bmi2X64 64-bit process and CPUID BMI2
Pclmulqdq Pclmulqdq CPUID PCLMULQDQ
Pclmulqdq.V256 pclmulqdq::V256 / PclmulqdqV256 PCLMULQDQ, VPCLMULQDQ and OS XMM/YMM state
Pclmulqdq.V512 pclmulqdq::V512 / PclmulqdqV512 PCLMULQDQ, VPCLMULQDQ, AVX-512F and OS ZMM state
Sse2 Sse2 x86_64 baseline or CPUID SSE2 on x86
Sse41 Sse41 CPUID SSE4.1
Ssse3 Ssse3 CPUID SSSE3

AVX2 and the vector-width PCLMULQDQ checks include operating-system extended-state support; a CPU feature bit alone is not sufficient to execute those instructions safely.

aarch64 capabilities

aarch64 exposes no unprivileged feature-query instruction — the ID_AA64ISAR*_EL1 registers are readable only at EL1 — so runtime detection goes through the operating system rather than an equivalent of CPUID.

LLVM models aarch64 target features more coarsely than the individual Arm architectural features, so one capability can cover several of them:

Rust capability Arm features Detection
Aes FEAT_AES and FEAT_PMULL aes target feature
Dit FEAT_DIT dit target feature
Neon FEAT_AdvSIMD architecture baseline, no probe
Sha2 FEAT_SHA1, FEAT_SHA256 sha2 target feature
Sha3 FEAT_SHA3, FEAT_SHA512 sha3 target feature
Sm4 FEAT_SM3, FEAT_SM4 sm4 target feature

There is deliberately no separate Pmull capability: Linux requires both the HWCAP_AES and HWCAP_PMULL bits, so a GHASH backend takes the Aes token. For the same reason a SHA-512 backend takes the Sha3 token.

Detection backends

A capability is available whenever the matching target_feature is enabled at compile time, because the compiler may then emit those instructions throughout the build. This floor applies whichever backend is selected, and costs nothing at run time. Several targets set most of these by default:

Target Default target_feature
aarch64-apple-darwin aes, dit, neon, sha2, sha3, and more
aarch64-unknown-linux-gnu neon
aarch64-unknown-none neon
aarch64-pc-windows-msvc neon

Runtime detection beyond that floor is a swappable backend:

Backend Cargo feature Covers
none (default) nothing beyond the floor; no dependencies
cpufeatures aarch64-detect Linux and Android via getauxval(AT_HWCAP), Apple platforms via sysctlbyname

No backend can probe Windows on ARM or bare-metal targets, which therefore rely on the compile-time floor alone; aarch64-detect buys them nothing. On aarch64-apple-darwin the floor already covers the features the backend would report, so it changes nothing there either. In practice the feature matters on Linux and Android.

Unlike the x86 module's use of CPUID, none of this requires std; a backend reaches the operating system directly.

Backends live in src/intrinsics/aarch64/backend.rs, behind a contract of one fn() -> bool per feature. Replacing one does not affect the public API.

Cargo features

Feature Effect
std Enables the TC_DISABLE_* environment-variable overrides
aarch64-detect Selects a runtime-detection backend for aarch64
disable-* Turns individual capabilities off; see below

Disabling optimized backends

Use these switches to exercise portable fallback paths in tests, or to temporarily avoid an optimized backend while investigating a defect. They control capability-based backend selection; they are not CPU tuning settings.

With std enabled, environment overrides let operators disable individual backends without rebuilding. Set them before starting the process, then restart the application. Detection results are cached: changing the environment of an already-running process is not a supported way to switch backends.

Cargo disable-* features apply at build time and require a rebuild. Both forms are listed below:

Capability Cargo feature Runtime environment variable with std
AES-NI disable-x86-aes-ni TC_DISABLE_X86_AES_NI
AVX2 disable-x86-avx2 TC_DISABLE_X86_AVX2
BMI1 disable-x86-bmi1 TC_DISABLE_X86_BMI1
BMI2 disable-x86-bmi2 TC_DISABLE_X86_BMI2
PCLMULQDQ, all widths disable-x86-pclmulqdq TC_DISABLE_X86_PCLMULQDQ
PCLMULQDQ 256-bit only disable-x86-pclmulqdq-v256 TC_DISABLE_X86_PCLMULQDQ_V256
PCLMULQDQ 512-bit only disable-x86-pclmulqdq-v512 TC_DISABLE_X86_PCLMULQDQ_V512
SSE2 disable-x86-sse2 TC_DISABLE_X86_SSE2
SSE4.1 disable-x86-sse41 TC_DISABLE_X86_SSE41
SSSE3 disable-x86-ssse3 TC_DISABLE_X86_SSSE3
Arm AES and PMULL disable-aarch64-aes TC_DISABLE_AARCH64_AES
Arm DIT disable-aarch64-dit TC_DISABLE_AARCH64_DIT
Arm NEON disable-aarch64-neon TC_DISABLE_AARCH64_NEON
Arm SHA-1 and SHA-256 disable-aarch64-sha2 TC_DISABLE_AARCH64_SHA2
Arm SHA-3 and SHA-512 disable-aarch64-sha3 TC_DISABLE_AARCH64_SHA3
Arm SM3 and SM4 disable-aarch64-sm4 TC_DISABLE_AARCH64_SM4

For example:

cargo build --features tc_runtime/disable-x86-avx2
cargo build --features tc_runtime/disable-x86-pclmulqdq
cargo build --features tc_runtime/disable-aarch64-aes

For an existing build with std enabled, set one or more of these variables in the service configuration or launching shell before restarting the application:

TC_DISABLE_X86_AVX2=1
TC_DISABLE_X86_PCLMULQDQ=1
TC_DISABLE_AARCH64_AES=1

The presence of a variable disables the matching capability regardless of its value. Results are cached, so changing an environment variable after the first check has no effect. The general PCLMULQDQ switch also disables its V256 and V512 capabilities.

There is currently no global disable switch. If the affected backend is unknown, set all applicable TC_DISABLE_* variables listed above before restarting. The application must provide portable fallbacks for the capabilities it uses.

These switches prevent callers from selecting the matching optimized backend; they do not guarantee that the Rust compiler emits no instructions belonging to that instruction set. In particular, SSE2 is part of the x86_64 baseline and NEON is part of the aarch64 baseline.