Skip to content

Send -fno-lto when linker plugin LTO is not requested to avoid having GCC do LTO when using rustc_codegen_gcc - #159924

Open
antoyo wants to merge 1 commit into
rust-lang:mainfrom
antoyo:fix/fno-lto
Open

antoyo wants to merge 1 commit into
rust-lang:mainfrom
antoyo:fix/fno-lto

Conversation

@antoyo

@antoyo antoyo commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor

@rustbot rustbot added A-run-make Area: port run-make Makefiles to rmake.rs 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 Jul 25, 2026
@rustbot

rustbot commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

r? @fee1-dead

rustbot has assigned @fee1-dead.
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 74 candidates
  • Random selection from 17 candidates

@bjorn3

bjorn3 commented Jul 25, 2026 •

Copy link
Copy Markdown
Member

Thinking about this again, this would cause a compilation failure if there is any C code compiled with -flto, right? Older versions of cc did that whenever rust LTO is enabled even though no LTO between C and Rust happens in that case. Only LTO between Rust crates and separately between C objects. And as it so happens rustc is currently stuck on said older version of the cc crate as disabling LTO for Jemalloc is a perf regression.

@antoyo

antoyo commented Jul 25, 2026

Copy link
Copy Markdown
Contributor Author

Thinking about this again, this would cause a compilation failure if there is any C code compiled with -flto, right?

You mean objects files that are not fat compiled with GCC?
Is this a case this is currently supported?
That would not work either when using LLD which is the default, no?
I would assume the way to support this would be to explicitly use a linker plugin.
What are your thoughts on this?

@bjorn3

bjorn3 commented Jul 25, 2026

Copy link
Copy Markdown
Member

That would not work either when using LLD which is the default, no?

It should currently work without extra configuration when using Clang as C compiler and either Clang as linker driver or LLD as linker though.

@antoyo

antoyo commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor Author

It should currently work without extra configuration when using Clang as C compiler and either Clang as linker driver or LLD as linker though.

So, we would need to only emit -fno-lto when gcc is used as the linker, right?
Is there a way to detect this?
If not, would using self.codegen_backend == "gcc" like we did in push_linker_plugin_lto_args be OK?

@fee1-dead

Copy link
Copy Markdown
Member

r? bjorn3

@rustbot rustbot assigned bjorn3 and unassigned fee1-dead Aug 1, 2026
@rust-log-analyzer

This comment has been minimized.

JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 8, 2026
…r=Kobzol

Make `run-make` testsuite work with other codegen backend than LLVM

Needed for rust-lang#159924.

Currently, we always run `run-make` testsuite with the codegen backend rustc was compiled with. However, in CI it's compiled with LLVM, so when we want to test with GCC (with `--test-codegen-backend`), it compiles `rmake.rs` with the GCC backend, but when running the test, it doesn't use the GCC backend since it just calls `rustc`. So to get around that, I now pass the codegen backend through the environment and set it in the `rustc` function of `run_make_support`.

To be noted that for now it's only for the `rustc` function, no other command uses it. Should I extend it right away for all commands (well, likely only `cargo`) or just `rustc` for now is enough?

r? @jieyouxu
Zalathar added a commit to Zalathar/rust that referenced this pull request Sep 9, 2026
…r=Kobzol

Make `run-make` testsuite work with other codegen backend than LLVM

Needed for rust-lang#159924.

Currently, we always run `run-make` testsuite with the codegen backend rustc was compiled with. However, in CI it's compiled with LLVM, so when we want to test with GCC (with `--test-codegen-backend`), it compiles `rmake.rs` with the GCC backend, but when running the test, it doesn't use the GCC backend since it just calls `rustc`. So to get around that, I now pass the codegen backend through the environment and set it in the `rustc` function of `run_make_support`.

To be noted that for now it's only for the `rustc` function, no other command uses it. Should I extend it right away for all commands (well, likely only `cargo`) or just `rustc` for now is enough?

r? @jieyouxu
mu001999 added a commit to mu001999/rust that referenced this pull request Sep 9, 2026
…r=Kobzol

Make `run-make` testsuite work with other codegen backend than LLVM

Needed for rust-lang#159924.

Currently, we always run `run-make` testsuite with the codegen backend rustc was compiled with. However, in CI it's compiled with LLVM, so when we want to test with GCC (with `--test-codegen-backend`), it compiles `rmake.rs` with the GCC backend, but when running the test, it doesn't use the GCC backend since it just calls `rustc`. So to get around that, I now pass the codegen backend through the environment and set it in the `rustc` function of `run_make_support`.

To be noted that for now it's only for the `rustc` function, no other command uses it. Should I extend it right away for all commands (well, likely only `cargo`) or just `rustc` for now is enough?

r? @jieyouxu
rust-bors Bot pushed a commit that referenced this pull request Sep 9, 2026
Rollup merge of #162482 - GuillaumeGomez:run-make-backends, r=Kobzol

Make `run-make` testsuite work with other codegen backend than LLVM

Needed for #159924.

Currently, we always run `run-make` testsuite with the codegen backend rustc was compiled with. However, in CI it's compiled with LLVM, so when we want to test with GCC (with `--test-codegen-backend`), it compiles `rmake.rs` with the GCC backend, but when running the test, it doesn't use the GCC backend since it just calls `rustc`. So to get around that, I now pass the codegen backend through the environment and set it in the `rustc` function of `run_make_support`.

To be noted that for now it's only for the `rustc` function, no other command uses it. Should I extend it right away for all commands (well, likely only `cargo`) or just `rustc` for now is enough?

r? @jieyouxu
@GuillaumeGomez

Copy link
Copy Markdown
Member

#162482 got merged, so after a rebase, this PR should be ready to go.

@rustbot

rustbot commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@rust-log-analyzer

This comment has been minimized.

@@ -1,4 +1,5 @@
//@ ignore-cross-compile (need to run fake linker)
//@ ignore-backends: gcc

@antoyo antoyo Sep 9, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Not sure if there would be a way to make this test "generic" on whether the flag -fno-lto is present or not, but I guess it's not super important anyway.

View changes since the review

@antoyo

antoyo commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

The test now passes.
@bjorn3: This is ready for review.

@mati865

mati865 commented Sep 22, 2026

Copy link
Copy Markdown
Member

Thinking about this again, this would cause a compilation failure if there is any C code compiled with -flto, right?

Yes.

You mean objects files that are not fat compiled with GCC?
Is this a case this is currently supported?

This is the default mode for GCC/Clang and it should work as long as you don't explicitly pass -fno-lto. Fat objects require additional argument to be produced: -ffat-lto-objects.

That would not work either when using LLD which is the default, no?

No, LLD doesn't handle GIMPLE bitcode at all (it doesn't even support the plugins).
I'm not sure if LLD is even a good default for GCC backend.

It should currently work without extra configuration when using Clang as C compiler and either Clang as linker driver or LLD as linker though.

Only when the host Clang version that produced the objects matches LLD version (by default the one shipped with particular Rust version). Also, when using Clang + any linker other than LLD -flto must be provided, otherwise Clang won't add linker plugin.
Since GCC is typically "the default" C/C++ compiler on Linux, in the great majority of the cases this won't work right now.

So, we would need to only emit -fno-lto when gcc is used as the linker, right?

For Clang it will make no difference since (-no)-flto only affects whether the compiler passes the plugin. So, we have:

  • GCC/Clang + LLD - (-no)-flto makes no difference as linker plugins are not used by LLD
  • GCC + non-LLD - implicit GCC LTO plugin can be disabled with -no-flto
  • Clang + non-LLD - no implicit plugin, -no-flto has no effect

Codegen options will likely need to be updated to reflect that: https://doc.rust-lang.org/rustc/codegen-options/index.html#linker-plugin-lto


One thing here I'm failing to understand is why should GCC backend differ from LLVM one here. Since libstd produced by LLVM backend is a fat object, LLD probably does some kind of LTO on it?

@antoyo

antoyo commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor Author

One thing here I'm failing to understand is why should GCC backend differ from LLVM one here.

GCC needs additional files to do LTO (libgccjit.so is not enough) which are not distributed/built in the Rust repo (neither by rustup).

Also, is cg_llvm really automatically doing LTO when building a program that depends on the std because the std is a fat object (even when not asked by the user)?
I'm asking because another reason I wanted this -fno-lto flag being passed is that it makes the compilation time of a simple "Hello, world!" program compiled by cg_gcc go to 30 seconds/1 minute.

@bjorn3

bjorn3 commented Sep 22, 2026

Copy link
Copy Markdown
Member

As I understand it, in the LLVM world the embedded bitcode of fat objects is ignored by the linker unless -flto is passed to clang.

@antoyo

antoyo commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

As I understand it, in the LLVM world the embedded bitcode of fat objects is ignored by the linker unless -flto is passed to clang.

Even for Rust? I thought you said otherwise above in this thread.

@mati865

mati865 commented Sep 23, 2026

Copy link
Copy Markdown
Member

Okay, those are pretty compelling arguments for disabling plugin based LTO with GCC backend.

I made small experiment with C code and LLD will only use bitcode from fat objects if given --ffat-lto-objects, which aligns with its --help. So, only when Clang was called with -flto -ffat-lto-objects.

With Rust the situation might be different because Clang puts bitcode in .llvm.lto, whereas rustc puts it in .llvmbc.

@bjorn3

bjorn3 commented Sep 23, 2026

Copy link
Copy Markdown
Member

With Rust the situation might be different because Clang puts bitcode in .llvm.lto, whereas rustc puts it in .llvmbc.

According to https://llvm.org/docs/BitCodeFormat.html#native-object-file-wrapper-format .llvmbc can also be produced by LLVM, but turns out the linker plugin will indeed ignore it. If I understand correctly that would mean -Clinker-plugin-lto would be ignoring the standard library entirely. cc @nikic

@antoyo

antoyo commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

It should currently work without extra configuration when using Clang as C compiler and either Clang as linker driver or LLD as linker though.

Just to make sure I understand, this is not actually the case? Would using clang without -Clinker-plugin-lto do any LTO at all when there are fat objects?

@bjorn3

bjorn3 commented Sep 23, 2026

Copy link
Copy Markdown
Member

I would have expected it to do LTO for C code that is compiled with -flto but without -ffat-lto-objects. I just checked it and it is the case when lld is used, not when any other linker is used with clang.

; echo 'int main() { return 0; }' | clang -flto -x c -c - -o foo.o
; clang foo.o -o foo
foo.o: file not recognized: file format not recognized
clang: error: linker command failed with exit code 1 (use -v to see invocation)
; clang foo.o -o foo -flto
; clang foo.o -o foo -fuse-ld=lld
; ./foo

@antoyo

antoyo commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor Author

I would have expected it to do LTO for C code that is compiled with -flto but without -ffat-lto-objects.

But does Rust send -flto to clang by default?
The difference here is that GCC (used as the linker, when sent object files) will do LTO even without -flto if some object files contain the GCC IR.

@bjorn3

bjorn3 commented Sep 23, 2026

Copy link
Copy Markdown
Member

But does Rust send -flto to clang by default?

No, only when using -Clinker-plugin-lto.

The difference here is that GCC (used as the linker, when sent object files) will do LTO even without -flto if some object files contain the GCC IR.

lld does do LTO without -flto when object files only contain bitcode (but not for -ffat-lto-objects). Clang doesn't for other linkers though.

@antoyo

antoyo commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Is there anything we should change in this PR or is it good to merge?

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-run-make Area: port run-make Makefiles to rmake.rs 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.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants