I’ve been playing with AVX lately, and I experienced first-hand that building
a program that uses non-standard instructions with Cargo is not as easy as it
could be. I would like to propose a way to specify required and optional
target features in
Cargo.toml. I don’t know if such a small change actually
requires an RFC, but I am requesting comments on this, so here we go:
- Feature Name: cargo_target_feature
- Start Date: 2016-03-22
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
required-target-features field to
Cargo.toml where libraries can
declare their required target features, and a
target-features field where
top-level projects can declare the target features that they want to enable.
(Target features here refer to the
-C target-feature codegen option,
not to the
[features] section of
Special instructions that require target features to be enabled can often provide a significant perfomance win on modern CPUs. Projects that wish to take advantage of this (e.g. audio and video encoders or decoders, cryptography libraries, scientific computing libraries) should not be harder to compile and use than any other project.
A project can use a target feature in two ways: optional, or required. When a target feature is required, the code relies on the corresponding instructions being available. An example would be a matrix manipulation library where the algorithms are tuned specifically for the AVX instruction set.
It is also possible for projects to take advantage of target features when
they are available and gracefully fall back otherwise. The
#[cfg(target_feature = "...")] attribute can be used for this.
An example would be a vector math library that can use AVX when available,
fall back to SSE if that is available, or fall back to serial operations
otherwise. A different example would be a cryto library that has an AES
implementation based on the AES instructions, and a fallback implementation
in pure Rust.
When compiling, only the top level project knows what system it is going to run on, so the top level project should control which target features are enabled. If one of the dependencies requires a certain target feature, it should not be enabled silently: this might unintendedly make the resulting binary incompatible with some systems. This leads to the following design:
Libraries should declare their required target features in the
[package]. The type of this field is a list of strings. Example:
[package] required-target-features = ["sse", "sse2"]
The top-level project should declare the target features to build with in the
[package]. The type of this field is a list of strings.
Cargo will verify that the union of all
required-target-features in all dependencies is a subset of
target-featuresof the top-level project. If this is not the case, building will fail with an error message indicating which target features must be enabled, and by which crates they are required.
target-featuresdeclared in the top-level project will be passed to the compiler for all crates that need to be compiled. For instance, if in
Cargo.tomlthe following features are enabled:
[package] target-features = ["sse", "sse2"]
Then the compiler will be invoked with:
$ rustc ... -C target-feature=+sse,+sse2 ...
None that I can think of.
RUSTFLAGSsupport just landed. It is possible to set
-C target-feature=+feature. This can even be done from
.cargo/config. However, this mechanism is not intended for required target features of a crate, and it spreads build configuration over two files, instead of keeping it central in
It is possible to call
cargo rustc -- -C target-feature=+feature, but this will only pass the target feature to the top-level project, not to dependencies. This makes e.g. the simd crate completely useless. Furthermore it is not very ergonomic to use.
In the proposed design, instead of failing to build if a required target feature is not declared, it could be enabled implicitly. This will make it easier to build, but it will also make it easier to accidentally produce binaries that are incompatible with some systems.
[package]section feels like the wrong section to put
required-target-featuresunder. Other codegen options are currently under
[profile.*], but target features generally do not differ per profile (you want to debug the same code that fails in release).
Perhaps they should go under
[target.<triple>]? This makes sense because the target features depend on the architecture. But what would putting target features in
[target.cfg(target_feature = "...")]do? And how to indicate that only one target is supported?
required-target-features? This can reduce boilerplate for libraries.
Should there be a way to couple target features to features in the
The current design only allows passing target features as
-C target-feature=+feature. Is there any use case for passing
Should there be a way for crates to advertise their optional target features?
I haven’t contributed anything significant to Cargo before, but I am willing to attempt to implement this.
Feedback would be appreciated!