- Feature Name:
target_feature
/cfg_target_feature
/cfg_feature_enabled
- Start Date: 2017-06-26
- RFC PR: rust-lang/rfcs#2045
- Rust Issue: rust-lang/rust#44839
Motivation and Summary
While architectures like x86_64
or ARMv8
define--target-feature
and --target-cpu
. Running these binaries
- determine決定するwhich features are available at compile-time, and
- determine決定するwhich features are available at run-time,実行時の(に)and
- embed code for different setsセットする、集合of features into the same binary,2進数
such that the programs can use different algorithms depending on the features available, and allowing
The objective of this RFC is to extend
- compile-time feature detection: using configuration macros
cfg!(target_feature = "avx2")
to detect whether a feature is enabled or disabled in a context文脈、背景(#![cfg(target_feature = "avx2")]
, ...), - run-time実行時の(に)feature detection: using the
cfg_feature_enabled!("avx2")
API to detect whether the current host supports the feature, and - unconditional code generation: using the function attribute
#[target_feature(enable = "avx2")]
to allow許可する、可能にするthe compiler to generate code under the assumption that this code will only be reached in hosts that support the feature.
Detailed design設計(する)
Target features
Each rustc target has a default set
This RFC does not add any target features to the language
- Be proposed in its own mini-RFC, RFC, or rustc-issue and follow下記の、次に続く、追従するa FCP period,
- Be behind its own feature gate macro of the form形式、形態、形作る
target_feature_feature_name
(wherefeature_name
should be replaced by the name of the feature ). - When possible, be detectable at run-time実行時の(に)via the
cfg_feature_enabled!("name")
API. - Include whether some backend-specific compilation options should enable the feature.
To use unstable target features on nightly, crates must opt into them as usual by writing, for example, #![allow(target_feature_avx2)]
. Since this is currently not required, a grace period of one full release cycle will be given
Backend compilation options
There are currently two ways of passing target feature information to rustc's code generation backend on stable Rust.
-
-C --target-feature=+/-backend_target_feature_name
: where+/-
add/remove features from the default feature setセットする、集合of the platform for the whole crate. -
-C --target-cpu=backend_cpu_name
, which changes the default feature setセットする、集合of the crate to be that of all features enabled forbackend_cpu_name
.
These two options are available on stable Rust and have been defacto stabilized. Their semantics are LLVM specific
This RFC proposes to keep these options "as is", and add one new compiler option, --enable-features="feature0,feature1,..."
, (the analogous --disable-features
is discussed in the "Future Extensions" section) that supports only stabilized target features.
This allows
The effect of --enable-features=feature-list
is to enable all features implicitlycfg!(target_feature = "feature")
and cfg_feature_enabled!("feature")
are true
.
Whether the backend compilation options -C --target-feature/--target-cpu
also enable some stabilized features or not should be resolved by the RFCs suggesting the stabilization of particular target features.
Unconditional code generation: #[target_feature]
(note: the function attribute #[target_feature]
is similar__attribute__ ((__target__ ("feature")))
.)
This RFC introduces a function attribute that only applies#[target_feature(enable = "feature_list")]
(the analogous #[target_feature(disable = "feature_list")]
is discussed in the "Future Extensions" section):
- This attribute extends拡張するthe feature setセットする、集合of a function beyond its default feature set,セットする、集合which allows許可する、可能にするthe compiler to generate code under the assumption that the function's code will only be reached on hardware that supports its feature set.セットする、集合
- Calling呼び出しa function on a target that does not support its features is undefined behaviorふるまい(see the "On the unsafety of
#[target_feature]
" section). - The compiler will not inline functions in contexts that do not support all the functions features.
- In
#[target_feature(enable = "feature")]
functions the value ofcfg!(target_feature = "feature")
andcfg_feature_enabled!("feature")
is alwaystrue
(otherwise undefined behaviorふるまいdid already happen).
Note 0: the current RFC does not introduce
Note 1: a function has the features of the crate where the function is defined#[target_feature]
annotations. Iff the function is inlined into a context
Example 0 (basics):
This example covers how to use #[target_feature]
with run-time
// This function will be optimized for different targets
#[inline(always)] fn foo_impl() { ... }
// This generates a stub for CPUs that support SSE4:
#[target_feature(enable = "sse4")] unsafe fn foo_sse4() {
// Inlining `foo_impl` here is fine because `foo_sse4`
// extends `foo_impl` feature set
foo_impl()
}
// This generates a stub for CPUs that support AVX:
#[target_feature(enable = "avx")] unsafe fn foo_avx() { foo_impl() }
// This function returns the best implementation of `foo` depending
// on which target features the host CPU does support at run-time:
fn initialize_global_foo_ptr() -> fn () -> () {
if cfg_feature_enabled!("avx") {
unsafe { foo_avx }
} else if cfg_feature_enabled!("sse4") {
unsafe { foo_sse4 }
} else {
foo_impl // use the default version
}
}
// During binary initialization we can set a global function pointer
// to the best implementation of foo depending on the features that
// the CPU where the binary is running does support:
lazy_static! {
static ref GLOBAL_FOO_PTR: fn() -> () = {
initialize_foo()
};
}
// ^^ note: the ABI of this function pointer is independent of the target features
fn main() {
// Finally, we can use the function pointer to dispatch to the best implementation:
global_foo_ptr();
}
Example 1 (inlining):
Conditional条件付き、条件的 compilation: cfg!(target_feature)
The cfg!(target_feature = "feature_name")
macro allowstrue
if the feature is enabled, and false
otherwise.
In a function annotated with #[target_feature(enable = "feature_name")]
the macro cfg!(target_feature = "feature_name")
expands to true
if the generated code for the function uses the feature (current bug.
Note: how accurate cfg!(target_feature)
can be made is an "Unresolved Question" (see the sectioncfg!(target_feature)
is used in a function that does not support the feature, it should still return true in the cases where the function gets inlined into a context#[inline]
function defined#![cfg(target_feature)]
is used, but not if if cfg!(target_feature)
is used since in this case all branches
Example 3 (conditional compilation):
Example 4 (value of cfg!
within #[target_feature]
):
Run-time実行時の(に) feature detection
Writing safe wrappers around unsafe
functions annotated with #[target_feature]
requires run-time
cfg_feature_enabled!("feature") -> bool-expr
with the following semantics: "if the host hardware on which the current code is running supports the "feature"
, the bool-expr
that cfg_feature_enabled!
expands to has value true
, and false
otherwise.
If the result
Examples of using run-time
If the API of run-time
How We Teach This
There are two parts to this story, the low-level part, and the high-level part.
Example 5 (high-level usage of target features):
note: ifunc
is not part of this RFC, but just an example of what can be built on top of it.
In the high-level part we have the ifunc
function attribute, implemented
#[ifunc("default", "sse4", "avx", "avx2")] //< MAGIC
fn foo() {}
fn main() {
foo(); // dispatches to the best implementation at run-time
#[cfg(target_feature = "sse4")] {
foo(); // dispatches to the sse4 implementation at compile-time
}
}
The following example covers what ifunc
might expand to.
Example 6 (ifunc expansion):
Note that there are many solutions to this problem and they have different trade-offs, but these can be explored in procedural macros. When wrapping unsafe intrinsics, conditional
Example 7 (three-layered approach to target features):
Due to the low-level and high-level nature of these feature we will need two kinds of documentation.
- document文書how to do compile-time and run-time実行時の(に)feature detection using
cfg!(target_feature)
andcfg_feature_enabled!
, - document文書how to use
#[target_feature]
, - document文書how to use all of these together to solve problems like in the examples of this RFC.
For the high-level part we should aim to bring third-party crates implementingifunc!
or similar
Drawbacks
- Obvious increase in language言語complexity.
The main drawback of not solving this issue is that many libraries that require conditional
Alternatives代わりのもの、選択肢
Backend options
An alternative--target-feature
.
Make #[target_feature]
safe
Calling#[target_feature]
on a host that does not support the feature invokes undefined behavior
That is, callingtarget_feature
is a promise from the user to the toolchain and the hardware, that the code will not be reached in a CPU that does not support the feature. LLVM, the assembler, and the hardware all assume that the user will not violate this contract, and there is little that the Rust compiler can do to make this safer:
- The Rust compiler cannot emit a compile-time diagnostic because it cannot know whether the user is going to run the binary2進数in a CPU that supports the features or not.
- A run-time実行時の(に)diagnostic always incurs a run-time実行時の(に)cost, and is only possible iff the absence of a feature can be detected at run-time実行時の(に)(the "Future Extensions" section節of this RFC discusses how to implement実装する"Run-time実行時の(に)diagnostics" to detect this, when possible).
However, the --target-feature/--target-cpu
compiler options allowsunsafe
annotations at all, so the answer to the question "Should #[target_feature]
be safe/unsafe?" is indeed a hard one.
The main differences between #[target_feature]
and --target-feature
/--enable-feature
are the following:
--target-feature/--enable-feature
are "backend options" while#[target_feature]
is part of the language言語--target-feature/--enable-feature
is specified特定する、指定する、規定するby whoever compiles the code, while#[target_feature]
is specified特定する、指定する、規定するby whoever writes the code- compiling safe Rust code for a particular target, and then running the binary2進数on that target, can only produce産出するundefined behaviorふるまいiff
#[target_feature]
is safe.
This RFC chooses that the #[target_feature]
attribute only appliesunsafe fn
s, so that if one compiles safe Rust source
Note that we can always make #[target_feature]
safe in the future without breaking backwards compatibility,#[target_feature]
safe such that the above holds,
Guarantee保証する no segfaults from unsafe
code
Calling#[target_feature]
-annotated function on a platform that does not support it invokes undefined behavior.
This RFC considers
The "Future Extension"s section
Make #[target_feature] + #[inline(always)]
incompatible
This RFC requires the compiler to error when a function marked with both #[target_feature]
and the #[inline(always)]
attribute cannot be inlined in a particular call
While this is technically correct, the compiler must detect when any function (#[inline(always)]
, #[inline]
, generics, ...) is inlined into an incompatible context,#[inline(always)]
does not significantly
Removing run-time実行時の(に) feature detection from this RFC
This RFC adds an API for run-time
The alternative
In particular, the API proposed in this RFC is "stringly-typed" (to make it uniform with the other features being proposed), but arguably a third party crate might want to use an enum
to allow
The main arguments
- it is impossible to write safe wrappers around
#[target_feature]
without it - implementing実装するit requires the
asm!
macro or linking to a C library (or linking to a C wrapper around assembly), - run-time実行時の(に)detection should be kept in sync with the addition追加of new target features,
- the compiler might want to use LLVM's run-time実行時の(に)feature detection which is part of compiler-rt.
The consensus in the internal forums and previous
It might turn out that the people from the future are able to come up with a better API. But in that case we can always deprecate the current API and include the new one in the standard library.
Addingたす full cpuid support to the standard library
The cfg_feature_enable!
macro is designedcfg_target_feature
and #[target_feature]
. However, in the grand scheme of things, run-timecpuid
-like CPU instructions.
Currently at least two great implementations
Unresolved questions
How accurate should cfg!(feature) be?
What happens if the macro cfg!(target_feature = "feature_name")
is used inside a function for which feature_name
is not enabled, but that function gets inlined into a contexttrue
in this case, that is, to be as accurate as possible so that users always get the most efficient
This might result#![cfg(target_feature)]
is used, but not if if cfg!(target_feature)
is used since in this case all branches
We might want to ammend this RFC with more concrete
How do we handle ABI issues with portable vector types?
The ABI of #[target_feature]
functions does not change for all types currently available in stable Rust. However, there are types that we might want to add to the language
The behavior#[target_feature]
for those types should be specified
The following examples showcase some potential problems when calling
Whether we can warn, or hard error at compile-time in these cases remains to be explored.
Example 8 (ABI):
Future Extensions
Mutually exclusive features
In some cases, e.g., when enabling AVX but disabling SSE4 the compiler should probably producethumb_mode
the behavior
Safely inlining #[target_feature]
functions on more contexts
The problem is the following:
If foo_avx2
gets inlined into baz
, optimizations that reorder its instructions
Maybe, one could make cfg_feature_enabled!
a bit magical, so that when it is used in the typical ways the compiler can infer whether inlining is safe, e.g.,
Whether this is worth it or can be done at all is an unresolved question. This RFC does not propose any of this, but leaves the door open for such an extension to be explored and proposed independently in a follow-up RFC.
Run-time実行時の(に) diagnostics
Calling#[target_feature]
-annotated function on a platform that does not support it invokes undefined behavior.panic!
message.
This can be done, for example, by desugaring this:
into this:
This is not required for safety and can be implemented
Disabling features
This RFC does not allow#[target_feature(disable = "feature-list")]
, --disable-feature=feature-list
). Disabling features can result
Acknowledgements
@parched @burntsushi @alexcrichton @est31 @pedrocr @chandlerc @RalfJung @matthieu-m
#[target_feature]
Pull-Request: https://github.com/rust-lang/rust/pull/38079cfg_target_feature
tracking issue: https://github.com/rust-lang/rust/issues/29717