#![allow(clippy::needless_doctest_main)] //! This crate provides the [`multiversion`] attribute for implementing function multiversioning. //! //! Many CPU architectures have a variety of instruction set extensions that provide additional //! functionality. Common examples are single instruction, multiple data (SIMD) extensions such as //! SSE and AVX on x86/x86-64 and NEON on ARM/AArch64. When available, these extended features can //! provide significant speed improvements to some functions. These optional features cannot be //! haphazardly compiled into programs–executing an unsupported instruction will result in a //! crash. //! //! **Function multiversioning** is the practice of compiling multiple versions of a function //! with various features enabled and safely detecting which version to use at runtime. //! //! # Cargo features //! There is one cargo feature, `std`, enabled by default. When enabled, [`multiversion`] will //! use CPU feature detection at runtime to dispatch the appropriate function. Disabling this //! feature will only allow compile-time function dispatch using `#[cfg(target_feature)]` and can //! be used in `#[no_std]` crates. //! //! # Capabilities //! The intention of this crate is to allow nearly any function to be multiversioned. //! The following cases are not supported: //! * functions that use `self` or `Self` //! * `impl Trait` return types (arguments are fine) //! //! If any other functions do not work please file an issue on GitHub. //! //! # Target specification strings //! Targets are specified as a combination of architecture (as specified in [`target_arch`]) and //! feature (as specified in [`target_feature`]). //! //! A target can be specified as: //! * `"arch"` //! * `"arch+feature"` //! * `"arch+feature1+feature2"` //! //! A particular CPU can also be specified with a slash: //! * `"arch/cpu"` //! * `"arch/cpu+feature"` //! //! The following are some valid target specification strings: //! * `"x86"` (matches the `"x86"` architecture) //! * `"x86_64+avx+avx2"` (matches the `"x86_64"` architecture with the `"avx"` and `"avx2"` //! features) //! * `"x86_64/x86-64-v2"` (matches the `"x86_64"` architecture with the `"x86-64-v2"` CPU) //! * `"x86/i686+avx"` (matches the `"x86"` architecture with the `"i686"` CPU and `"avx"` //! feature) //! * `"arm+neon"` (matches the `arm` architecture with the `"neon"` feature //! //! A complete list of available target features and CPUs is available in the [`target-features` //! crate documentation](target_features::docs). //! //! [`target`]: attr.target.html //! [`multiversion`]: attr.multiversion.html //! [`target_arch`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch //! [`target_feature`]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature /// Provides function multiversioning. /// /// The annotated function is compiled multiple times, once for each target, and the /// best target is selected at runtime. /// /// Options: /// * `targets` /// * Takes a list of targets, such as `targets("x86_64+avx2", "x86_64+sse4.1")`. /// * Target priority is first to last. The first matching target is used. /// * May also take a special value `targets = "simd"` to automatically multiversion for common /// SIMD target features. /// * `attrs` /// * Takes a list of attributes to attach to each target clone function. /// * `dispatcher` /// * Selects the preferred dispatcher. Defaults to `default`. /// * `default`: If the `std` feature is enabled, uses either `direct` or `indirect`, /// attempting to choose the fastest choice. If the `std` feature is not enabled, uses `static`. /// * `static`: Detects features at compile time from the enabled target features. /// * `indirect`: Detect features at runtime, and dispatches with an indirect function call. /// Cannot be used for generic functions, `async` functions, or functions that take or return an /// `impl Trait`. This is usually the default. /// * `direct`: Detects features at runtime, and dispatches with direct function calls. This is /// the default on functions that do not support indirect dispatch, or in the presence of /// indirect branch exploit mitigations such as retpolines. /// /// # Example /// This function is a good candidate for optimization using SIMD. /// The following compiles `square` three times, once for each target and once for the generic /// target. Calling `square` selects the appropriate version at runtime. /// /// ``` /// use multiversion::multiversion; /// /// #[multiversion(targets("x86_64+avx", "x86+sse"))] /// fn square(x: &mut [f32]) { /// for v in x { /// *v *= *v /// } /// } /// ``` /// /// This example is similar, but targets all supported SIMD instruction sets (not just the two shown above): /// /// ``` /// use multiversion::multiversion; /// /// #[multiversion(targets = "simd")] /// fn square(x: &mut [f32]) { /// for v in x { /// *v *= *v /// } /// } /// ``` /// /// # Notes on dispatcher performance /// /// ### Feature detection is performed only once /// The `direct` and `indirect` dispatchers perform function selection on the first invocation. /// This is implemented with a static atomic variable containing the selected function. /// /// This implementation has a few benefits: /// * The function selector is typically only invoked once. Subsequent calls are reduced to an /// atomic load. /// * If called in multiple threads, there is no contention. Both threads may perform feature /// detection, but the atomic ensures these are synchronized correctly. /// /// ### Dispatcher elision /// If the optimal set of features is already known to exist at compile time, the entire dispatcher /// is elided. For example, if the highest priority target requires `avx512f` and the function is /// compiled with `RUSTFLAGS=-Ctarget-cpu=skylake-avx512`, the function is not multiversioned and /// the highest priority target is used. /// /// [`target`]: attr.target.html /// [`multiversion`]: attr.multiversion.html pub use multiversion_macros::multiversion; /// Provides a less verbose equivalent to the `cfg(target_arch)` and `target_feature` attributes. /// /// A function tagged with `#[target("x86_64+avx+avx2")]`, for example, is equivalent to a /// function tagged with each of: /// * `#[cfg(target_arch = "x86_64")]` /// * `#[target_feature(enable = "avx")]` /// * `#[target_feature(enable = "avx2")]` /// /// The [`target`] attribute is intended to be used in tandem with the [`multiversion`] attribute /// to produce hand-written multiversioned functions. /// /// [`target`]: attr.target.html /// [`multiversion`]: attr.multiversion.html pub use multiversion_macros::target; /// Inherit the `target_feature` attributes of the selected target in a multiversioned function. /// /// # Example /// ``` /// use multiversion::{multiversion, inherit_target}; /// #[multiversion(targets = "simd")] /// fn select_sum() -> unsafe fn(x: &mut[f32]) -> f32 { /// #[inherit_target] /// unsafe fn sum(x: &mut[f32]) -> f32 { /// x.iter().sum() /// } /// sum as unsafe fn(&mut[f32]) -> f32 /// } pub use multiversion_macros::inherit_target; /// Information related to the current target. pub mod target { // used by docs #[allow(unused)] use super::*; /// Get the selected target in a multiversioned function. /// /// Returns the selected target as a [`Target`]. /// /// This macro only works in a function marked with [`multiversion`]. /// /// # Example /// ``` /// use multiversion::{multiversion, target::selected_target}; /// /// #[multiversion(targets = "simd")] /// fn foo() { /// if selected_target!().supports_feature_str("avx") { /// println!("AVX detected"); /// } else { /// println!("AVX not detected"); /// } /// } pub use multiversion_macros::selected_target; /// Equivalent to `#[cfg]`, but considers `target_feature`s detected at runtime. /// /// This macro only works in a function marked with [`multiversion`]. pub use multiversion_macros::target_cfg; /// Equivalent to `#[cfg_attr]`, but considers `target_feature`s detected at runtime. /// /// This macro only works in a function marked with [`multiversion`]. pub use multiversion_macros::target_cfg_attr; /// Match the selected target. /// /// Matching is done at compile time, as if by `#[cfg]`. Target matching considers both /// detected features and statically-enabled features. Arms that do not match are not /// compiled. /// /// This macro only works in a function marked with [`multiversion`]. /// /// # Example /// ``` /// use multiversion::{multiversion, target::match_target}; /// /// #[multiversion(targets = "simd")] /// fn foo() { /// match_target! { /// "x86_64+avx" => println!("x86-64 with AVX"), /// "aarch64+neon" => println!("AArch64 with Neon"), /// _ => println!("another architecture"), /// } /// } /// ``` pub use multiversion_macros::match_target; /// Equivalent to `cfg!`, but considers `target_feature`s detected at runtime. /// /// This macro only works in a function marked with [`multiversion`]. pub use multiversion_macros::target_cfg_f; #[doc(hidden)] pub use multiversion_macros::{ match_target_impl, target_cfg_attr_impl, target_cfg_f_impl, target_cfg_impl, }; #[doc(no_inline)] pub use target_features::Target; } #[doc(hidden)] pub use target_features;