Skip to content

Stabilize optimize attribute#157273

Open
veluca93 wants to merge 1 commit into
rust-lang:mainfrom
veluca93:optimize-attribte
Open

Stabilize optimize attribute#157273
veluca93 wants to merge 1 commit into
rust-lang:mainfrom
veluca93:optimize-attribte

Conversation

@veluca93

@veluca93 veluca93 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

View all comments

This commit stabilizes the #[optimize] attribute, which allows for more fine-grained control of optimization levels.

Stabilization report

Summary

Tracking issue: #54882
Reference PRs: rust-lang/reference#2278

cc @rust-lang/lang @rust-lang/lang-advisors @rust-lang/t-compiler

What is stabilized

// Instructs the optimization pipeline to prioritize smaller code size over speed.
#[optimize(size)]
pub fn binary_size_sensitive() {
    // ...
}

// Instructs the optimization pipeline to prioritize execution speed over code size.
#[optimize(speed)]
pub fn performance_critical() {
    // ...
}

// Disables optimizations entirely for this item.
#[optimize(none)]
pub fn debug_only() {
    // ...
}

What isn't stabilized

We might want to allow specifying a per-function optimization level (i.e. #[optimize(level = 3)]). To my understanding, backend support for this is not quite there yet (and this - or other - extensions have consequently not been implemented yet).

Applying the attribute on a module level is also not implemented yet.

Design

Reference

RFC history

Answers to unresolved question

  • Should we also implement optimize(always)? optimize(level=x)?

Not yet.

  • Should there be any way to specify what global optimization for speed level is used in conjunction with the optimization for speed option (e.g. -Copt-level=s3 could be equivalent to -Copt-level=3 and #[optimize(size)] on the crate item).

Not yet.

Post-RFC changes

The optimize(none) variant was added. The variants above were discussed here: #54882 (comment)

Key points

Nightly uses

The standard library itself uses this attribute in a few places

Implementation

Major part

Coverage

Tool changes

Trivial changes to rust-analyzer to support the new attribute as an inert attribute: rust-lang/rust-analyzer#22511

Type system, opsem

Breaks the AM?

As far as the AM is concerned, this attribute doesn't exist.

Acknowledgments

Most of the work was not done by me, I'm just writing the stabilization report :-)

@nagisa did the initial implementation, @clubby789 implemented optimize(none) and fixed the attribute being allowed in too many places.

@rustbot

rustbot commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

rust-analyzer is developed in its own repository. If possible, consider making this change to rust-lang/rust-analyzer instead.

cc @rust-lang/rust-analyzer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. T-rust-analyzer Relevant to the rust-analyzer team, which will review and decide on the PR/issue. labels Jun 1, 2026
@rustbot

rustbot commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

r? @JohnTitor

rustbot has assigned @JohnTitor.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 17 candidates

@rustbot

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@veluca93 veluca93 force-pushed the optimize-attribte branch from deb33c2 to 1de7123 Compare June 1, 2026 20:28
@rustbot

This comment has been minimized.

@veluca93 veluca93 force-pushed the optimize-attribte branch from 1de7123 to 56db805 Compare June 1, 2026 20:30
@veluca93 veluca93 changed the title stabilize optimize attribute (size, speed, and none) stabilize optimize attribute Jun 1, 2026
@tgross35

tgross35 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Given the interactions here, I expect this will need a lang+compiler FCP

@rustbot label +I-lang-nominated +I-compiler-nominated

See some recent discussion about this feature on Zulip #t-lang > status of #[optimize] attribute

@rustbot rustbot added I-compiler-nominated Nominated for discussion during a compiler team meeting. I-lang-nominated Nominated for discussion during a lang team meeting. labels Jun 1, 2026
@tgross35 tgross35 added needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. T-lang Relevant to the language team and removed T-rust-analyzer Relevant to the rust-analyzer team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Jun 1, 2026
@rust-log-analyzer

This comment has been minimized.

@JohnTitor JohnTitor added S-blocked Status: Blocked on something else such as an RFC or other implementation work. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 1, 2026
@JohnTitor

Copy link
Copy Markdown
Member

Marking as S-blocked given the current status.

@veluca93 veluca93 force-pushed the optimize-attribte branch from 56db805 to 680ec3c Compare June 1, 2026 22:03
@rustbot

rustbot commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

These commits modify tests/rustdoc-json.
rustdoc-json is a public (but unstable) interface.

Please ensure that if you've changed the output:

  • It's intentional.
  • The FORMAT_VERSION in src/librustdoc-json-types is bumped if necessary.

cc @aDotInTheVoid, @obi1kenobi

@rustbot rustbot added the A-rustdoc-json Area: Rustdoc JSON backend label Jun 1, 2026
@obi1kenobi

Copy link
Copy Markdown
Member

With my cargo-semver-checks hat on: do we expect this attribute to have SemVer implications?

For example, can applying or removing this attribute on an item influence the contexts in which that item may be used without compile errors or other changes that would be considered public-API-breaking?

@veluca93

veluca93 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author

With my cargo-semver-checks hat on: do we expect this attribute to have SemVer implications?

For example, can applying or removing this attribute on an item influence the contexts in which that item may be used without compile errors or other changes that would be considered public-API-breaking?

I don't believe this is possible - the attribute should only influence how the compiler optimizes functions, and compiler optimizations should not have any visible effects.

Comment thread tests/ui/feature-gates/feature-gate-optimize_attribute.stderr Outdated

@jieyouxu jieyouxu Jun 2, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussion: hm, I wonder if this attribute deserves an entry in the rustc book (a bit like lint levels), because while sure there's the Reference PR, but this is more like a compiler knob?

View changes since the review

@rustbot rustbot removed the S-waiting-on-concerns Status: Awaiting concerns to be addressed by the author label Jun 11, 2026
@traviscross

Copy link
Copy Markdown
Contributor

The attribute can be written as {#[optimize(size)] |x| x} or similar, but that seems to have no effect as far as I can tell. So that probably requires some fixing.

FWIW, specifically for closures, I think we should at least make an optimize attribute applied to a closure do something if the closure is not inlined...

As best I can tell, optimize on closures does have effect and seems to work in the way I'd expect (i.e., in the same way as inline). E.g., Godbolt link.

@veluca93: Are we looking at the same thing?

@veluca93

Copy link
Copy Markdown
Contributor Author

The attribute can be written as {#[optimize(size)] |x| x} or similar, but that seems to have no effect as far as I can tell. So that probably requires some fixing.

FWIW, specifically for closures, I think we should at least make an optimize attribute applied to a closure do something if the closure is not inlined...

As best I can tell, optimize on closures does have effect and seems to work in the way I'd expect (i.e., in the same way as inline). E.g., Godbolt link.

@veluca93: Are we looking at the same thing?

I think we are looking at similar things, at least - I tried (size) instead of (none) and its behaviour seems a bit less predictable, see https://rust.godbolt.org/z/jGjc51PKn :-)

@traviscross

Copy link
Copy Markdown
Contributor

I think we are looking at similar things, at least - I tried (size) instead of (none) and its behaviour seems a bit less predictable, see https://rust.godbolt.org/z/jGjc51PKn :-)

What's important here, as best I can tell, is the coercion to a function pointer. Due to that, we're calling the call_once shim. The closure body then gets inlined into that shim which (currently) doesn't inherit the optimize attribute applied to the closure. Setting #[inline(never)] on the closure or calling the closure directly (without coercion to a function pointer) causes the body to stay rolled. See: Godbolt link.

This seems a QoI matter. @veluca93, do you want to look into whether optimize should be inherited by the shim from the closure, or would you prefer to move forward and look into this later?

@veluca93

Copy link
Copy Markdown
Contributor Author

I think we are looking at similar things, at least - I tried (size) instead of (none) and its behaviour seems a bit less predictable, see https://rust.godbolt.org/z/jGjc51PKn :-)

What's important here, as best I can tell, is the coercion to a function pointer. Due to that, we're calling the call_once shim. The closure body then gets inlined into that shim which (currently) doesn't inherit the optimize attribute applied to the closure. Setting #[inline(never)] on the closure or calling the closure directly (without coercion to a function pointer) causes the body to stay rolled. See: Godbolt link.

This seems a QoI matter. @veluca93, do you want to look into whether optimize should be inherited by the shim from the closure, or would you prefer to move forward and look into this later?

#157802 ought to fix the problem (I think, not 100% sure that the approach is right).

If we decide that we are OK with optimize(size) mapping to MinSize, and with closures not inheriting the function's optimize attributes, I believe all the issues are resolved, or am I missing something?

@traviscross traviscross added the needs-reference-pr This language change needs an approved Reference PR to proceed. label Jun 12, 2026
@traviscross

Copy link
Copy Markdown
Contributor

Thanks @veluca93 for pushing forward this work. I propose that we accept this stabilization.

(If we're to accept #157802 or a similar PR, we'll ensure that lands before this PR is merged.)

@rfcbot fcp merge lang,compiler

@rust-rfcbot

rust-rfcbot commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

@traviscross has proposed to merge this. The next step is review by the rest of the tagged team members:

Concerns:

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jun 12, 2026
@traviscross traviscross changed the title stabilize optimize attribute Stabilize optimize attribute Jun 12, 2026
@mejrs

mejrs commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Also, can we confirm whether we can write the attribute in a fashion that applies to closures?

Yes, this PR will stabilize the following:

#![feature(optimize_attribute)]

fn main() {
    f(
        #[optimize(size)] || { },
    );
}

fn f(_: impl Fn()) {}

Note the absence of #![feature(stmt_expr_attributes)]. See also #127436 (comment) for more relevant discussion.

samueltardieu added a commit to samueltardieu/rust that referenced this pull request Jun 12, 2026
…mejrs,wesleywiser

Ensure that optimize attributes on closures are inherited by the shim.

Tracking issue: rust-lang#54882
Stabilization PR: rust-lang#157273
@tmandry

tmandry commented Jun 13, 2026

Copy link
Copy Markdown
Member

I think it's weird for this attribute not to apply to nested functions, and I finally put my finger on why.

Optimization is a knob you usually set at the level of an entire compilation unit, and in fact, even an entire project. This attribute is great as a way of making that knob more fine-grained. But that implies it should work on modules. In other words, it would be surprising if there was a discontinuity here:

  1. Entire project: opt-level in cargo config
  2. Compilation unit (crate): -C opt-level to compiler
  3. Module: ???
  4. Function: #[optimize]

If we accept that #[optimize] should work on a module, we should also apply the attribute to inner functions and closures.

In contrast, there is no equivalent of inline(always) at the whole-program level (thank goodness). Practically speaking, the inline attribute only makes sense at the level of an individual function. I'm sure there are special llvm flags to disable inlining globally and set the inline threshold, but they are not commonly used.

jhpratt added a commit to jhpratt/rust that referenced this pull request Jun 13, 2026
…mejrs,wesleywiser

Ensure that optimize attributes on closures are inherited by the shim.

Tracking issue: rust-lang#54882
Stabilization PR: rust-lang#157273
jhpratt added a commit to jhpratt/rust that referenced this pull request Jun 13, 2026
…mejrs,wesleywiser

Ensure that optimize attributes on closures are inherited by the shim.

Tracking issue: rust-lang#54882
Stabilization PR: rust-lang#157273
jhpratt added a commit to jhpratt/rust that referenced this pull request Jun 13, 2026
…mejrs,wesleywiser

Ensure that optimize attributes on closures are inherited by the shim.

Tracking issue: rust-lang#54882
Stabilization PR: rust-lang#157273
@veluca93

Copy link
Copy Markdown
Contributor Author

I think it's weird for this attribute not to apply to nested functions, and I finally put my finger on why.

Optimization is a knob you usually set at the level of an entire compilation unit, and in fact, even an entire project. This attribute is great as a way of making that knob more fine-grained. But that implies it should work on modules. In other words, it would be surprising if there was a discontinuity here:

  1. Entire project: opt-level in cargo config
  2. Compilation unit (crate): -C opt-level to compiler
  3. Module: ???
  4. Function: #[optimize]

If we accept that #[optimize] should work on a module, we should also apply the attribute to inner functions and closures.

In contrast, there is no equivalent of inline(always) at the whole-program level (thank goodness). Practically speaking, the inline attribute only makes sense at the level of an individual function. I'm sure there are special llvm flags to disable inlining globally and set the inline threshold, but they are not commonly used.

The intended semantics would be that:

  • by default, inner functions (including closures) inherit the optimize attribute from the outer functions
  • unless they have an optimize attribute of their own, which takes precedence

Is that correct? If so, I can make a PR for that once #157802 is merged.

This naturally extends to allowing the optimize attribute on modules, but we can leave that to a future extension IMO.

rust-timer added a commit that referenced this pull request Jun 13, 2026
Rollup merge of #157802 - veluca93:optimize-attr-closure, r=mejrs,wesleywiser

Ensure that optimize attributes on closures are inherited by the shim.

Tracking issue: #54882
Stabilization PR: #157273
@traviscross

Copy link
Copy Markdown
Contributor

Practically speaking, the inline attribute only makes sense at the level of an individual function.

I've wanted inline(never) to apply to regions of code for reasons similar to why one might want that for optimize(none). And inline also often makes sense to apply in a consistent way to many functions. I ran across a case of that just the other day in #137598 (comment).

Optimization is a knob you usually set at the level of an entire compilation unit, and in fact, even an entire project. This attribute is great as a way of making that knob more fine-grained. But that implies it should work on modules. In other words, it would be surprising if there was a discontinuity here...

Would you feel the same way if we had attributes to control loop unrolling or vectorization (i.e., some of the underlying things controlled by an optimize(…) attribute along with inlining)? What about for instruction_set?

While I understand the UX intuition that you mention (i.e., that some knobs feel more like compilation-unit ones), when adding attributes to control those, it seems better to me, as a language matter, for our codegen attributes to apply to items in a consistent way rather than having them differ based on those intuitions (which may vary between people).

If we want to support automatic application to nested items, I'd suggest we do that (later) as an orthogonal axis in a syntactically distinct way. As one comparable, that's what GCC does:

// Compile compilation unit with `-O2`.

static int helper(int x) { return x * 7 + 3; }

__attribute__((optimize("O0")))
int with_attribute(int x) {
    int attr_nested(int y) { return helper(y) + 1; } // Still `-O2`.
    return attr_nested(x);
}

#pragma GCC push_options
#pragma GCC optimize ("O0")
int in_region(int x) {
    int region_nested(int y) { return helper(y) + 2; } // This is `-O0`.
    return region_nested(x);
}
#pragma GCC pop_options

Godbolt link.

@petrochenkov

Copy link
Copy Markdown
Contributor

@rfcbot reviewed
(In assumption that it doesn't apply to nested items.)

@nnethercote

Copy link
Copy Markdown
Contributor

This seems fine to me, but one thing did surprise me:

Looking at the code I see that #[optimize(speed)] is a no-op. Why is that?

@veluca93

Copy link
Copy Markdown
Contributor Author

This seems fine to me, but one thing did surprise me:

Looking at the code I see that #[optimize(speed)] is a no-op. Why is that?

I don't personally know, perhaps @nagisa would? (as - I believe - the person to implement that part)

@nagisa

nagisa commented Jun 18, 2026

Copy link
Copy Markdown
Member

There isn't a way to tell LLVM that you want to optimize a single function for speed; or at least there wasn't. My memory is that I had planned to implement this as setting -Copt-level=X crate-wide and then annotating every other function with either optsize or optnone depending on what the original optimization level was. Turned out to be a relatively non-trivial task.

EDIT: though now with the improvements on LLVM side that Nikita mentioned maybe we can do tell LLVM that we want speed pretty much unconditionally and then mark all the functions not otherwise annotated with an #[optimize(...)] with optnone/optsize as needed to implement -Copt-size=0/s/z.

@nnethercote

Copy link
Copy Markdown
Contributor

There isn't a way to tell LLVM that you want to optimize a single function for speed

Whatever ends up happening, a comment on that empty match arm would be helpful.

@tmandry

tmandry commented Jun 19, 2026

Copy link
Copy Markdown
Member

We shouldn't stabilize #[optimize(speed)] if it does nothing in our primary backend.

@rfcbot concern optimize-speed-does-nothing

@veluca93

Copy link
Copy Markdown
Contributor Author

We shouldn't stabilize #[optimize(speed)] if it does nothing in our primary backend.

@rfcbot concern optimize-speed-does-nothing

Agreed. @nikic, is there a way to get optimize(speed) to work? How does clang handle GCC-style push-option attributes?

My two cents: it'd be nice to stabilize optimize(speed) if LLVM can easily support it, but if it can't, then I think we should go forward with size and none.

@nikic

nikic commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

optimize(speed) is supported (or at least it's supposed to be, I haven't verified). The way it currently works is by raising the global optimization level to O2, and not annotating the optimize(speed) functions with minsize. In LLVM 23, the global optimization level will always be O2 by default, which will avoid any kind of weird behavior where using optimize(speed) on one function could impact some other code due to the change to the global optimization level.

tl;dr optimize(speed) is the default from the LLVM perspective, so the implementation of it is to just not add attributes to optimize for size.

@nikic

nikic commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

See

providers.backend_optimization_level = |tcx, cratenum| {
let for_speed = match tcx.sess.opts.optimize {
// If globally no optimisation is done, #[optimize] has no effect.
//
// This is done because if we ended up "upgrading" to `-O2` here, we’d populate the
// pass manager and it is likely that some module-wide passes (such as inliner or
// cross-function constant propagation) would ignore the `optnone` annotation we put
// on the functions, thus necessarily involving these functions into optimisations.
config::OptLevel::No => return config::OptLevel::No,
// If globally optimise-speed is already specified, just use that level.
config::OptLevel::Less => return config::OptLevel::Less,
config::OptLevel::More => return config::OptLevel::More,
config::OptLevel::Aggressive => return config::OptLevel::Aggressive,
// If globally optimize-for-size has been requested, use -O2 instead (if optimize(size)
// are present).
config::OptLevel::Size => config::OptLevel::More,
config::OptLevel::SizeMin => config::OptLevel::More,
};
for the relevant code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-attributes Area: Attributes (`#[…]`, `#![…]`) A-rustdoc-json Area: Rustdoc JSON backend disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. I-lang-nominated Nominated for discussion during a lang team meeting. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-fcp This change is insta-stable, or significant enough to need a team FCP to proceed. needs-reference-pr This language change needs an approved Reference PR to proceed. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. S-blocked Status: Blocked on something else such as an RFC or other implementation work. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.