Stabilize linker-plugin based LTO (aka cross-language LTO) by michaelwoerister · Pull Request #58057 · rust-lang/rust (original) (raw)
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
[ Show hidden characters]({{ revealButtonHref }})
This PR stabilizes linker plugin based LTO, also known as "cross-language LTO" because it allows for doing inlining and other optimizations across language boundaries in mixed Rust/C/C++ projects.
As described in the tracking issue, it works by making rustc emit LLVM bitcode instead of machine code, the same as clang does. A linker with the proper plugin (like LLD) can then run (Thin)LTO across all modules.
The feature has been implemented over a number of pull requests and there are various codegen and run-make tests that make sure that it keeps working.
It also works for building big projects like Firefox.
The PR makes the feature available under the -C linker-plugin-lto flag. As discussed in the tracking issue it is not cross-language specific and also not LLD specific. -C linker-plugin-lto is descriptive of what it does. If someone has a better name, let me know :)
slanterns, lovesegfault, mark-i-m, LifeIsStrange, green-s, ErichDonGubler, tux3, Kerollmops, 0x7CFE, pdavydov108, and 7 more reacted with heart emoji mmalinin, ejpcmac, runiq, and Be-ing reacted with rocket emoji
r? @estebank
(rust_highfive has picked a reviewer for you, use r? to override)
As discussed in the tracking issue, we'll ask @rust-lang/compiler to sign off on it.
@rfcbot fcp merge
Looks reasonable to me! Do you know if the way to use this is written down somewhere? I presume it's almost "just set RUSTFLAGS with this flag" but we may want to mention compatible linkers and such? (also things like LLVM version compatibility, etc)
☔ The latest upstream changes (presumably #57937) made this pull request unmergeable. Please resolve the merge conflicts.
Marks issues that should be documented in the release notes of the next release.
label
Do you know if the way to use this is written down somewhere?
Yeah, I ran into a similar problem with path-prefix-remapping a while back. There seems to be no canonical place for documenting niche-features like these. Should I create a page on https://forge.rust-lang.org/? @rust-lang/docs might have a better suggestion.
The reference looks like it's more about the language, not about the compiler.
It looks a bit unmaintained though...
I knocked out the basics, and we'd like to continue to expand it, but just need people to do the work, as always :)
It was created to exactly be the place for this kind of information.
OK, I'll just add a subsection for the feature. It's complicated enough to warrant its own page, I think.
@michaelwoerister Is there any mechanism to ensure matching LLVM IR versions are used?
I feel like this option should be -C lto, but that's already used for other things. It also seems to me that this doesn't have to be implemented by a plugin, so that probably shouldn't be a part of the option, unless you're actually passing a plugin path for the linker to use.
🔔 This is now entering its final comment period, as per the review above. 🔔
Is there any mechanism to ensure matching LLVM IR versions are used?
What do you have in mind? Only the linker sees both versions of the LLVM IR, so don't think that rustc can do a check unfortunately.
It also seems to me that this doesn't have to be implemented by a plugin, so that probably shouldn't be a part of the option, unless you're actually passing a plugin path for the linker to use.
I'm open for suggestions regarding a different name for the command line option. I don't like -C linker-lto because that sounds weird if you know what "LTO" stands for. The name xLTO has been used for the feature by some folks but I think it's suitable for a commandline option. -C defer-lto would be kind of OK, but -C linker-plugin-lto has more useful information in it. As even LLD uses -plugin for its CLI here, I think it's fine.
Are there any plans to integrate this as a cargo flag? Or is this intended to stay as a RUSTFLAG?
This would be great to have on in most cases for embedded, especially if this would inline external assembly files as well.
Edit: specifically as a profile flag that could be set in a Cargo.toml, not a cargo CLI flag
It also works for building big projects like Firefox.
Is Firefox using it to compile its jemalloc-sys fork somewhere ?
Are there any plans to integrate this as a cargo flag?
Not that I'm aware of. @rust-lang/cargo would have to decide about this.
Is Firefox using it to compile its jemalloc-sys fork somewhere ?
Mabye Ted knows something about this? cc @luser
@jamesmunns FWIW this unfortunately wouldn't work for external assembly files, linker-based LTO only works for code compiled by the same compiler to the same IR, such as C and Rust compiled both to LLVM IR. External assembly files are compiled directly to object files and skip the LLVM IR step, so they can't be optimized/inlined together.
I think we'll probably want to stabilize this first and then see how it plays out to figure out how to best add it to Cargo
I think we'll probably want to stabilize this first and then see how it plays out to figure out how to best add it to Cargo
It might be best to add it to the cc crate first. A lot of crates use cc in their build.rs to either compile C/C++ directly, or to obtain "Makefile" options that are then passed to autoconf/automake, make, cmake, and friends.
That is, we probably want cc to detect whether the C compiler that will / should be used is the clang binary shipped with Rust, and if that's the case, then detect whether the linker is the appropriate one. If all stars align, then the cc crate should automatically use this. We probably want to be able to enforce it through some configuration option, which could then be enabled also externally e.g. via an environment variable that users and cargo could set.
📌 Commit 3a9d171 has been approved by alexcrichton
bors added S-waiting-on-bors
Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
and removed S-waiting-on-review
Status: Awaiting review from the assignee but also interested parties.
labels
First of all, this is awesome, and I'd love to see it happen.
Second, while I wouldn't want to block this on it, we need to take this change into account in any future designs for better ways of handling builds of hybrid C/Rust projects.
Centril added a commit to Centril/rust that referenced this pull request
Centril added a commit to Centril/rust that referenced this pull request
bors added a commit that referenced this pull request
Rollup of 13 pull requests
Successful merges:
- #57693 (Doc rewording)
- #57815 (Speed up the fast path for assert_eq! and assert_ne!)
- #58034 (Stabilize the time_checked_add feature)
- #58057 (Stabilize linker-plugin based LTO (aka cross-language LTO))
- #58137 (Cleanup: rename node_id_to_type(_opt))
- #58166 (allow shorthand syntax for deprecation reason)
- #58196 (Add specific feature gate error for const-unstable features)
- #58200 (fix str mutating through a ptr derived from &self)
- #58273 (Rename rustc_errors dependency in rust 2018 crates)
- #58289 (impl iter() for dyn Error)
- #58387 (Disallow
autotrait alias syntax) - #58404 (use Ubuntu keyserver for CloudABI ports)
- #58405 (Remove some dead code from libcore)
Failed merges:
r? @ghost
Centril added a commit to Centril/rust that referenced this pull request
bors added a commit that referenced this pull request
Rollup of 12 pull requests
Successful merges:
- #57693 (Doc rewording)
- #57815 (Speed up the fast path for assert_eq! and assert_ne!)
- #58034 (Stabilize the time_checked_add feature)
- #58057 (Stabilize linker-plugin based LTO (aka cross-language LTO))
- #58137 (Cleanup: rename node_id_to_type(_opt))
- #58166 (allow shorthand syntax for deprecation reason)
- #58200 (fix str mutating through a ptr derived from &self)
- #58273 (Rename rustc_errors dependency in rust 2018 crates)
- #58289 (impl iter() for dyn Error)
- #58387 (Disallow
autotrait alias syntax) - #58404 (use Ubuntu keyserver for CloudABI ports)
- #58405 (Remove some dead code from libcore)
Failed merges:
r? @ghost
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request
Pkgsrc changes:
- Bump required rust version to build to 1.33.0.
- Adapt patches to changed file locations.
- (I worry about 32-bit ports, now that Atomic64 apparently is First-Class; this has been built on NetBSD/amd64 so far)
Upstream changes:
Version 1.34.0 (2019-04-11)
Language
- You can now use
#[deprecated = "reason"]as a shorthand for#[deprecated(note = "reason")]. This was previously allowed by mistake but had no effect. - You can now accept token streams in
#[attr()],#[attr[]], and#[attr{}]procedural macros. - You can now write
extern crate self as foo;to import your crate's root into the extern prelude.
Compiler
- You can now target
riscv64imac-unknown-none-elfandriscv64gc-unknown-none-elf. - You can now enable linker plugin LTO optimisations with
-C linker-plugin-lto. This allows rustc to compile your Rust code into LLVM bitcode allowing LLVM to perform LTO optimisations across C/C++ FFI boundaries. - You can now target
powerpc64-unknown-freebsd.
Libraries
- The trait bounds have been removed on some of
HashMap<K, V, S>'s andHashSet<T, S>'s basic methods. Most notably you no longer require theHashtrait to create an iterator. - The
Ordtrait bounds have been removed on some ofBinaryHeap<T>'s basic methods. Most notably you no longer require theOrdtrait to create an iterator. - The methods
overflowing_negandwrapping_negare nowconstfunctions for all numeric types. - Indexing a
stris now generic over all types that implementSliceIndex<str>. str::trim,str::trim_matches,str::trim_{start, end}, andstr::trim_{start, end}_matchesare now#[must_use]and will produce a warning if their returning type is unused.- The methods
checked_pow,saturating_pow,wrapping_pow, andoverflowing_poware now available for all numeric types. These are equivalvent to methods such aswrapping_addfor thepowoperation.
Stabilized APIs
std & core
Any::type_idError::type_idatomic::AtomicI16atomic::AtomicI32atomic::AtomicI64atomic::AtomicI8atomic::AtomicU16atomic::AtomicU32atomic::AtomicU64atomic::AtomicU8convert::Infallibleconvert::TryFromconvert::TryIntoiter::from_fniter::successorsnum::NonZeroI128num::NonZeroI16num::NonZeroI32num::NonZeroI64num::NonZeroI8num::NonZeroIsizeslice::sort_by_cached_keystr::escape_debugstr::escape_defaultstr::escape_unicodestr::split_ascii_whitespace
std
Cargo
Misc
Compatibility Notes
Command::before_execis now deprecated in favor of the unsafe methodCommand::pre_exec.- Use of
ATOMIC_{BOOL, ISIZE, USIZE}_INITis now deprecated. As you can now useconstfunctions instaticvariables.
pquux mentioned this pull request
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request
Pkgsrc changes:
- Bump required rust version to build to 1.33.0.
- Adapt patches to changed file locations.
- (I worry about 32-bit ports, now that Atomic64 apparently is First-Class; this has been built on NetBSD/amd64 so far)
Upstream changes:
Version 1.34.0 (2019-04-11)
Language
- You can now use
#[deprecated = "reason"]as a shorthand for#[deprecated(note = "reason")]. This was previously allowed by mistake but had no effect. - You can now accept token streams in
#[attr()],#[attr[]], and#[attr{}]procedural macros. - You can now write
extern crate self as foo;to import your crate's root into the extern prelude.
Compiler
- You can now target
riscv64imac-unknown-none-elfandriscv64gc-unknown-none-elf. - You can now enable linker plugin LTO optimisations with
-C linker-plugin-lto. This allows rustc to compile your Rust code into LLVM bitcode allowing LLVM to perform LTO optimisations across C/C++ FFI boundaries. - You can now target
powerpc64-unknown-freebsd.
Libraries
- The trait bounds have been removed on some of
HashMap<K, V, S>'s andHashSet<T, S>'s basic methods. Most notably you no longer require theHashtrait to create an iterator. - The
Ordtrait bounds have been removed on some ofBinaryHeap<T>'s basic methods. Most notably you no longer require theOrdtrait to create an iterator. - The methods
overflowing_negandwrapping_negare nowconstfunctions for all numeric types. - Indexing a
stris now generic over all types that implementSliceIndex<str>. str::trim,str::trim_matches,str::trim_{start, end}, andstr::trim_{start, end}_matchesare now#[must_use]and will produce a warning if their returning type is unused.- The methods
checked_pow,saturating_pow,wrapping_pow, andoverflowing_poware now available for all numeric types. These are equivalvent to methods such aswrapping_addfor thepowoperation.
Stabilized APIs
std & core
Any::type_idError::type_idatomic::AtomicI16atomic::AtomicI32atomic::AtomicI64atomic::AtomicI8atomic::AtomicU16atomic::AtomicU32atomic::AtomicU64atomic::AtomicU8convert::Infallibleconvert::TryFromconvert::TryIntoiter::from_fniter::successorsnum::NonZeroI128num::NonZeroI16num::NonZeroI32num::NonZeroI64num::NonZeroI8num::NonZeroIsizeslice::sort_by_cached_keystr::escape_debugstr::escape_defaultstr::escape_unicodestr::split_ascii_whitespace
std
Cargo
Misc
Compatibility Notes
Command::before_execis now deprecated in favor of the unsafe methodCommand::pre_exec.- Use of
ATOMIC_{BOOL, ISIZE, USIZE}_INITis now deprecated. As you can now useconstfunctions instaticvariables.
I would like to bring to your attention that stabilizing this requires the LLVM version used in Rust to be a non-arbitrary version of LLVM trunk. It does happen that LLVM IR breaks compatibility on trunk (and it ends up being fixed at some point), and IIRC, it has happened that Rust picked a version of LLVM that happened to be broken.
I would like to bring to your attention that stabilizing this requires the LLVM version used in Rust to be a non-arbitrary version of LLVM trunk.
Why? This option is disabled by default. If a user enables it, and the LLVM used is incompatible, we could:
- error
- emit a warning and disable the option (compilation proceeds)
- disable the option without error or warning (compilation proceeds silently).
AFAICT, this option enables and optimization and that can be "best-effort". We should document the behavior, and that's it.
If the current behavior is neither of these (e.g. unsound, an LLVM error is emitted, etc.), you can open an issue about this. That's bad and should be fixed.
@glandium While this is true in general, rust is currently using (and has been since its release) the LLVM 8 branch, not trunk. If you're seeing issues, they cannot be related to trunk changes.
@nikic users might be using the system LLVM (up to 6.0), depending on their platform (linux distribution, etc.). This case needs to be handled somehow.
@gnzlbg That's true. Using the system rust (which will likely be linked against system LLVM) should work though. We also have #56371 which suggests to ship a compatible clang in rustup.
ghost mentioned this pull request
Labels
This issue / PR is in PFCP or FCP with a disposition to merge it.
The final comment period is finished for this PR / Issue.
Marks issues that should be documented in the release notes of the next release.
Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Relevant to the compiler team, which will review and decide on the PR/issue.