Skip to content

de-stabilize optimize(speed) - #165592

Open
RalfJung wants to merge 1 commit into
rust-lang:mainfrom
RalfJung:opt-not-speed
Open

RalfJung wants to merge 1 commit into
rust-lang:mainfrom
RalfJung:opt-not-speed

Conversation

@RalfJung

@RalfJung RalfJung commented Nov 7, 2026 •

Copy link
Copy Markdown
Member

View all comments

Reverts parts of #157273.
We don't actually know what this attribute should do (see Zulip). In LLVM, the opt-level is used to configure the entire pass pipeline, it is not possible to overwrite this for a single function. Correspondingly, it currently does very little: it suppresses a global -Copt-level=s/z, but that's it.

Cc @rust-lang/lang
Tracking issue: #165590

@rustbot

rustbot commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred in compiler/rustc_attr_parsing

cc @jdonszelmann, @JonathanBrouwer

@rustbot rustbot added A-attributes Area: Attributes (`#[…]`, `#![…]`) 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. labels Nov 7, 2026
@rustbot

rustbot commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

r? @mu001999

rustbot has assigned @mu001999.
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 79 candidates
  • Random selection from 23 candidates

@rustbot rustbot added the A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. label Nov 7, 2026
@veluca93

veluca93 commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

If I understand correctly that code, it does do something: it omits default_optimisation_attrs when optimizing for speed. That means that optimize(speed) makes LLVM not optimize for size functions with #[optimize(speed)] even if globally configured for optimizing for size.

@RalfJung RalfJung added the beta-nominated Nominated for backporting to the compiler in the beta channel. label Nov 7, 2026
@RalfJung

RalfJung commented Nov 7, 2026 •

Copy link
Copy Markdown
Member Author

Fair, I updated the description.

@traviscross traviscross added T-lang Relevant to the language team I-lang-radar Items that are on lang's radar and will need eventual work or consideration. labels Nov 7, 2026
@traviscross

traviscross commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

I'm OK either way on this, but I'll ask: is this worth reverting? It's just a hint. We're allowed to ignore it.

I feel as though we do know what this is supposed to do.1 To the degree that it's not yet doing that, it seems something we could fix post-stabilization.

Footnotes

  1. Though maybe not how it's supposed to do that, given the way LLVM works currently. ↩

@hanna-kruppe

hanna-kruppe commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

In LLVM, the opt-level is used to configure the entire pass pipeline, it is not possible to overwrite this for a single function. Correspondingly, it currently does very little: it suppresses a global -Copt-level=s/z, but that's it.

To elaborate on this: to some extent it's fine and expected that optimize(speed) only cancels out the global opt-level=s/z because LLVM no longer has separate pipelines for s/z (see llvm/llvm-project#191363), it runs the same pipeline with different defaults for functions that don't have an attribute one way or another. So what we currently do in rustc indeed gives optimize(speed) functions the opt-level=2 experience under opt-level=s/z. However, there's still the distinction between the numeric opt-levels. Which means:

  • With global opt-level = 0, we can run more MIR opt passes on optimize(speed) functions, but LLVM passes won't run.
  • With global opt-level > 0, optimize(speed) also doesn't do anything in LLVM terms. In particular, global opt-level=1 can't be "locally upgraded" to opt-level=2 via the attribute.
  • With global opt-level = s/z, optimize(speed) will affect optimization passes that are in the -O2 pipeline but not enable those that are only in the -O3. There aren't actually that many -O3-exclusive passes currently (e.g., I previously mentioned LoopVectorizer on Zulip, but that one's actually in the -O2 pipeline), but there are some. Also, any passes that are in the -O2 pipeline but "try harder" at -O3 will behave like -O2 under opt-level=s/z + optimize(speed).

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job test-aarch64-gnu-llvm-22-1 failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)

---- [codegen] tests/codegen-llvm/optimize-attr-1.rs#NO-OPT stdout ----
------rustc stdout------------------------------

------rustc stderr------------------------------
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]  --> /checkout/tests/codegen-llvm/optimize-attr-1.rs:33:1
   |
33 | #[optimize(speed)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
   = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
   = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0658`.

------------------------------------------

error in revision `NO-OPT`: compilation failed!
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/codegen-llvm/optimize-attr-1.rs" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2" "--target=aarch64-unknown-linux-gnu" "--cfg" "no_opt" "--check-cfg" "cfg(test,FALSE,no_opt,size_opt,speed_opt)" "-O" "-Cdebug-assertions=no" "-Zcodegen-source-order" "--emit" "llvm-ir" "-C" "prefer-dynamic" "-o" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/codegen-llvm/optimize-attr-1.NO-OPT/optimize-attr-1.ll" "-Znext-solver=coherence" "-A" "internal_features" "-A" "incomplete_features" "-A" "unused_parens" "-A" "unused_braces" "-Crpath" "-Cdebuginfo=0" "--edition=2015" "-Copt-level=0" "-Ccodegen-units=1"
stdout: none
--- stderr -------------------------------
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]  --> /checkout/tests/codegen-llvm/optimize-attr-1.rs:33:1
   |
33 | #[optimize(speed)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
   = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
   = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date
---
---- [codegen] tests/codegen-llvm/optimize-attr-1.rs#NO-OPT stdout end ----
---- [codegen] tests/codegen-llvm/optimize-attr-1.rs#SIZE-OPT stdout ----
------rustc stdout------------------------------

------rustc stderr------------------------------
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]  --> /checkout/tests/codegen-llvm/optimize-attr-1.rs:33:1
   |
33 | #[optimize(speed)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
   = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
   = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date
---
------------------------------------------

error in revision `SIZE-OPT`: compilation failed!
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/codegen-llvm/optimize-attr-1.rs" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2" "--target=aarch64-unknown-linux-gnu" "--cfg" "size_opt" "--check-cfg" "cfg(test,FALSE,no_opt,size_opt,speed_opt)" "-O" "-Cdebug-assertions=no" "-Zcodegen-source-order" "--emit" "llvm-ir" "-C" "prefer-dynamic" "-o" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/codegen-llvm/optimize-attr-1.SIZE-OPT/optimize-attr-1.ll" "-Znext-solver=coherence" "-A" "internal_features" "-A" "incomplete_features" "-A" "unused_parens" "-A" "unused_braces" "-Crpath" "-Cdebuginfo=0" "--edition=2015" "-Copt-level=s" "-Ccodegen-units=1"
stdout: none
--- stderr -------------------------------
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]  --> /checkout/tests/codegen-llvm/optimize-attr-1.rs:33:1
   |
33 | #[optimize(speed)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
   = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
   = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date
---
---- [codegen] tests/codegen-llvm/optimize-attr-1.rs#SIZE-OPT stdout end ----
---- [codegen] tests/codegen-llvm/optimize-attr-1.rs#SPEED-OPT stdout ----
------rustc stdout------------------------------

------rustc stderr------------------------------
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]  --> /checkout/tests/codegen-llvm/optimize-attr-1.rs:33:1
   |
33 | #[optimize(speed)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
   = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
   = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0658`.

------------------------------------------

error in revision `SPEED-OPT`: compilation failed!
status: exit status: 1
command: env -u RUSTC_LOG_COLOR RUSTC_ICE="0" RUST_BACKTRACE="short" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2/bin/rustc" "/checkout/tests/codegen-llvm/optimize-attr-1.rs" "-Zsimulate-remapped-rust-src-base=/rustc/FAKE_PREFIX" "-Ztranslate-remapped-path-to-local-path=no" "-Z" "ignore-directory-in-diagnostics-source-blocks=/cargo" "-Z" "ignore-directory-in-diagnostics-source-blocks=/checkout/vendor" "--sysroot" "/checkout/obj/build/aarch64-unknown-linux-gnu/stage2" "--target=aarch64-unknown-linux-gnu" "--cfg" "speed_opt" "--check-cfg" "cfg(test,FALSE,no_opt,size_opt,speed_opt)" "-O" "-Cdebug-assertions=no" "-Zcodegen-source-order" "--emit" "llvm-ir" "-C" "prefer-dynamic" "-o" "/checkout/obj/build/aarch64-unknown-linux-gnu/test/codegen-llvm/optimize-attr-1.SPEED-OPT/optimize-attr-1.ll" "-Znext-solver=coherence" "-A" "internal_features" "-A" "incomplete_features" "-A" "unused_parens" "-A" "unused_braces" "-Crpath" "-Cdebuginfo=0" "--edition=2015" "-Copt-level=3" "-Ccodegen-units=1"
stdout: none
--- stderr -------------------------------
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]  --> /checkout/tests/codegen-llvm/optimize-attr-1.rs:33:1
   |
33 | #[optimize(speed)]
   | ^^^^^^^^^^^^^^^^^^
   |
   = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
   = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
   = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date

@nikic

nikic commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

Do I understand correctly that you want to destabilize optimize(speed) because it does not "work" at -C opt-level=0? If so, you could destabilize optimize(size) based on the same argument.

At -C opt-level=2/3, optimize(size) allows you to optimize some functions as if you used -C opt-level=z for them. At -C opt-level=s/z, optimize(speed) allows you to optimize some functions as if you used -C opt-level=2 for them. Their behavior is entirely symmetric.

So I don't think this PR makes sense.

@hanna-kruppe

hanna-kruppe commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

@traviscross

I feel as though we do know what this is supposed to do. To the degree that it's not yet doing that, it seems something we could fix post-stabilization.

So, what exactly is it supposed to do that it's not doing today? If there's a conflict between what "we know" it's supposed to do vs. how it actually works, then that should give us pause and prompt us to reconsider. After all, there are good reasons why LLVM works the way it does. Of course the attribute is a hint that is allowed to do nothing, but we do want it to do something that is both useful and actually implementable.

So, for example, if people "know" that putting optimize(speed) on a function is supposed to make its runtime performance similar to opt-level=1/2 builds even with global opt-level=0 (like a fine-grained version of changing opt-level for one crate in Cargo via profile overrides), then I don't think we should stabilize that because it's not clear that this will ever be implementable without very regrettable trade-offs.

@nikic

Do I understand correctly that you want to destabilize optimize(speed) because it does not "work" at -C opt-level=0? If so, you could destabilize optimize(size) based on the same argument.

Per the recent Zulip discussion, the difference is in expectations: some people would like optimize(speed) to be usable for cases where some code is unbearably slow to run at opt-level=0. Like a finer grained equivalent of crate-level opt-level overrides in Cargo profiles. In contrast, I haven't seen anyone notice or complain about optimize(size) doing nothing at opt-level=0. I think that's because nobody directly cares about code size in unoptimized builds, but rather they add optimize(sized) to prevent excessive code size in optimized builds resulting from specific optimization passes going ham.

@RalfJung

RalfJung commented Nov 7, 2026 •

Copy link
Copy Markdown
Member Author

but I'll ask: is this worth reverting? It's just a hint. We're allowed to ignore it.

Yes, we are, but I don't see who we are helping by having an attribute that basically never does what people expect it to do.

Do I understand correctly that you want to destabilize optimize(speed) because it does not "work" at -C opt-level=0?

No, I want to destabilize it because it basically never works.

optimize(size) at least doe something for release builds. We should probably mention in the docs that at the moment, it has basically no effect on opt-level 0.

@traviscross

traviscross commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

At -C opt-level=2/3, optimize(size) allows you to optimize some functions as if you used -C opt-level=z for them. At -C opt-level=s/z, optimize(speed) allows you to optimize some functions as if you used -C opt-level=2 for them. Their behavior is entirely symmetric.

Following @nikic's logic above about symmetry,1 my feeling is that either we should be leaning into the idea that these attributes are just hints, or we should be reverting the entire stabilization.

Footnotes

  1. Regarding expectations, I don't find one direction particularly more surprising than the other here. ↩

@RalfJung

RalfJung commented Nov 7, 2026

Copy link
Copy Markdown
Member Author

I think that's throwing out the baby with the bathwater.

At -C opt-level=s/z, optimize(speed) allows you to optimize some functions as if you used -C opt-level=2 for them.

I thought we'd not have all the right passes in the pipeline for optimize(speed) to do anything useful there?

Also FWIW on mir-opts the behavior is quite different from what you describe.

@hanna-kruppe

Copy link
Copy Markdown
Contributor

No, I want to destabilize it because it basically never works.

optimize(size) at least doe something for release builds.

FWIW I do have projects where “release build” means opt-level=s, and optimize(speed) does something in release builds in those cases. It’s not doing anything in the default release profile, but it’s not “basically never” either. I think the symmetry that @nikic mentions is fine, it gives a useful knob without making an even bigger mess of the pass pipeline and making opt-level=0 builds slower.

But since the implications of this for optimize(speed) are surprising to several people and apparently weren’t fully understood by everyone during the FCP, I guess it does make sense to go back and try to get everyone on the same page about what optimize(speed) actually does. Probably we don’t need to actually agree on which behavior would be most desirable in theory, and probably we should stabilize the actual behavior anyway sooner rather than later, but it’s easier to have the conversation without the time pressure of the stabilization being in beta already.

@rust-log-analyzer

Copy link
Copy Markdown
Collaborator

The job test-x86_64-gnu-tools failed! Check out the build log: (web) (plain enhanced) (plain)

Click to see the possible cause of the failure (guessed by this bot)
---- attributes/codegen.md - Code_generation_attributes::The_ (line 1000) stdout ----
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]    --> attributes/codegen.md:1004:5
     |
1004 |     #[optimize(speed)] || (); // A closure.
     |     ^^^^^^^^^^^^^^^^^^
     |
     = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
     = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
     = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date

error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]    --> attributes/codegen.md:1017:5
     |
1017 |     #[optimize(speed)]
     |     ^^^^^^^^^^^^^^^^^^
     |
     = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
     = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
     = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date
---
---- attributes/codegen.md - Code_generation_attributes::The_ (line 887) stdout ----
error[E0658]: `#[optimize(speed)]` is currently unstable
##[error]   --> attributes/codegen.md:891:1
    |
891 | #[optimize(speed)]
    | ^^^^^^^^^^^^^^^^^^
    |
    = note: see issue #165590 <https://github.com/rust-lang/rust/issues/165590> for more information
    = help: add `#![feature(optimize_speed)]` to the crate attributes to enable
    = note: this compiler was built on 2026-11-07; consider upgrading it if it is out of date

@traviscross traviscross added I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-0 Lang team prioritization drag level 0.https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang. labels Nov 7, 2026
@hanna-kruppe

hanna-kruppe commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

I thought we'd not have all the right passes in the pipeline for optimize(speed) to do anything useful there?

We do have all the opt-level=2 passes (which is almost all of them) in the pipeline at opt-level=s. Although as I mentioned before, they may have other knobs besides the one encoded in optsize/optiminsize. For example, I found a loop that gets vectorized under opt-level=2 but doesn't get vectorized with opt-level=s + optimize(speed): https://rust.godbolt.org/z/c6nzYPKqW

Not sure if that's a problem in LLVM itself (and if so, whether it's already fixed in main) or due to one of the places in rustc that branches on opt-level. #155656 only updated the mapping to LLVM's enum, but I recall that we do a lot more decisions in configuring the pipeline.

@hanna-kruppe

hanna-kruppe commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Oh yeah we do tell LLVM to disable some passes including loop unrolling and vectorization based on rustc's own -Copt-level rather than the LLVM pipeline level.

@RalfJung

RalfJung commented Nov 7, 2026

Copy link
Copy Markdown
Member Author

We do have all the opt-level=2 passes (which is almost all of them) in the pipeline at opt-level=s.

Ah I see, I was not aware of that.

FWIW I do have projects where “release build” means opt-level=s, and optimize(speed) does something in release builds in those cases. It’s not doing anything in the default release profile, but it’s not “basically never” either. I think the symmetry that @nikic mentions is fine, it gives a useful knob without making an even bigger mess of the pass pipeline and making opt-level=0 builds slower.

Hm, that does make sense.

I would however argue it then also makes sense to do something similar for MIR opts, i.e. a -Copt-level=0 run should not suddenly apply the full suite of MIR opts to an #[optimize(speed)] function.

@hanna-kruppe

hanna-kruppe commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

I would however argue it then also makes sense to do something similar for MIR opts, i.e. a -Copt-level=0 run should not suddenly apply the full suite of MIR opts to an #[optimize(speed)] function.

Yeah that would be more consistent. I don't think there's currently much harm in running more mir-opt passes, if it's easy to support and doesn't cost much compile time (especially not for the vast majority of code not annotated optimize(speed)). But we should definitely be ready to run fewer passes if it causes any problems for other work on the MIR pipeline (e.g., I think we don't currently run the MIR inliner there, we shouldn't complicate the code to be able to run it) or if for any reason rustc-perf shows significant compile time improvements from running fewer passes.

@hanna-kruppe

Copy link
Copy Markdown
Contributor

For example, I found a loop that gets vectorized under opt-level=2 but doesn't get vectorized with opt-level=s + optimize(speed): https://rust.godbolt.org/z/c6nzYPKqW

I reported this as #165601. It's not a fundamental problem with what the attribute means, but as long as we have that bug, optimize(speed) pretty useless IMO. Loop unrolling and loop vectorization are among the biggest culprits for "trading a lot of code size for a lot of speed" (along with inlining), and we don't have stable attributes for controlling them (unlike for inlining), so optimize(speed) is mainly useful for opting into those optimizations for the hottest loops of projects that otherwise get built with opt-level=s.

The fact that this wasn't noticed before indicates to me that nobody actually used this attribute in anger yet, which is maybe a bad sign for stabilizing it.

@RalfJung

RalfJung commented Nov 7, 2026

Copy link
Copy Markdown
Member Author

(e.g., I think we don't currently run the MIR inliner there, we shouldn't complicate the code to be able to run it)

I think we should de-complicate the code so that the inliner runs.^^ The current logic is strange.

This branch has not been deployed

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

Labels

A-attributes Area: Attributes (`#[…]`, `#![…]`) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. beta-nominated Nominated for backporting to the compiler in the beta channel. 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. P-lang-drag-0 Lang team prioritization drag level 0.https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang. 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-lang Relevant to the language team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants