- Feature Name: optimise_attr
- Start Date: 2018-03-26
- RFC PR: (leave this empty)
- Rust Issue: (leave this empty)
Summary
This RFC introduces the #[optimise]
attribute, specifically #[optimise(size)]
and
#[optimise(no)]
, which allow controlling optimisation level on a per-item basis.
Motivation
Currently, rustc has only a small number of optimisation options that apply globally to the crate. With LTO and RLIB-only crates, it is likely, that a whole-program optimisation level would remain. This is a troublesome trend, as it removes one of the more important knobs for the size-awaare applications such as embedded.
For those applications it is critical that they are able to specify certain pieces of code to optimise in a different manner (e.g. without unrolling the loops) so that the code is able to satisfy the size constraints.
In C-world this is very easy to achieve by simply compiling an object without optimisations. In Rust world where Cargo decides your build commands for you and the compiler flags are per-crate, such approach is less feasible.
Guide-level explanation
Sometimes, optimisations are a tradeoff between execution time and the code size. Some optimisations, such as loop unrolling increase code size many times on average (compared to original function size).
#[optimise(size)]
fn banana() {
// code
}
Will instruct rustc to consider this tradeoff more carefully and avoid optimising in a way that would result in larger code rather than a smaller one. It may also have effect on what instructions are selected to appear in the final binary.
Note that #[optimise(size)]
is a hint, rather than a hard requirement and compiler may still,
while optimising, take decisions that increase function size.
In addition a way to entiely disable optimisations for a function is provided:
#[optimise(no)] // implies #[inline(never)]
fn banana() -> i32 {
2 + 2
}
Would prevent optimisations from running on the function banana
only, resulting in an explicit
instruction adding the two and two together, rather than constant-folded 4:
; x86 assembly
banana:
movl $2, %eax
addl $2, %eax
retq
Note, that some cross-function optimisations may still be able to make decisions depending on the
implementation of banana
.
Reference-level explanation
optimise(no)
The #[optimise(no)]
attribute applied to a function definition will inhibit most of the
optimisations, with exception of cross-function ones, that would otherwise be applied to the
function.
This attribute implies #[inline(never)]
and specifying any other inline attribute in conjunction
with #[optimise(no)]
will cause a compilation error.
In context of MIR optimisations, the optimisation pass would skip over functions annotated with this attribute.
optimise(size)
The #[optimise(size)]
attribute applied to a function definition will instruct the optimisation
engine to avoid applying optimisations that could result in a size increase and macine code
generator to generate code that’s smaller rather than larger.
Note that the optimise(size)
attribute is just a hint and is not guaranteed to result in any
different or smaller code.
Drawbacks
- Not all of the alternative codegen backends may be able to express such a request, hence the
“this is an optimisation hint” note on the
#[optimise(size)]
attribute. RFC mandates support for#[optimise(no)]
, so a backend that does not support disabling optimisations on a per-function basis would end up not being full featured.
Rationale and alternatives
Proposed is a very semantic solution (describes the desired result, instead of behaviour) to the problem of needing to sometimes inhibit some of the trade-off optimisations such as loop unrolling.
Alternative, of course, would be to add attributes controlling such optimisations, such as
#[unroll(no)]
on top of a a loop statement. There’s already precedent for this in the #[inline]
annotations.
optimize
instead of optimise
… or both?
Prior art
- LLVM:
optsize
,optnone
,minsize
function attributes (exposed in Clang in some way); - GCC: attribute((optimize)) function attribute which allows setting the optimisation level and
using certain(?)
-f
flags for each function; - IAR: Optimisations have a checkbox for “No size constraints”, which allows compiler to go out of its way to optimise without considering the size tradeoff.
Unresolved questions
- N/A?